An Apollo Link to easily use GraphQL with a JSON API compliant server.
Built on top of
apollo-link-rest
. If you
have a non-JSON API REST service, check that out as an alternative.
npm install apollo-link-json-api apollo-link graphql graphql-anywhere qs humps --save
# or
yarn add apollo-link-json-api apollo-link graphql graphql-anywhere qs humps
apollo-link
, graphql
, qs
, humps
, and graphql-anywhere
are peer dependencies needed by apollo-link-json-api
.
import { JsonApiLink } from "apollo-link-json-api";
// Other necessary imports...
// Create a JsonApiLink for the JSON API
// If you are using multiple link types, jsonApiLink should go before httpLink,
// as httpLink will swallow any calls that should be routed through jsonApi!
const jsonApiLink = new JsonApiLink({
uri: 'http://jsonapiplayground.reyesoft.com/v2/',
});
// Configure the ApolloClient with the default cache and JsonApiLink
const client = new ApolloClient({
link: jsonApiLink,
cache: new InMemoryCache(),
});
// A simple query to retrieve data about the first author
const query = gql`
query firstAuthor {
author @jsonapi(path: "authors/1") {
name
}
}
`;
// Invoke the query and log the person's name
client.query({ query }).then(response => {
console.log(response.data.name);
});
JSON API Link supports unpacking related resources into a friendlier GraphQL query structure.
const query = gql`
query firstAuthor {
author @jsonapi(path: "authors/1?include=series,series.books") {
name
series {
title
books {
title
}
}
}
}
`;
While JSON API Link does support running multiple nested queries, prefer
sideloading resources in a single request by using the ?include
parameter if
your JSON API server supports it.
// Avoid this
const badQuery = gql`
query firstAuthor {
author @jsonapi(path: "authors/1") {
name
series @jsonapi(path: "authors/1/series") {
title
}
}
}
`;
// Prefer this
const query = gql`
query firstAuthor {
author @jsonapi(path: "authors/1?include=series") {
name
series {
title
}
}
}
`;
import React from 'react'
import gql from 'graphql-tag'
import { Mutation } from 'react-apollo'
export const UPDATE_BOOK_TITLE = gql`
mutation UpdateBookTitle($input: UpdateBookTitleInput!) {
book(input: $input) @jsonapi(path: "/books/{args.input.data.id}", method: "PATCH") {
title
}
}
`
const UpdateBookTitleButton = ({ bookId }) => (
<Mutation
mutation={UPDATE_BOOK_TITLE}
update={(store, { data: { book } }) => {
// Update your Apollo cache with result
console.log(book.title)
}}
>
{mutate => (
<button onClick={() =>
mutate({
variables: {
input: {
data: {
id: bookId,
type: 'books',
attributes: { title: 'Changed title!' }
}
}
},
optimisticResponse: {
book: {
__typename: 'books',
title: 'Changed title!'
}
}
})
}>
Update your book title!
</button>
)}
</Mutation>
)
JSON API Link takes an object with some options on it to customize the behavior of the link. The options you can pass are outlined below:
uri
: the URI key is a string endpoint (optional whenendpoints
provides a default)endpoints
: root endpoint (uri) to apply paths to or a map of endpointscustomFetch
: a customfetch
to handle API callsheaders
: an object representing values to be sent as headers on the requestcredentials
: a string representing the credentials policy you want for the fetch callfieldNameNormalizer
: function that takes the response field name and converts it into a GraphQL compliant namefieldNameDenormalizer
: function that takes the JavaScript object key name and converts it into a JSON API compliant nametypeNameNormalizer
: function that takes the JSON API resource type and converts it to a GraphQL__typename
.
JSON API Link uses the headers
field on the context to allow passing headers to the HTTP request. It also supports the credentials
field for defining credentials policy.
headers
: an object representing values to be sent as headers on the requestcredentials
: a string representing the credentials policy you want for the fetch call
By default, this library flattens your server response. If you need to access
values that are unavailable by this simple querying method, you can add
includeJsonapi: true
to your @jsonapi
directive, which will instead return
the flattened "GraphQL-like" structure under a graphql
key, and the original
response structure under a jsonapi
key. Resources are still nested in a tree
structure under the jsonapi
key, but data
/attribute
/relatioship
keys are
not flattened out.
query authorsWithMeta {
authors @jsonapi(path: "authors?include=series", includeJsonapi: true) {
graphql {
name
series {
title
}
}
jsonapi {
meta {
pageCount
}
links {
first
last
current
}
// The resource data is available here, though it's probably easier to
// grab from the `graphql` structure
data {
attributes {
name
}
relationships {
series {
data {
attributes {
title
}
}
links {
related
}
}
}
}
}
}
}
This project uses TypeScript to bring static types to JavaScript and uses Jest for testing. To get started, clone the repo and run the following commands:
npm install # or `yarn`
npm test # or `yarn test` to run tests
npm test -- --watch # run tests in watch mode
npm run check-types # or `yarn check-types` to check TypeScript types
To run the library locally in another project, you can do the following:
npm link
# in the project you want to run this in
npm link apollo-link-json-api