Skip to content

Latest commit

 

History

History
404 lines (330 loc) · 9.26 KB

File metadata and controls

404 lines (330 loc) · 9.26 KB

GraphQL

Convert JSON schema to GraphQL types (string) including GraphQL transforms/directives (decorators).

How?

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

Example

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)

Person type

type Person {
  id: ID!
  name: String! @connection(name: "UserNames")
  age: Int!
  money: Float
  car: PersonCar
  accounts: [Account]
}

PersonCar type

type @model PersonCar {
  name: String!
}

Account type

type Account {
  id: ID!
  name: String!
  money: Float!
  type: AccountType
}

AccountType enum

enum AccountType {
  saving
  credit
}

Color enum

enum Color {
  red
  green
  blue
}

Supporting transforms (decorators)

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
});

Using meta data embedded in JSON schema

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.

Type customization

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);

Supporting Scalars

Please see GraphQL scalar type and its input and result coercion

Date scalar

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.

GraphQL transforms

Amplify

Amplify GraphQL transforms

  • @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")
}

Prisma

Prisma

type User {
  id: ID! @unique
  age: Int
  email: String! @unique
  name: String!
  accessRole: AccessRole
  posts: [Post!]!
}

Apollo

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!

Alternatives

Union types

Add support for union types

Useful for arrays that can have multiple types under items.

union SearchResult = User | Movie | Book

Enum

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
}