GraphQL Code Generator
Introduce Type Safety for the GraphQL Schema and Documents
Need to install dependencies and dev dependencies.
/DataAccess - (Terminal / Command Propt)
npm i @graphql-tools/graphql-file-loader @graphql-tools/load @graphql-tools/load-files @graphql-tools/schema @graphql-tools/stitch graphql-scalars
npm i -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers
npm i -D @graphql-codegen/introspection @graphql-codegen/typed-document-node @graphql-codegen/typescript-operations
Structure:
#inputs
/data-access
- /codegen.yml #Configuration file for GrapQL Code Generator
- /graphql
- /context.ts #Type definition of the Context
- /resolvers
- index.ts #exports all resolvers to be merged into Schema
- /core/
- /graphql-tools-scalars.ts #Enables Scalars to be used
- /schema.graphql #Baseline schema
- /**/*.reslovers.ts #GraphQL Resolvers
- /**/*.graphql #Definitions of GraphQL Types,Queries,Mutations,Inputs
- /types/*.ts #Definitions of TypeScript Interfaces which Match GraphQL Types
#outputs
/data-access
- /graphql
- /generated.ts #Type file to ensure type safety for Queries/Mutations/Resolvers/Types
- /graphql.schema.json #Introspection file to Expose all GraphQL Schema to Apollo
/ui/src/generated.tsx #Type file to ensure type safety for UI side Queries/Mutations/Resolvers/Types/Fragments
This is very simple context object - more details can be added later.
/data-access/graphql/context.ts
export type Context = {
authToken: string;
}
/data-access/graphql/resolvers/index.ts
import {Resolvers} from '../generated';
import path from 'path';
import { mergeResolvers } from '@graphql-tools/merge';
import { loadFilesSync } from '@graphql-tools/load-files';
const resolversArray = loadFilesSync(path.join(__dirname, "./**/*.resolvers.*"));
export const resolvers: Resolvers = mergeResolvers(resolversArray);
The GraphQL Tools Scalars project exposes a handful of helpful types including DateTime/Email/ObjectID. In order to make these types available we need to re-export them.
/data-access/graphql/resolvers/core/graphql-tools-scalars.ts
const { typeDefs } = require('graphql-scalars');
const { buildSchema } = require('graphql');
const scalars = typeDefs.join('\n')
module.exports = buildSchema(scalars);
/data-access/graphql/resolvers/core/schema.graphql
""" Core schema """
schema {
query: Query
mutation: Mutation
}
""" Base Mutation Type definition - all mutations will be defined in separate files extending this type """
type Mutation {
"""
IGNORE: Dummy field necessary for the Mutation type to be valid
"""
_empty:String
}
""" Base Query Type definition - , all mutations will be defined in separate files extending this type """
type Query {
"""
IGNORE: Dummy field necessary for the Query type to be valid
"""
_empty:String
}
""" Required to enable Apollo Cache Control """
enum CacheControlScope {
PUBLIC
PRIVATE
}
""" Required to enable Apollo Cache Control """
directive @cacheControl(
maxAge: Int
scope: CacheControlScope
inheritMaxAge: Boolean
) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION
/data-access/graphql/init/extensions/schema-builder.ts
import { loadSchemaSync } from '@graphql-tools/load';
import { addResolversToSchema, mergeSchemas } from '@graphql-tools/schema';
import { resolvers } from '../../resolvers';
import { JsonFileLoader } from '@graphql-tools/json-file-loader';
import * as Scalars from 'graphql-scalars';
import { makeExecutableSchema } from '@graphql-tools/schema';
const schema = loadSchemaSync('./graphql.schema.json', {
loaders: [new JsonFileLoader()],
});
const appSchema = addResolversToSchema(schema,resolvers)
const scalarSchema = makeExecutableSchema({
typeDefs:[
...Scalars.typeDefs,
],
resolvers:{
...Scalars.resolvers,
}
});
export const combinedSchema = mergeSchemas({
schemas: [
appSchema,
scalarSchema,
]
});
YML
/data-access/codegen.yml
overwrite: true
schema:
- "./graphql/resolvers/core/graphql-tools-scalars.ts"
- "./graphql/resolvers/**/**.graphql"
documents: "../ui/src/components/**/**.graphql"
generates:
graphql/generated.ts:
config:
contextType: "./context#Context"
useIndexSignature: true
mappers:
Category: ./resolvers/types/category#CategoryType
Listing: ./resolvers/types/listing#ListingType
CreateListingPayload: ./resolvers/types/create-listing-payload#CreateListingPayloadType
Location: ./resolvers/types/location#LocationType
Point: ./resolvers/types/point#PointType
User: ./resolvers/types/user#UserType
scalars:
BigInt: any
Byte: any
Currency: any
Date: Date
DateTime: any
Duration: any
EmailAddress: string
GUID: string
HSL: any
HSLA: any
HexColorCode: any
Hexadecimal: any
IBAN: any
IPv4: any
IPv6: any
ISBN: any
ISO8601Duration: any
JSON: any
JSONObject: any
JWT: any
Latitude: any
LocalDate: any
LocalEndTime: any
LocalTime: any
Long: any
Longitude: any
MAC: any
NegativeFloat: any
NegativeInt: any
NonEmptyString: any
NonNegativeFloat: any
NonNegativeInt: any
NonPositiveFloat: any
NonPositiveInt: any
ObjectID: any
PhoneNumber: any
Port: any
PositiveFloat: any
PositiveInt: any
PostalCode: any
RGB: any
RGBA: any
SafeInt: any
Time: any
Timestamp: any
URL: any
USCurrency: any
UUID: any
UnsignedFloat: any
UnsignedInt: any
UtcOffset: any
Void: any
plugins:
- "typescript"
- "typescript-resolvers"
./graphql.schema.json:
plugins:
- "introspection"
../ui/src/generated.tsx:
config:
withHooks: true
withHOC: false
withComponent: false
plugins:
- "typescript"
- "typescript-operations"
- "typed-document-node"
hooks:
afterAllFileWrite:
- npx prettier --write
Last updated