Convert JSON schema to GraphQL types (string) including GraphQL transforms/directives (decorators).
To render GraphQL entitities, we need to traverse the state created via the basic schema builder.
For each type or enum we can reshape the shape generated. Reshaping can add extra metadata as needed, such as decorators for properties and types.
Then pass we can pass the final shapes to be rendered.
Schema transformers and builders:
Note that the graphschematojson
library is the perfect gateway to take the JSON schema model output and transform to a GrapQL typedef.
Use the graph-schema-json-writer to write a TypeScript class source file for a TypeORM entity from a JS schema definition object.
You can also use the library json-schema-to-graphql-types to a similar, but limited effect as illustrated in the example below:
See also Type Resolution
const schema = {
$schema: "http://json-schema.org/draft-07/schema#",
$id: "http://example.com/person.schema.json",
title: "Person",
description: "A person",
type: "object",
graphql: {
decorators: {
client: true
}
},
properties: {
id: {
type: "string",
generated: true,
unique: true,
required: true
},
name: {
description: "Name of the person",
type: "string",
graphql: {
decorators: {
connection: {
name: "UserNames"
}
}
}
},
age: {
description: "Age of person",
type: "integer",
required: true
},
money: {
description: "Money in pocket",
type: "number"
},
accounts: {
description: "Bank accounts",
type: "array",
items: {
$ref: "#/definitions/account"
}
},
numberOfChildren: {
description: "Children parented",
type: "array",
items: {
type: "number",
enum: [0, 1, 2]
}
},
favoriteCoulor: {
description: "Colors liked",
name: "color",
type: "string",
items: {
type: "number",
enum: ["red", "green", "blue"]
}
},
car: {
description: "Car owned",
type: "object",
decorators: {
model: true
},
properties: {
name: {
type: "string",
required: true
}
}
}
},
definitions: {
account: {
description: "Bank account",
type: "object",
decorators: {
client: true
},
properties: {
id: {
type: "string",
generated: true,
unique: true,
required: true
},
name: {
description: "Name of the account",
type: "string"
},
money: {
description: "Money in account",
type: "number",
required: true
},
type: {
description: "Account type",
// should be implicit: "name": "AccountType",
type: "string",
enum: ["saving", "credit"]
}
}
}
},
required: ["name"]
};
const { buildTypes } = require("json-schema-to-graphql-types");
const mapping = buildTypes(schema);
console.log({
mapping
});
Will output the following GraphQL types (as a raw indented text)
type Person {
id: ID!
name: String! @connection(name: "UserNames")
age: Int!
money: Float
car: PersonCar
accounts: [Account]
}
type @model PersonCar {
name: String!
}
type Account {
id: ID!
name: String!
money: Float!
type: AccountType
}
enum AccountType {
saving
credit
}
enum Color {
red
green
blue
}
Add transforms/directives for GraphQL types in a declarative way so they can be included in the generated types.
Allow supplying an extra config
object with meta data for directives to be merged into output
{
decorators: {
Cart: {
client: true
}
User: {
email: {
unique: true
}
},
Post: {
blog: {
connection: {
name: 'BlogPosts'
}
}
}
}
}
Simply pass the config
object as second argument
const types = buildTypes(schema, config);
console.log({
types
});
StackOverflow: json-schema-additional-metadata
"You don't have to do anything special to use additional metadata keywords. You can just use them. In JSON Schema it is not an error to include undefined keywords."
The GraphQL decorators can also be supplied via a graphql
entry directly in the JSON schema as follows:
properties: {
blog: {
type: 'string'
graphql: {
decorators: {
connection: {
name: 'BlogPosts'
}
}
}
},
}
You can alternatively use the decorators
entry directly as follows.
properties: {
blog: {
type: 'string'
decorators: {
connection: {
name: 'BlogPosts'
}
}
},
}
You might want to use decorators for other purposes as well (for other generators).
We recommend namespacing decorators
under graphql
for large projects that heavily relies on a schema driven development approach.
The configuration object can take type mappings that will be used in the generated types.
const config = {
_meta_: {
types: {
date: "Date", // to use custom Date scalar
json: "JSON" // to use custom JSON scalar
}
}
decorators: {
// ...
}
};
const types = buildTypes(schema, config);
Please see GraphQL scalar type and its input and result coercion
You can customize the output to use your own scalar types via config._meta_.types
. The builder (generator) currently assumes you are using the Date
scalar by default.
@model
@auth
@connection
@searchable
Also see graphql-transform-tutorial
type Post @model {
id: ID!
title: String!
blog: Blog @connection(name: "BlogPosts")
comments: [Comment] @connection(name: "PostComments")
}
type User {
id: ID! @unique
age: Int
email: String! @unique
name: String!
accessRole: AccessRole
posts: [Post!]!
}
Apollo has a @client directive for Apollo Link State
Adding the @client
directive to a field is how Apollo Link knows to resolve your data from the Apollo cache instead of making a network request. This approach is similar to other Apollo Link APIs, such as apollo-link-rest, which uses the @rest
directive to specify fields that should be fetched from a REST endpoint.
const getUser = gql`
query getUser($id: String) {
user(id: $id) {
id
name
cart @client {
product {
name
id
}
}
}
}
`;
Thanks to the power of directives and Apollo Link, you’ll (soon) be able to request @client
data, @rest
data, and data from your GraphQL server all in one query!
- graphql-to-json-schema GraphQL Schema to JSON Schema
- json-schema-to-graphql-types
- jsonschema-to-graphql
Add support for union types
Useful for arrays that can have multiple types under items
.
union SearchResult = User | Movie | Book
Add support for enum
type, see How to design GraphQL queries and mutations: enum type
The enum
type can be used as a primitive value according to GraphQL specs.
enum TaskStateEnum {
assigned
unassigned
inProgress
}