graphql-compose-json
This is a plugin for graphql-compose, which generates GraphQLTypes from REST response or any JSON. It takes fields from object, determines their types and construct GraphQLObjectType with same shape.
Demo
We have a Live demo (source code repo) which shows how to build an API upon SWAPI using graphql-compose-json.
Installation
npm install graphql graphql-compose graphql-compose-json --save Modules graphql, graphql-compose, are located in peerDependencies, so they should be installed explicitly in your app. They have global objects and should not have ability to be installed as submodule.
Example
You have a sample response object restApiResponse which you can pass to graphql-compose-json along with desired type name as your first argument and it will automatically generate a composed GraphQL type PersonTC.
// person.js import composeWithJson from 'graphql-compose-json'; const restApiResponse = { name: 'Anakin Skywalker', birth_year: '41.9BBY', gender: 'male', mass: 77, homeworld: 'https://swapi.co/api/planets/1/', films: [ 'https://swapi.co/api/films/5/', 'https://swapi.co/api/films/4/', 'https://swapi.co/api/films/6/', ], species: ['https://swapi.co/api/species/1/'], starships: [ 'https://swapi.co/api/starships/59/', 'https://swapi.co/api/starships/65/', 'https://swapi.co/api/starships/39/', ], }; export const PersonTC = composeWithJson('Person', restApiResponse); export const PersonGraphQLType = PersonTC.getType(); Customization
You can write custom field configs directly to a field of your API response object via function (see mass and starships_count field):
import composeWithJson from 'graphql-compose-json'; const restApiResponse = { name: 'Anakin Skywalker', birth_year: '41.9BBY', starships: [ 'https://swapi.co/api/starships/59/', 'https://swapi.co/api/starships/65/', 'https://swapi.co/api/starships/39/', ], mass: () => 'Int!', // by default JSON numbers coerced to Float, here we set up Int starships_count: () => ({ // more granular field config with resolve function type: 'Int', resolve: source => source.starships.length, }), }; export const CustomPersonTC = composeWithJson('CustomPerson', restApiResponse); export const CustomPersonGraphQLType = CustomPersonTC.getType(); Will be produced following GraphQL Type from upper shape:
const CustomPersonGraphQLType = new GraphQLObjectType({ name: 'CustomPerson', fields: () => { name: { type: GraphQLString, }, birth_year: { type: GraphQLString, }, starships: { type: new GraphQLList(GraphQLString), }, mass: { type: GraphQLInt, }, starships_count: { type: GraphQLInt, resolve: source => source.starships.length, }, }, }); Schema building
Now when you have your type built, you may specify the schema and data fetching method:
// schema.js import { GraphQLSchema, GraphQLObjectType, GraphQLNonNull, GraphQLInt } from 'graphql'; import fetch from 'node-fetch'; import { PersonTC } from './person'; const schema = new GraphQLSchema({ query: new GraphQLObjectType({ name: 'Query', fields: { person: { type: PersonTC.getType(), // get GraphQL type from PersonTC args: { id: { type: new GraphQLNonNull(GraphQLInt), } }, resolve: (_, args) => fetch(`https://swapi.co/api/people/${args.id}/`).then(r => r.json()), }, }, }), }); Or do the same via graphql-compose:
import { schemaComposer } from 'graphql-compose'; schemaComposer.Query.addFields({ person: { type: PersonTC, args: { id: `Int!`, // equals to `new GraphQLNonNull(GraphQLInt)` }, resolve: (_, args) => fetch(`https://swapi.co/api/people/${args.id}/`).then(r => r.json()), }, } const schema = schemaComposer.buildSchema(); // returns GraphQLSchema Building schema asynchronously
To build the schema at the runtime, you should rewrite the Schema.js and insert there an async function which will return a promise:
export const buildAsyncSchema = async (): Promise<GraphQLSchema> => { const url = `https://swapi.co/api/people/1`; const data = await fetch(url); const jsonData = await data.json(); const PeopleTC = composeWithJson('People', jsonData); PeopleTC.addResolver({ name: 'findById', type: PeopleTC, args: { id: 'Int!', }, resolve: rp => { return fetch(`https://swapi.co/api/people/${rp.args.id}/`).then(r => r.json()); }, }); schemaComposer.Query.addFields({ person: PeopleTC.getResolver('findById'), }); const schema = schemaComposer.buildSchema(); return schema; }; So, you can just import this function and tell to the express-graphql that we are passing a promise:
import express from 'express'; import graphqlHTTP from 'express-graphql'; import { buildAsyncSchema } from './Schema'; const PORT = 4000; const app = express(); const promiseSchema = buildAsyncSchema(); app.use( '/graphql', graphqlHTTP(async req => ({ schema: await promiseSchema, graphiql: true, context: req, })) ); Further customization with graphql-compose
Moreover, graphql-compose allows you to pass pre-defined resolvers of other types to the response object and customize them:
const restApiResponse = { name: 'Anakin Skywalker', starships: () => StarshipTC.getResolver('findByUrlList') // get some standard resolver .wrapResolve(next => rp => { // wrap with additional logic const starshipsUrls = rp.source.starships; rp.args.urls = starshipsUrls; // populate `urls` arg from source return next(rp); // call standard resolver }) .removeArg('urls'), // remove `urls` args from resolver and schema }; } const PersonTC = composeWithJson('Person', restApiResponse); In case you need to separate custom field definition from your response object there are graphql-compose methods made for this purpose.
If you want to specify new fields of your type, simply use the addFields method of graphql-compose:
PersonTC.addFields({ vehicles_count: { type: 'Int!', // equals to `new GraphQLNonNull(GraphQLInt)` resolve: (source) => source.vehicles.length, }, }); When you want to create a relation with another type simply use addRelation method of graphql-compose:
PersonTC.addRelation('filmObjects', { resolver: () => FilmTC.getResolver('findByUrlList'), prepareArgs: { urls: source => source.films, }, }); graphql-compose provides a vast variety of methods for fields and resolvers (aka field configs in vanilla GraphQL) management of GraphQL types. To learn more visit graphql-compose repo.
