gql-schema
TypeScript icon, indicating that this package has built-in type declarations

0.5.5 • Public • Published

GraphQL Schema Decorators

Build Status Coverage Status

Yet another library for defining graphql schemas using decorators.

Warning: This software is still at an early stage of development. Use at your own risk!

Getting started

npm install gql-schema --save

This library requires node.js 4.4.0 or higher, typescript 2.4.x and uses es7 decorators. Make sure your tsconfig.json has experimentalDecorators set to true true

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "noImplicitAny": false,
        "sourceMap": false,
        "moduleResolution": "node",
        "experimentalDecorators": true
    }
}

Defining Schema

import {type, field, createSchema } from 'gql-schema';
import {GraphQLString, GraphQLSchema, graphql} from "graphql";
 
@type()
class Query {
    @field(GraphQLString) 
    helloWorld:string
}
 
@type()
class Mutation {
    @field(GraphQLString) 
    doSomeMutation:string
}
 
// create schema from annotated classes
const schema:GraphQLSchema = createSchema(Query, Mutation)
 
async function main() {
    const result = await graphql(schema, `query { helloWorld } `);
    console.log(result.data.helloWorld);
}
 
main();

@type decorator

import {type, field, list, nonNull, nonNullItems, resolve, description, id, input, params} from 'gql-schema';
import {GraphQLString, GraphQLInt} from "graphql";
 
const resolveFunction = (_, args:SomeParams, ctx):Partial<SomeType> => {
    return {} // return SomeData type. For most cases it would be Partial<SomeData> because nested data will be resolved by other resolvers
};
 
@type({description: 'SomeType description'})
class SomeType {
    @description('id field description')
    @id() @nonNull()
    id:string;
 
    @field(GraphQLInt)
    someNullableField?:number;
 
    @field(GraphQLString) @nonNull()
    nonNullableField:string;
 
    @list(GraphQLString)
    nullableListWithNullItemsAllowed?: string[];
 
    @list(GraphQLString) @nonNull()
    nonNullableListWithNullItemsAllowed: string[];
 
    @list(GraphQLString) @nonNull() @nonNullItems()
    nonNullableListWithNullItemsForbidden: string[]
}
 
@input()
class SomeParams {
    @field(GraphQLString) @nonNull()
    someParam:string
}
 
@type()
class Query {
    @field(SomeType) @nonNull()
    @params(SomeParams) @resolve(resolveFunction)
    someData:SomeType
}

Given annotated classes will generate following schema definition

# SomeType description
type SomeType {
   # id field description
   id: ID!
   someNullableField: Int
   nonNullableField: String! 
   nullableListWithNullItemsAllowed: [String]
   nonNullableListWithNullItemsAllowed: [String]!
   nonNullableListWithNullItemsForbidden: [String!]!
}
 
type Query {
    someData(someParam: String!):SomeType!
}
 

@input decorator

import {field, input, nonNull, params, input, resolve, type} from 'gql-schema';
import {GraphQLString} from "graphql";
 
const createUser = (_, args:CreateUserParams, ctx):Partial<User> => {
    return {}
};
 
@input()
class NewUserParams {
    @field(GraphQLString) @nonNull()
    email:string;
 
    @field(GraphQLString) @nonNull()
    firstName:string;
 
    @field(GraphQLString) @nonNull()
    password:string;
}
 
@input()
class NewUserAddressParams {
    @field(GraphQLString) @nonNull()
    street:string;
 
    @field(GraphQLString) @nonNull()
    city:string;
}
 
@input()
class CreateUserParams {
    @field(NewUserParams) @nonNull()
    userParams:NewUserParams;
 
    @field(NewUserAddressParams) @nonNull()
    userAddressParams:NewUserAddressParams;
}
 
 
@type()
class Address {
    @field(GraphQLString) @nonNull()
    street:string;
 
    @field(GraphQLString) @nonNull()
    city:string;
}
 
@type()
class User {
    @field(GraphQLString) @nonNull()
    email:string;
 
    @field(GraphQLString) @nonNull()
    firstName:string;
 
    @field(Address) @nonNull()
    address:Address
}
 
 
@type()
class Mutation {
    @field(User) @nonNull()
    @params(CreateUserParams) @resolve(createUser)
    createUser:User
}

Given annotated classes will generate following schema definition

input NewUserParams {
    email:String!
    firstName:String!
    password:String!
}
 
input NewUserAddressParams{
    street: String!
    city: String!
 
type User {
    email:String!
    firstName:String!
    address:Address!
}
 
type Address {
    street: String!
    city: String!
}
 
type Mutation {
    createUser(userParams: NewUserParams, addressParams: NewUserAddressParams):User!
}

Using typescript enums

import {decorateEnum, type, field, nonNull} from 'gql-schema';
 
enum JobStatus {
    done = 'ok',
    failed = 'error'
}
 
decorateEnum('Status', JobStatus); // it makes JobStatus acceptable by @field decorator
 
@type()
class BackgroundJob {
    @field(JobStatus) @nonNull()
    status:JobStatus;
}

Given annotated classes will produce following schema definition.

type BackgroundJob {
    status: Status!
}
 
enum Status {
    done
    failed
}

GraphQLEnumType for JobStatus will have the same mapping as ts enum

[
    {name: 'done', value: 'ok'},
    {name: 'failed', value: 'error'},
]

Defining union types

import {createUnion, field, nonNull, type} from "gql-schema";
import {GraphQLInt} from "graphql";
 
@type()
class Circle {
    @field(GraphQLInt) @nonNull()
    radius:number;
}
 
@type()
class Square {
    @field(GraphQLInt) @nonNull()
    length:string;
}
 
const Shape = createUnion('Shape', [Circle, Square], (obj) => {
    if (_.isNumber(obj.radius)) {
        return Circle
    }
 
    if (_.isNumber(obj.length)) {
        return Square
    }
 
    throw new Error(`Unknown shape type`);
});
 
@type()
class SomeType {
    @field(Shape) @nonNull()
    shape: Circle | Square
}

Given code will produce following graphql schema definition

type Circle {
    radius: Int!
}
 
type Square {
    length: Int!
}
 
union Shape = Circle | Square
 
type Shape {
    shape:Shape!
}

@interfaceType

import {field, id, interfaceType, nonNull, type} from "gql-schema";
import {GraphQLInt, GraphQLString} from "graphql";
 
@interfaceType({
    resolveType: (asset:Asset) => {
        if (asset.mimeType === 'image/jpg'){
            return Image
        }
 
        if (asset.mimeType === 'audio/mp3'){
            return AudioAsset
        }
 
        throw new Error("Unknown asset type")
    }
})
export class Asset {
    @id() @nonNull()
    id:string;
 
    @field(GraphQLInt) @nonNull()
    size: number;
 
    @field(GraphQLString) @nonNull()
    mimeType: string;
}
 
@type({
    interfaces: () => [Asset]
})
export class Image {
    @id() @nonNull()
    id:string;
 
    @field(GraphQLInt) @nonNull()
    size:number;
 
    @field(GraphQLString) @nonNull()
    mimeType:string;
 
    @field(GraphQLInt)
    width:number;
 
    @field(GraphQLInt)
    height:number;
}
 
@type({
    interfaces: () => [Asset]
})
export class AudioAsset {
    @id() @nonNull()
    id:string;
 
    @field(GraphQLInt) @nonNull()
    size:number;
 
    @field(GraphQLString) @nonNull()
    mimeType:string;
 
    @field(GraphQLInt)
    length:number;
}

Given annotated code will produce following graphql schema

interface Asset {
     id: ID!
     size: Int!
     mimeType: String!
}
            
type Image implements Asset {
     id: ID!
     size: Int!
     mimeType: String!
     width: Int
     height: Int
}
 
type AudioAsset implements Asset {
    id: ID!
    size: Int!
    mimeType: String!
    length: Int
}

Inheritance

import {field, id, interfaceType, nonNull, type} from "gql-schema";
import {GraphQLInt, GraphQLString} from "graphql";
 
@type()
class PersistedObject {
    @id() @nonNull()
    id:string;
 
    @field(GraphQLInt)
    createdAt:number; //for simplification store as integer timestamp
 
    @field(GraphQLInt)
    updatedAt:number; //for simplification store as integer timestamp
}
 
@type()
class User extends PersistedObject {
    @field(GraphQLString)
    email:string
}
 
@type()
class Product extends PersistedObject {
    @field(GraphQLString)
    productName:string
}

Given annotated classes will produce

type User {
    id: ID!
    createdAt: Int
    updatedAt: Int
    email: String
}
 
type Product {
    id: ID!
    createdAt: Int
    updatedAt: Int
    productName: String
}

Readme

Keywords

none

Package Sidebar

Install

npm i gql-schema

Weekly Downloads

2

Version

0.5.5

License

MIT

Last publish

Collaborators

  • robak86