Giter VIP home page Giter VIP logo

reason-graphql's Introduction

CircleCI

Type safe GraphQL server in pure reason. Compiles to nodejs. A direct port from https://github.com/andreas/ocaml-graphql-server to make it work with Javascript backend.

Motivation

Bucklescript is an amazing alternative to other compie-to-js languages. But it's a tragedy that we have such few libraries and bindings written for the server-side.

In the browser, we can get away with just one great binding for React. However, for the server side, people want major building blocks - a solid database abstractions, a fast http server and a Graphql layer are usually must haves for non-trivial projects.

For these type of frameworks, I believe we should do more than just writing simple bindings to existing npm libraries, because then we are not taking advantage of the expressive type system and rich features of OCaml/Reason. We also lose the chance to show what a language like Reason can do for developer happiness and why its worth it to use it over existing alternatives.

What we get over the traditional JS/TS/Flow + graphql-js combination

Type safety without manual work

Like the ReasonReact implementation, GraphQL schemas defined by this library can express more things and place more constraint on the graphql schema than the vanilla javascript or typescript counterparts.

With Typescript (or Flow), you are required to manually write out the types for all your fields, or use a type generation cli tool like https://graphql-code-generator.com - on top of a lot of manual typecasting, because its typesystem cannot express advanced concepts like heterogenous lists (essentially what arguments and fields are, in GraphQL).

To give you a taste of what this means - if we define a field argument called id with type string with graphql-js, even with typescript or flow, it cannot infer the type of args inside the resolver to be {id: string}. We have to manually type it, which makes it very error-prone. Forget about tracking the nullability of the fields - I have seen many production errors where the manually casted types are out of sync with the schema definition, and vice versa, when the schema gets out of sync with the underlying database models.

  type PersonByIdArgs = {
    id: string
  }
  
  personById: {
    type: Person,
    args: { id: GraphqlNonNull(GraphqQLString) },
    resolve: (ctx, parent, args: PersonByIdArgs) => {
                           ^^^^ this is inferred as `Any`, so we need to manually cast it to `PersonByIdArgs`
    }
  }

In Reason, we can use a more advanced feature of the type system called GADT (Generalized Algebriac Data Types) to express our schema types.

What GADT allows us to do is to have type-safe definitions without needing to manually write types for field arguments and resolver. It can "unfold" the types for resolver as you write out field args! (https://drup.github.io/2016/08/02/difflists/)

 field("PersonById", 
  ~typ=person, 
  ~args=Args.[arg("id", nonnull(string))] 
  ~resolve=(_ctx, _parent, id) => {
//                         ^^ Unfolds args into resolver arguments and correctly types it as a string!
  }

This works for as many arguments as you like, it infers nullability for you as well and give you an option('a) if its nullable!

 field("PersonById", 
  ~typ=person, 
  ~args=Args.[arg("id", nonnull(string)), arg("age", int)] 
  ~resolve=(_ctx, _parent, id,     age) => {
                           ^^      ^^^ 
                           string  option(int) because age is not non-null
  }

Features todolist:

  • Query
  • Mutation
  • Async Fields
  • Directives
  • Subscription
  • Non-type Validations (unused fragment, unused variables, and etc)

reason-graphql's People

Contributors

arecvlohe avatar jrmdayn avatar leeor avatar sikanhe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

reason-graphql's Issues

How do you use it?

I've clone it compile it, run the tests, now how do I run the server?

Robust error type

I am now working through an issue where I need to return an error from a resolve() function that is more robust than a simple string.

TL;DR: Errors returned from resolve functions are converted to GraphQL errors, but sometimes, we need to turn them into transfer protocol errors, e.g., HTTP 401 responses.

As an example use case, consider an API that has a set of mutations and queries, some of which are publicly accessible while others require to be called by an authenticated user.
These latter APIs require the ability to return an error to the client to signal that authentication is required. However, the execute layer cannot return an error that is not a string.

I can hack it by adding a facility to the context which will record such errors, but I think a more direct solution should be available.

Looking at the code, the error case of the resolve function is bound by the way errors are converted to a Map in errorResponse().

I'm interested in your opinion on the matter, and whether you already have any plans to address it or an existing approach you are using.

Support for Subscriptions

The subscriptions support is listed as missing feature. Could you explain what would need to be done to add support for subscriptions? Maybe packages you have in mind to help solve the problem? This would make it easier for someone to contribute with this feature :)

More examples

Hello,
I've been exploring this lib (and reasonml at the same time) quite recently, and even though the swapi example was a really great starting point, i've been missing a few recipes.

So far i've needed to add a scalar in my schema, and define a enum, here's how i've done :

let datetime: 'ctx. Schema.typ('ctx, option(BsLuxon.DateTime.dt)) =
  Scalar({
    name: "DateTime",
    description: None,
    serialize: dt => {
      let isoDt: string = BsLuxon.DateTime.toISO((), dt);
      `String(isoDt);
    },
  });

let draftValue = Schema.enumValue(~value=Chapital.Draft, "DRAFT");
let publishedValue = Schema.enumValue(~value=Chapital.Published, "PUBLISHED");
let archivedValue = Schema.enumValue(~value=Chapital.Archived, "ARCHIVED");
let assetStatus: 'ctx. Schema.typ('ctx, option(Chapital.assetStatus)) =
  Enum({
    name: "AssetStatus",
    description: None,
    values: [draftValue, publishedValue, archivedValue],
  });

I'm not sure this is the right way to go (specially about the enum, i saw there was an makeEnum function but couldn't find the proper way to use it).
Cheers

[Question] Can't find GraphqlJsPromise module

Hello,

I'm trying to follow the example of reason-graphql-bs-express but I couldn't open GraphqlJsPromise module.

Also, a similar issue happens if I try to follow this another example. Compiler points an error on this line on node_modules related to the same GraphqlJsPromise module.

Where can I locate this module? Am I missing a dependency?

Thanks in advance!

This is my package.json file:

{
  "name": "hello-reason-gql",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "build": "bsb -make-world",
    "dev:re": "bsb -make-world -w",
    "dev:js": "nodemon src/server.bs.js",
    "clean": "bsb -clean-world"
  },
  "devDependencies": {
    "bs-platform": "8.2.0",
    "nodemon": "2.0.4"
  },
  "dependencies": {
    "bs-express": "1.0.2",
    "reason-dataloader": "0.1.1",
    "reason-future": "2.6.0",
    "reason-graphql": "0.7.0-beta.1",
    "reason-graphql-bs-express": "0.6.0"
  }
}

My bsconfig.json file:

{
  "name": "hello-reason-gql",
  "version": "0.1.0",
  "sources": {
    "dir" : "src",
    "subdirs" : true
  },
  "package-specs": {
    "module": "commonjs",
    "in-source": true
  },
  "suffix": ".bs.js",
  "bs-dependencies": [
    "bs-express",
    "reason-dataloader",
    "reason-future",
    "reason-graphql",
    "reason-graphql-bs-express"
  ],
  "warnings": {
    "number" : "-44-45",
    "error" : "+101"
  },
  "namespace": true,
  "refmt": 3
}

Adding a LICENSE file

Hello!

Thanks for your work, this look awesome!

I just have one request, would you mind adding a LICENSE.md file?

Thanks again!

Suggestion: remove 'variations'

Motivation

Reduce the dependency footprint. Right now, due to RationalJS/future#53, I cannot update my bs-platform version to 8.

Since these variations are very simple and short, they could be documented instead of being a part of the source code. There's also the option of making each variation a separate npm module, but it might be overkill.

I'll be happy to submit a PR if you agree with this suggestion.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.