aor-simple-graphql-client
A simple GraphQL client for admin-on-rest
built with Apollo
About GraphQL and Apollo
This library is meant to be used with Apollo on the client side but
you're free to use any graphql server.
Note that this client is not compatible with graphcool. However, another client exists for graphcool: aor-graph-cool-client.
Installation
Install with:
npm install --save aor-simple-graphql-client
or
yarn add aor-simple-graphql-client
Usage
Let's create a file for our admin page admin.js
:
import React, { Component } from 'react';
import { buildApolloClient } from 'aor-simple-graphql-client';
import { Admin, Resource } from 'admin-on-rest';
import { Delete } from 'admin-on-rest/lib/mui';
import { PostCreate, PostEdit, PostList } from '../components/admin/posts';
const client = new ApolloClient();
class AdminPage extends Component {
constructor() {
super();
this.state = { restClient: null };
}
componentDidMount() {
buildApolloClient()
.then(restClient => this.setState({ restClient }));
}
render() {
const { restClient } = this.state;
if (!restClient) {
return <div>Loading</div>;
}
return (
<Admin restClient={restClient}>
<Resource name="Post" list={PostList} edit={PostEdit} create={PostCreate} remove={Delete} />
</Admin>
);
}
}
export default AdminPage;
And that's it, buildApolloClient
will create a default ApolloClient for you and
run an introspection query on your graphql endpoint.
By default, it expect the following queries and mutations for each resource:
### List resources with pagination
Example with resource Post
:
getPageOfPosts(page: Int, perPage: Int, sortField: String, sortOrder: String, filter: String) {
items: [Post]
totalCount: Int
}
Note that the function should be named with the plural version of Post
.
We use pluralize to generate it.
filter
may contain a serialized JSON object, for example:
'{ "authorId": "4e80878c-6baa-4506-a93c-ef99b74e73e0" }'
Get a resource
Example with resource Post
:
getPost(id: ID!) Post
Create a new resource
Example with resource Post
:
createPost(data: String) Post
data
is a serialized JSON object, for example:
'{ "title": "My first post", "authorId": "4e80878c-6baa-4506-a93c-ef99b74e73e0", "body": "..." }'
Update a resource
Example with resource Post
:
updatePost(data: String) Post
data
is a serialized JSON object, for example:
'{ "id": "c02e92e8-2a21-4ae7-9197-cb9601861a44", "title": "My first post", "authorId": "4e80878c-6baa-4506-a93c-ef99b74e73e0", "body": "..." }'
Remove a resource
Example with resource Post
:
removePost(id: ID!) Boolean
Options
Customize the Apollo client
You can either supply the client options by calling buildApolloClient
like this:
buildApolloClient({ clientOptions: { uri: 'http://localhost:3000', ...otherApolloOptions } });
Or supply your client directly with:
buildApolloClient({ client: myClient });
Customize the introspection
These are the default options for introspection:
const introspectionOptions = {
includeTypes: null,
excludeTypes: null,
includeQueries: null,
excludeQueries: null,
includeMutations: null,
excludeMutations: null,
excludeFields: null,
templates: {
GET_LIST: resourceType => `getPageOf${pluralize(resourceType.name)}`,
GET_ONE: resourceType => `get${resourceType.name}`,
CREATE: resourceType => `create${resourceType.name}`,
UPDATE: resourceType => `update${resourceType.name}`,
DELETE: resourceType => `remove${resourceType.name}`,
},
}
And how you pass them to the buildApolloClient
function:
buildApolloClient({ introspection: introspectionOptions });
Note: excludeXXX
and includeXXX
are mutualy exclusives and
that includeXXX
will always take precendance.
excludeFields
deserves more details. If supplying a function, it
will receive the following parameters:
field
: the field definition (see the documentation on
introspection for more details)resource
: the resource type (for example: Post
)type
: the operation type (matching those of admin-on-rest, for example: GET_LIST
)
Supply your own queries and mutations
You need more control? Then provide your queries with the following format:
const queries = {
Post: {
GET_LIST: () => gql`your query`,
GET_MANY: () => gql`your query`,
GET_MANY_REFERENCE: () => gql`your query`,
GET_ONE: () => gql`your query`,
CREATE: () => gql`your query`,
UPDATE: () => gql`your query`,
DELETE: () => gql`your query`,
}
}
buildApolloClient({ queries });
Note: GET_MANY
and GET_MANY_REFERENCE
are optional.
If not specified, GET_LIST
will be called with the filter
and perPage
set to 1000
.
Note: You can mix introspection and custom queries by just supplying your custom queries.
If you want to disable introspection, set the introspection
option to false
.
buildApolloClient({ queries, introspection: false });
Realtime updates
Using ApolloClient, one is able to get real time updates when data changes:
see their documentation for details.
With aor-simple-graphql-client
, you can enable real time updates like this:
import React, { Component } from 'react';
import { buildApolloClient } from 'aor-simple-graphql-client';
import { Admin, Resource } from 'admin-on-rest';
import { Delete } from 'admin-on-rest/lib/mui';
import { PostCreate, PostEdit, PostList } from '../components/admin/posts';
const client = new ApolloClient();
class AdminPage extends Component {
constructor() {
super();
this.state = { restClient: null };
}
componentDidMount() {
buildApolloClient()
.then(restClient => this.setState({ restClient }));
}
render() {
const { restClient } = this.state;
if (!restClient) {
return <div>Loading</div>;
}
return (
<Admin restClient={restClient} customSagas={[restClient.saga()]}>
<Resource name="Post" list={PostList} edit={PostEdit} create={PostCreate} remove={Delete} />
</Admin>
);
}
}
export default AdminPage;
We simply pass a custom saga
to the Admin
component.
By default, it will use the Apollo polling mechanism with a pollInterval
of 2 seconds for all
GET_LIST
and GET_ONE
requests.
Customization
You can specify the options to pass to the watchQuery
function.
For all resources and request types
const apolloWatchOptions = {
pollInterval: 10000,
};
const apolloSaga = restClient.saga(apolloWatchOptions);
Or, you can supply a function instead of an object and it will be call
with resource
and requestType
parameters:
const apolloWatchOptions = (resource, requestType) => ({
pollInterval: 10000,
});
const apolloSaga = restClient.saga(apolloWatchOptions);
For a specific resources and all request types
const apolloWatchOptions = {
Post: {
pollInterval: 10000,
},
Comment: {
pollInterval: 5000,
},
};
const apolloSaga = restClient.saga(apolloWatchOptions);
Or, you can supply a function instead of an object and it will be call
with a requestType
parameter:
const apolloWatchOptions = (resource, requestType) => {
Post: {
pollInterval: 10000,
},
Comment: {
(requestType): => ({
pollInterval: 5000,
}),
},
};
const apolloSaga = restClient.saga(apolloWatchOptions);
For a specific resources and request type
const apolloWatchOptions = {
Post: {
pollInterval: 2000,
},
Comment: {
GET_ONE: {
pollInterval: 10000,
},
GET_LIST: {
pollInterval: 5000,
},
},
};
const apolloSaga = restClient.saga(apolloWatchOptions);
Or, you can supply a function instead of an object:
const apolloWatchOptions = (resource, requestType) => {
Post: {
pollInterval: 10000,
},
Comment: {
GET_ONE: {
pollInterval: 10000,
},
GET_LIST: {
(): => ({
pollInterval: 5000,
}),
},
},
};
const apolloSaga = restClient.saga(apolloWatchOptions);
TODO
Contributing
Coverage data is available in ./coverage
after executing make test
.
An HTML report is generated in ./coverage/lcov-report/index.html
.