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 🙏

© 2024 – Pkg Stats / Ryan Hefner

graysql

v0.5.0

Published

GraysQL is a GraphQL manager and loader.

Downloads

14

Readme

GraysQL

GraysQL is a manager and loader for GraphQL. It provides a uniform way of organize your GraphQL schema. GraphQL tries to create an easy to read codebase. It features a plugins API to allow the extension of the core functionalities.

It's directly compatible with the GraphQL reference implementation.

Installation

Install from NPM. You need to install it's peer dependencies if you haven't done yet too.

$ npm install --save graysql graphql graphql-relay

Examples

Here is a simple example to get started:

const GraphQLUtils = require('graphql/utilities');
const GraysQL = require('graysql');
const Graylay = require('graysql/extensions/graylay');  // Add support for Relay entities
const DB = require('./db');  // Mockup data source

const GQL = new GraysQL({
  DB: DB
});

// Add some extensions
GQL.use(Graylay);

// You can import types
GQL.registerType(require('./types/group'));

// Or define them inline
GQL.registerType(function (GQL, types) {
  return {
    name: 'User',
    nodeId: id => GQL.options.DB.getUser(id),
    isTypeOf: obj => obj instanceof GQL.options.DB.User,
    interfaces: ['Node'],
    fields: {
      id: {
        type: 'Int'
      },
      nick: {
        type: 'String'
      },
      group: {
        type: 'Group'  // Define type dependencies
      }
    },
    queries: {
      user: {  // You can define inline queries
        type: 'User',
        args: {
          id: {
            type: 'Int'
          }
        },
        resolve: (_, args) => GQL.options.DB.getUser(args.id);
      },
      users: require('./queries/users')  // Or import them
    }
  }
});


const Schema = GQL.generateSchema();
console.log(GraphQLUtils.printSchema(Schema));

Overview

Type

A Type is a representation of a GraphQLObjectType. Types are the main object in GraysQL and every other object relates to them in some way. They define the layout of your schema. To create a new Type simply create a JavaScript function that takes an argument that is a reference to the current GraysQL instance and that function should return an object with the keys needed to create an object.

The mandatory keys are:

  • name: A string representing the name of the type that will be used to create and store it. Other types will use this name to reference the type as well.
  • fields: An object representing the fields of the type. This is directly related to the fields property in GraphQLObjectType.

The type of the field is specified as a string instead of a GraphQLScalarType. The basic list of supported types is:

  • Int: Translates to GraphQLInt
  • String: Translates to GraphQLString
  • TypeName: Reference another type in the system.
  • [TypeName]: Reference an array of another type in the system. Equivalent to GraphQLList.

Besides that, extensions can define custom keys or even you can define your own keys if you need it.

Example Types

Types can be defined in its own file. For example, types/user.js and types/group.js.


// types/user.js
module.exports = function (GQL) {
  return {
    name: 'User',
    fields: {
      id: { type: 'Int' },
      nick: { type: 'String' },
      group: { type: 'String' }
    }
  };
}

// types/group.js
module.exports = function (GQL) {
  return {
    name: 'Group',
    fields: {
      id: { type: 'Int' },
      nick: { type: 'String' },
      members: { type: '[User]' }
    }
  };
}

When the two types are defined, we can register them within the system:

const GraysQL = require('graysql');

const GQL = new GraysQL();
GQL.registerType(require('./types/user.js'));
GQL.registerType(require('./types/group.js'));

Interfaces

TODO

Query

In order to make request to the Schema you must define Queries in your Types. As queries are only plain Javascript object, they can be defined inline in your types or in separate files and later exported. However, if you define your queries in standalone files, you should wrap them in a function. This function, like types, will receive a single parameter that is the current GQL intance, and should return the query object.

Once a query is defined, you can add it to the sytem using two ways. Embedding them in a type, like the first example, or using the auxiliar function GQL.addQuery(query).

The query object should have three keys that are mandatory:

  • type: The type to which the query is applied. It has to be registered in the system before the query will be generated.
  • args: It's equivalent to args in GraphQL queries. It uses the same syntax for types than the key fields in Types.
  • resolve: It's the same as resolve in GraphQL queries.

Example Queries

// queries/user.js
module.exports = function (GQL) {
  return {
    type: 'User',
    args: {
      id: { type: 'Int' }
    },
    resolve: (_, args) => GQL.options.DB.getUser(args.id)
  }
}

// index.js
GQL.addQuery('user', require('./queries/user'));

Mutations

TODO

GraysQL

Constructor

new GraysQL(options)

GraysQL is initialized by passing an object with any number of custom keys. This keys will be available later in the GraysQL instance.

const DB = require('./db');
const GraysQL = require('graysql');
const GQL = new GraysQL({
  DB: DB
});
console.log(GQL.options.DB);

Methods

GraysQL.use(extension)

Receives a GraysQL extension and add it to GraysQL.

  • Parameters
    • extension Function: A valid extension to be added.
const GraysQL = require('graysql');
const LoadFromDir = require('graysql/extensions/load-from-dir');
const Graylay = require('graysql/extensions/graylay');

const GQL = new GraysQL();
GQL.use(LoadFromDir);
GQL.use(Graylay);

GQL.registerType(type, [overwrite])

Registers a new type in the system.

  • Parameters
    • type Function: A valid type to be registered.
    • overwrite Boolean: A flag wether the registered type should overwrite an existent type with the same name or not.
  • Returns
    • Object: The registered type.
const GraysQL = require('graysql');
const GQL = new GraysQL();

const UserType = function (GQL) {
  return {
    name: 'User',
    fields: {
      id: { type: 'Int' },
      nick: { type: 'String' }
    }
  };
}

GQL.registerType(UserType);

GQL.registerInterface(interface, [overwrite])

Registers a new interface in the system that can be later implemented by types. Interfaces should be registered before the implementing types.

  • Parameters
    • interface Function: A valid interface to be registered.
    • overwrite Boolean: A flag wether the registered interface should overwrite an existent interface with the same name or not.
  • Returns
    • Object: The registered interface.
const GraysQL = require('graysql');
const GQL = new GraysQL();

const EmployeeInterface = function (GQL) {
  return {
    name: 'Employee',
    fields: {
      employeeId: { type: 'Int' }
    }
  };
}
GQL.registerInterface(EmployeeInterface);

GQL.addQuery(name, query, [overwrite])

Adds a new query to the system. Note that if the type of the query is not already registered in the system, this will throw an error.

  • Parameters
    • name String: The name of the query to be added.
    • query Function: A valid query to be added.
    • overwrite Boolean: A flag wether the added query should overwrite an existent query with the same name or not.
  • Returns
    • Object: The added query.

GQL.addMutation(name, mutation, [overwrite])

Adds a new mutation to the system. Note that if the type of the mutation is not already registered in the system, this will throw an error.

  • Parameters
    • name String: The name of the mutation to be added.
    • mutation Function: A valid mutation to be added.
    • overwrite Boolean: A flag wether the added mutation should overwrite an existent mutation with the same name or not.
  • Returns
    • Object: The added mutation.

GQL.generateSchema()

Generates a GraphQLSchema from the registered objects.

  • Returns
    • GraphQLSchema: The generated schema.

Extensions

ORMLoader

Loads the models defined with an ORM into GraysQL to generate a Schema from them. More in the project repository.

LoadFromDir

Allows GraysQL to scan a folder to build a schema from the files found. You only need to define your schema and GraysQL will take care of the registration process for you. This way, you can model your schema from the dir structure. This allows you to add or delete objects on the fly, without having to register the new ones, or de-register the old ones

The folder structure that LoadFromDir will search for it's:

schema/
├── types/
│   ├── type-name/
│   │   └── index.js
├── interfaces/
│   └── interface-name.js

Where:

  • schema/: Is the root folder of the Schema. It contains all the structure.
  • types/: Contains all the defined types. Each type goes in its own folder.
  • type-name/: Contains a type. The type is defined in the index.js file.
  • interfaces/: Contains the interfaces. Each interface goes in it's own file interface-name.js.

In order for the queries to be automatically added to GraysQL, each type should define its queries in its own query key.

LoadFromDir defines a new method in GraysQL:

GQL.load(directory, [overwrite])

Load the objects from the given folder and add them to GraysQL.

  • Parameters
    • directory String: The root folder that contains the objects.
    • Overwrite Boolean: A flag wether the loaded objects should overwrite existent objects with the same name or not.
// schema/types/user.js
module.exports = function (GQL) {
  return {
    name: 'User',
    fields: {
      id: { type: 'Int' },
      nick: { type: 'String' }
    },
    queries: {
      user: {
        type: 'User',
        args: { id: { type: 'Id' } },
        resolve: (_, args) => GQL.options.DB.getUser(args.id)
      }
    }
  };
}

// index.js
const DB = require('./db');
const GraysQL = require('graysql');
const LoadFromDir = require('graysql/extensions/load-from-dir');

const GQL = new GraysQL({ DB: DB });
GQL.use(LoadFromDir);
GQL.load('schema');

Graylay

Creates Relay compatible objects from your objects. Add needed entities for Relay to work with your Schema. For instance, it creates the Node interface or establishes connections between your types. Besides to the common keys your type need to define, Graylay adds one more, nodeId. Graylay will use this key to generate the Node interface needed by Relay.

Additionaly, Graylay adds two new symbols to define relations between types. If you precede a type name with @ Graylay will translate that into a connection with that type and will resolve to a connectionFromArray. If you precede a type name with @> instead, Graylay will translate that into a connection that will resolve to a connectionFromPromisedArray. In any case, you can specify your own resolve, and Graylay will honour it. Besides that, your types will need to define the usual keys needed by Relay like isTypeOf and implement the Node interface.

const GraysQL = require('graysql');
const DB = require('./db');
const GraphQLRelay = require('graphql-relay');
const Graylay = require('graysql/extensions/graylay');

const UserType = function (GQL) {
  return {
    name: 'User',
    nodeId: id => GQL.options.DB.getUser(id),
    isTypeOf: obj => obj instanceof GQL.options.DB.User,
    interfaces: ['Node'],
    fields: {
      id: { type: 'Int' },
      nick: { type: 'String' },
      groups: {
        type: '@Group'  // Resolves to a connectionFromArray
      }
    }
  };
}

const GQL = new GraysQL({ DB: DB });
GQL.use(Graylay);
GQL.registerType(UserType);

Plugin API

TODO

Examples

Usage examples can be found in examples directory.

Tests

The tests are written with mocha and can be run with the following command:

$ npm test

To get a code coverage report, run the following command:

$ npm run cover

TODO

  • [x] Add support for non nullable args in mutations and queries.
  • [x] Implement Graylay and LoadFromDir.
  • [ ] Document Plugin API.
  • [ ] Provide an async version of LoadFromDir.
  • [x] Add support for lists as arguments in queries and mutations.

License

MIT