123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- import {
- GraphQLBoolean,
- GraphQLFloat,
- GraphQLID,
- GraphQLInt,
- GraphQLList,
- GraphQLNonNull,
- GraphQLObjectType,
- GraphQLSchema,
- GraphQLString,
- GraphQLInputObjectType,
- } from 'graphql';
- import {
- mapValues,
- mapKeys,
- } from 'lodash';
- import {
- schema
- } from './swapi.js';
- import rp from 'request-promise';
- import DataLoader from 'dataloader';
- // Generate a GraphQL object type for every entry in the schema
- const graphQLObjectTypes = mapValues(schema, (jsonSchema) => {
- return new GraphQLObjectType({
- name: jsonSchema.title,
- description: jsonSchema.description,
- fields: () => {
- return mapValues(jsonSchema.properties, (propertySchema, propertyName) => {
- const value = {
- description: propertySchema.description,
- type: jsonSchemaTypeToGraphQL(propertySchema.type, propertyName)
- };
- // All of the arrays in the schema happen to be references to other types
- if(propertySchema.type === 'array') {
- value.resolve = (root, args) => {
- const arrayOfUrls = root[propertyName];
- const arrayOfResults = arrayOfUrls.map(restLoader.load.bind(restLoader));
- return arrayOfResults;
- }
- }
- return value;
- });
- }
- })
- });
- // Convert the JSON Schema types to the actual GraphQL types in our schema
- function jsonSchemaTypeToGraphQL(jsonSchemaType, schemaName) {
- if (jsonSchemaType === "array") {
- if (graphQLObjectTypes[schemaName]) {
- return new GraphQLList(graphQLObjectTypes[schemaName]);
- } else {
- const translated = {
- pilots: "people",
- characters: "people",
- residents: "people"
- }[schemaName];
- if (! translated) {
- throw new Error(`no type ${schemaName}`);
- }
- const type = graphQLObjectTypes[translated];
- if (! type) {
- throw new Error(`no GraphQL type ${schemaName}`);
- }
- return new GraphQLList(type);
- }
- }
- return {
- string: GraphQLString,
- date: GraphQLString,
- integer: GraphQLInt,
- }[jsonSchemaType];
- }
- const queryType = new GraphQLObjectType({
- name: 'Query',
- fields: () => {
- // For each type, make a query to get a page of that type
- const plural = mapValues(graphQLObjectTypes, (type, typePluralName) => {
- return {
- type: new GraphQLList(type),
- description: `All ${typePluralName}.`,
- args: {
- page: { type: GraphQLInt }
- },
- resolve: (_, { page }) => {
- // Simple pagination, where you just pass the page number through to the REST API
- return fetchPageOfType(typePluralName, page);
- },
- };
- });
- // For each type, also make a query to get just one object of that type
- const singular = mapValues(mapKeys(graphQLObjectTypes, (value, key) => {
- // Name the query people_one vehicles_one etc. not sure what the standard should be
- // here. We could also adopt the Relay node spec
- return key + "_one";
- }), (type, typeQueryName) => {
- const restName = typeQueryName.split('_')[0];
- return {
- type: type,
- description: `One ${restName}.`,
- args: {
- id: { type: GraphQLString }
- },
- resolve: (_, { id }) => {
- return fetchOne(restName, id);
- },
- }
- });
- return {
- ...plural,
- ...singular
- };
- },
- });
- // A helper to unwrap the paginated object from SWAPI
- function fetchPageOfType(typePluralName, pageNumber) {
- let url = `http://swapi.co/api/${typePluralName}/`;
- if (pageNumber) {
- url += `?page=${pageNumber}`;
- };
- return restLoader.load(url).then((data) => {
- // Paginated results have a different shape
- return data.results;
- });
- }
- // Constructs a URL from the endpoint name and object ID
- function fetchOne(restName, id) {
- return restLoader.load(`http://swapi.co/api/${restName}/${id}`);
- }
- // Use dataloader to batch URL requests for each layer of the query
- const restLoader = new DataLoader((urls) => {
- console.log("Fetching batch:", urls);
- return Promise.all(urls.map((url) => {
- return rp({
- uri: url,
- json: true
- });
- }));
- });
- // This schema has no mutations because you can't really write to the Star Wars API.
- export const Schema = new GraphQLSchema({
- query: queryType,
- });
|