Graphqly
Graphqly
is a library to reduce the complexity of developing Graphql services.
1. Motivation
I think it's complicated
to develop & maintain Graphql services (mainly their schemas). Let's look at how we currently define such schemas:
; const rootSchema = ` interface List { offset: Int! limit: Int! total: Int! } type Post { id: Int! title: String! content: String! } type Posts implements List { offset: Int! limit: Int! total: Int! posts: [Post!] } type Query { # List all posts posts: [Post] } type Mutation { addPost(title: String!, content: String!): Post } type Subscription { # Subscription fires on every comment added postAdded: Post } schema { query: Query mutation: Mutation subscription: Subscription }`; const rootResolvers = Query: { // .. } Mutation: { // .. } ; const resolverMap = List: { if objproducts return "Products"; return null; } const schema = ;;
In my opinion, there are several things to pay attention to:
-
We have to define basic structures (type, interface, input, enum) and there's no mechanism for us to
reuse
such structures in other defintions. Byreuse
, I mean we can't have something like:type PostEx extends Post{ }
This makes
reusability
hard. -
Each operation (query, mutation, subscription) requires an associated resolver function. Separating definitions and resolvers makes Graphql flexible. But if you have large operations to serve, it's a pain in the ass to organize them properly.
Graphqly
is designed to solve above problems elegantly.
2. Installation
npm install graphqly --save
# or if you're using yarn
# yarn add graphqly
3. Usage
For more information, please visit repo graphqly-demo
3.1 Definitions
; const gBuilder = graphly; // define types, inputs ... (in any order)gBuildertype"Products"; gBuildertype"Product"; // we're too lazy to define a separate input, so we can `extend` other structuregBuilderinput"ProductInput"; gBuilder; // define interface `List`gBuilder; // subscriptions may not need to provide resolving functions// If you want to manipulate published data, you must provide resolving functions of // `(payload, args, context, info) => {any}` as described by// https://github.com/apollographql/graphql-subscriptions#payload-manipulationgBuilder; gBuilder; gBuilder; // and finallyconst schema = gBuilder; // inside, `makeExecutableSchema` is invoked
You may work with meta data of Resolvable instances (including Queries, Mutations & Subscriptions) via methods set
, get
:
gBuilder // imagine we have scopes to use when authorizing requests;
3.2 Reusability
We may reuse other definitions by grouping them into providers:
{ buildertype"Brands"; buildertype"Brand";} { // ......} gBuilder; // it's fun, right? // you can also use multiple providersgBuilder;
3.3 Extendability
Graphqly
can be extended by using hooks. For example, we can have features of logging, caching, authorizing... through hooks.
Graphqly
has 2 kinds of hooks:
Global hooks
in SchemaBuilderLocal hooks
in each operations (queries or mutations)
There are 4 hook points, including pre.query
, post.query
, pre.mutation
, post.mutation
.
// Global hookgBuilder; // Local hookgBuilder ;
3.4 Logging
createBuilder
accepts configurable options. Currently, graphqly
only allows users to configure logging capability.
;const gBuilder = ; // now in resolving functions, you may use functions `error`, `info`...gBuilder ;
You may provide your own logger factories. Please have a look at directory lib/extra/winston
for more information.
By default, there's no logger factory configured. But if you configure it, in case of runtime errors in resolving functions, you may have related information printed out. For example
2017-07-26T13:29:49.403Z - error: [Subscription orderTimelineChanged] Filtering error. Detail: ReferenceError: _ is not defined
4. License
GNU GPL3
Please submit a pull request if you see anything that can be improved!