npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

gql-schema

v0.5.5

Published

Yet another experimental library for defining graphql schemas using decorators.

Readme

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
}