在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):hasura/ra-data-hasura开源软件地址(OpenSource Url):https://github.com/hasura/ra-data-hasura开源编程语言(OpenSource Language):TypeScript 75.3%开源软件介绍(OpenSource Introduction):ra-data-hasuraA GraphQL data provider for react-admin v3 tailored to target Hasura GraphQL endpoints.
Example applications demonstrating usage:
Benefits and MotivationThis utility is built on top of ra-data-graphql and is a custom data provider for the current Hasura GraphQL API format. The existing ra-data-graphql-simple provider, requires that your GraphQL endpoint implement a specific grammar for the objects and methods exposed, which is different with Hasura because the exposed objects and methods are generated differently. This utility auto generates valid GraphQL queries based on the properties exposed by the Hasura API such as InstallationInstall with: npm install --save graphql ra-data-hasura UsageThe buildHasuraProvider(
options?: Object,
buildGqlQueryOverrides?: Object,
customBuildVariables?: Function,
customGetResponseParser?: Function,
) => Function See the Options and Customizing queries sections below for more details on these arguments. This function acts as a constructor for a // Initialize the dataProvider before rendering react-admin resources.
import React, { useState, useEffect } from 'react';
import buildHasuraProvider from 'ra-data-hasura';
import { Admin, Resource } from 'react-admin';
import { PostCreate, PostEdit, PostList } from './posts';
const App = () => {
const [dataProvider, setDataProvider] = useState(null);
useEffect(() => {
const buildDataProvider = async () => {
const dataProvider = await buildHasuraProvider({
clientOptions: { uri: 'http://localhost:8080/v1/graphql' },
});
setDataProvider(() => dataProvider);
};
buildDataProvider();
}, []);
if (!dataProvider) return <p>Loading...</p>;
return (
<Admin dataProvider={dataProvider}>
<Resource
name="Post"
list={PostList}
edit={PostEdit}
create={PostCreate}
/>
</Admin>
);
};
export default App; How It WorksThe data provider converts React Admin queries into the form expected by Hasura's GraphQL API. For example, a React Admin {
"pagination": { "page": 1, "perPage": 5 },
"sort": { "field": "name", "order": "DESC" },
"filter": {
"ids": [101, 102]
}
} will generate the following GraphQL request for Hasura :
With the following variables to be passed alongside the query:
React Admin sort and filter objects will be converted appropriately, for example sorting with dot notation: export const PostList = (props) => (
<List {...props} sort={{ field: 'user.email', order: 'DESC' }}>
...
</List>
); will generate the following GraphQL query variables: {
limit: 25,
offset: 0,
order_by: { user: { email: 'desc' } }
} OptionsCustomize the Apollo clientYou can either supply just the client options: buildGraphQLProvider({
clientOptions: {
uri: 'http://localhost:8080/v1/graphql',
...otherApolloOptions,
},
}); or supply the client instance directly: buildGraphQLProvider({ client: myClient }); Adding Authentication HeadersTo send authentication headers, you'll need to supply the client instance directly with headers defined: import { ApolloClient, InMemoryCache } from '@apollo/client';
const myClientWithAuth = new ApolloClient({
uri: 'http://localhost:8080/v1/graphql',
cache: new InMemoryCache(),
headers: {
'x-hasura-admin-secret': 'hasuraAdminSecret',
// 'Authorization': `Bearer xxxx`,
},
});
buildHasuraProvider({ client: myClientWithAuth }); Adding headers using just client optionsYou can also add headers using only client options rather than the client itself: import { createHttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
'x-hasura-admin-secret': 'hasuraAdminSecret',
// 'Authorization': `Bearer xxxx`,
},
}));
const httpLink = createHttpLink({
uri: 'http://localhost:8080/v1/graphql',
});
const clientOptionsWithAuth = {
link: authLink.concat(httpLink),
};
buildHasuraProvider({ client: clientOptionsWithAuth }); Customize the introspectionThese are the default options for introspection: const introspectionOptions = {
include: [], // Either an array of types to include or a function which will be called for every type discovered through introspection
exclude: [], // Either an array of types to exclude or a function which will be called for every type discovered through introspection
};
// Including types
const introspectionOptions = {
include: ['Post', 'Comment'],
};
// Excluding types
const introspectionOptions = {
exclude: ['CommandItem'],
};
// Including types with a function
const introspectionOptions = {
include: (type) => ['Post', 'Comment'].includes(type.name),
};
// Including types with a function
const introspectionOptions = {
exclude: (type) => !['Post', 'Comment'].includes(type.name),
}; Note: Note: When using functions, the Pass the introspection options to the buildApolloProvider({ introspection: introspectionOptions }); Customize the Data ReturnOnce the data is returned back from the provider, you can customize it by implementing the const [dataProvider, setDataProvider] = React.useState<DataProvider | null>(
null
);
React.useEffect(() => {
const buildDataProvider = async () => {
const dataProviderHasura = await buildHasuraProvider({
clientOptions: {
uri: 'http://localhost:8080/v1/graphql',
},
});
const modifiedProvider: DataProvider = {
getList: async (resource, params) => {
let { data, ...metadata } = await dataProviderHasura.getList(
resource,
params
);
if (resource === 'example_resource_name') {
data = data.map(
(val): Record => ({
...val,
id: val.region_id,
})
);
}
return {
data: data as any[],
...metadata,
};
},
getOne: (resource, params) => dataProviderHasura.getOne(resource, params),
getMany: (resource, params) =>
dataProviderHasura.getMany(resource, params),
getManyReference: (resource, params) =>
dataProviderHasura.getManyReference(resource, params),
update: (resource, params) => dataProviderHasura.update(resource, params),
updateMany: (resource, params) =>
dataProviderHasura.updateMany(resource, params),
create: (resource, params) => dataProviderHasura.create(resource, params),
delete: (resource, params) => dataProviderHasura.delete(resource, params),
deleteMany: (resource, params) =>
dataProviderHasura.deleteMany(resource, params),
};
setDataProvider(() => modifiedProvider);
};
buildDataProvider();
}, []); Customizing queriesQueries built by this data provider are made up of 3 parts:
Each of these can be customized - functions overriding numbers 2 and 3 can be passed to directly to {
buildFields?: Function,
buildMetaArgs?: Function,
buildArgs?: Function,
buildApolloArgs?: Function,
} A likely scenario is that you want to override only the This can be easily done, and importantly can be done using Example: extending a query to include related entitiesBy default, the data provider will generate queries that include all fields on a resource, but without any relationships to nested entities. If you would like to keep these base fields but extend the query to also include related entities, then you can write a custom import buildDataProvider, { buildFields } from 'ra-data-hasura';
import type { BuildFields } from 'ra-data-hasura';
import gql from 'graphql-tag';
/**
* Extracts just the fields from a GraphQL AST.
* @param {GraphQL AST} queryAst
*/
const extractFieldsFromQuery = (queryAst) => {
return queryAst.definitions[0].selectionSet.selections;
};
// Define the additional fields that we want.
const EXTENDED_GET_ONE_USER = gql`
{
todos_aggregate {
aggregate {
count
}
}
}
`;
const customBuildFields: BuildFields = (type, fetchType) => {
const resourceName = type.name;
// First take the default fields (all, but no related or nested).
const defaultFields = buildFields(type, fetchType);
if (resourceName === 'users' && fetchType === 'GET_ONE') {
const relatedEntities = extractFieldsFromQuery(EXTENDED_GET_ONE_USER);
defaultFields.push(...relatedEntities);
}
// Extend other queries for other resources/fetchTypes here...
return defaultFields;
};
buildDataProvider(options, { buildFields: customBuildFields }); Example: write a completely custom queryIf you want full control over the GraphQL query, then you can define the entire set of fields like this: import gql from 'graphql-tag';
import buildDataProvider, { buildFields } from 'ra-data-hasura';
import type { BuildFields } from 'ra-data-hasura';
/**
* Extracts just the fields from a GraphQL AST.
* @param {GraphQL AST} queryAst
*/
const extractFieldsFromQuery = (queryAst) => {
return queryAst.definitions[0].selectionSet.selections;
};
const GET_ONE_USER = gql`
{
id
name
todos(
where: { is_completed: { _eq: false } }
order_by: { created_at: asc }
) {
title
}
todos_aggregate {
aggregate {
count
}
}
}
`;
const customBuildFields: BuildFields = (type, fetchType) => {
const resourceName = type.name;
if (resourceName === 'users' && fetchType === 'GET_ONE') {
return extractFieldsFromQuery(GET_ONE_USER);
}
// No custom query defined, so use the default query fields (all, but no related/nested).
return buildFields(type, fetchType);
};
buildDataProvider(options, { buildFields: customBuildFields }); Note that when using this approach in particular, it is possible that you will come across this issue. Special Filter FeatureThis adapter allows filtering several columns at a time with using specific comparators, e.g. <Filter {...props}>
<TextInput
label="Search"
source="email,first_name@_eq,last_name@_like"
alwaysOn
/>
</Filter> It will generate the following filter payload |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论