GraphQL Schema Decorators
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
}
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 { }
} ;
@ 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 ) ;
@ 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 ;
@ field ( GraphQLInt )
updatedAt : number ;
}
@ 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
}