Giter Club home page Giter Club logo

graffiti's Introduction

⚠ Notice: the development of the package is discontinued. Use it for educational purposes and hobby projects only.

graffiti

npm version Codeship Status for RisingStack/graffiti bitHound Overall Score Known Vulnerabilities

Currently the consumption of HTTP REST APIs dominate the client-side world, GraphQL aims to change this. This transition can be time-consuming - this is where graffiti comes into the picture.

We don't want to rewrite our application - no one wants that. graffiti provides an Express middleware, a Hapi plugin and a Koa middleware to convert your existing models into a GraphQL schema and exposes it over HTTP.

What is GraphQL?

GraphQL is a query language created by Facebook in 2012 which provides a common interface between the client and the server for data fetching and manipulations.

The client asks for various data from the GraphQL server via queries. The response format is described in the query and defined by the client instead of the server: they are called client‐specified queries.

For more info check out RisingStack's GraphQL tutorial.

Example server and queries

For a running example server and executable queries, check out our example repository and play with your GraphQL queries: graffiti-example

Adapters

Supported servers

Install

npm install @risingstack/graffiti --save

Usage

  1. run MongoDB
  2. register the middleware
  3. provide a schema (returned by an adapters getSchema method or your own GraphQLSchema instance)
  4. the GraphQL endpoint is available on /graphql

Express

import express from 'express';
import { json } from 'body-parser';
import graffiti from '@risingstack/graffiti';
import { getSchema } from '@risingstack/graffiti-mongoose';

import Cat from './models/Cat';
import User from './models/User';

const app = express();

// parse body as json
app.use(json());

app.use(graffiti.express({
  schema: getSchema([User, Cat]),
  context: {} // custom context
}));

app.listen(3000);

Hapi

import { Server } from 'hapi';
import graffiti from '@risingstack/graffiti';
import { getSchema } from '@risingstack/graffiti-mongoose';

const server = new Server();
server.connection({ port: 3000 });

server.register({
  register: graffiti.hapi,
  options: {
    schema: getSchema([User, Cat]),
    context: {}, // custom context 
    config: {} // config parameter for hapi graphql route
  }
}, function (err) {
  if (err) {
    console.error('Failed to load plugin:', err);
  }

  server.start(function () {
      console.log('Server running at:', server.info.uri);
  });
});

Koa

import koa from 'koa';
import parser from 'koa-bodyparser';
import graffiti from '@risingstack/graffiti';
import { getSchema } from '@risingstack/graffiti-mongoose';

import Cat from './models/Cat';
import User from './models/User';

const app = koa();

app.use(parser());

app.use(graffiti.koa({
  schema: getSchema([User, Cat]),
  context: {} // custom context
}));

app.listen(3000);

Options

  • schema: a GraphQLSchema instance. You can use an adapters getSchema method, or provide your own schema. (required)
  • graphiql: may present GraphiQL when loaded directly from a browser. (default: true)
  • context: custom GraphQL context object. (default: {})

Test

npm test

graffiti's People

Contributors

developer-rakeshpaul avatar fiws avatar gergelyke avatar tothandras 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  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

graffiti's Issues

graphql 0.5.0 compatibility ?

Currently available graphql version is 0.5.0, but graffiti is peerDependent on graphql@^0.4.17
Is compatibility possible or planned or should we stick to graphql 0.4.17 ?

SQL / Bookshelf.js progress

Hi,

First, let me say that I love what you've done with the mongoose adapter.

I'm currently converting a project to graphQL that already uses bookshelf.js and stumbled upon graffiti, so I was curious as to what the current progress / ETA is and whether you could do with a hand finishing it.

Koa tests don't actually test

The koa tests in src/koa/koa.spec.js use generator functions. The standard mocha installation does not support generators... because of that, the tests always pass, since the generators successfully return an iterator (whose next() function is never called), even if an expect condition should fail (since it is actually never run).

My suggestion is to use https://github.com/blakeembrey/co-mocha
You can simply npm install --save-dev co-mocha, then just add --require co-mocha to the "test" command in package.json.

co-mocha monkey-patches mocha to properly handle generators.

Road towards relay compatibility

Just putting some thoughts down of currently missing features:

Unique Object IDs

Relay wants to be able to (re-)fetch any object in the graph by a unique ID via a root query named node:

node (id: "user123") {
  id
  name
}

Relay methods: fromGlobalId/toGlobalId
See: https://github.com/relayjs/relay-starter-kit/blob/master/data/schema.js#L115

Pagination

On the query side, relay requires first and after arguments (as well as last and before for backwards pagination):

users (first: 10, after: "user123") {
  edges {
    cursor
    node {
      id
      name
    }
  }
  pageInfo {
    hasNextPage
  }
}

Also see: https://facebook.github.io/relay/graphql/connections.htm

Mutation

TBC

I expect graphql-relay-js to help us out with the more intricate details

I hope we can work towards relay compatibility. Please let me know if this is more appropriate for an adapter, e.g. graffiti-mongoose

1 to 1 mapping with persistence schema ?

Hi,

One of the greatest benefit of GraphQL is that it gives your application a strong typing system you can rely on but that graph does not have to map 1 for 1 with a given persistence engine. For example a given attribute in Graphql could come from a rating service while everything else is coming from Mongo.

It seems to me that right now, by trying to import the models from Mongo, we loose the ability to enhance or complement that GraphQL Schema so it can be more that just what our persistence engine is providing.

I think it's great if Graffiti can help so you dont have to repeat yourself twice on how you declare your schema but at the same time, it is equally important that graffiti gives us the opportunity to add stuff or override stuff in the GraphQL schema as well.

Errors are serialized to {}

Overview of the Issue

Something I have seen in the koa adapter at least. When graphql promise returns an object with a list of errors, they are of the Error type (obviously), so they get/might get serialized to {} in the JSON response (which is worse).

Reproduce the Error

Provide an unambiguous set of steps, Node version, package version, etc.

Steps to reproduce:

  1. Have a bad graphql setup or something similar to my error which was "Schema must be an instance of GraphQLSchema. Also ensure that there are not multiple versions of GraphQL installed in your node_modules directory."
  2. get { "errors": [ {} ] } response

Node version: 7.0.0
Graffiti version: 3.2.0

Quick & dirty fix

The solution that I hacked for myself to see the error was something like changing in koa.js

this.body = yield graphql(schema, query, { request: this.request }, context, parsedVariables);

to

this.body = yield graphql(schema, query, { request: this.request }, context, parsedVariables)
                  .then((response) => {
                    if (response.errors) {
                      response.errors = response.errors.map((err) => err.message);
                    }
                    return response;
                  });

This is an ugly solution. I see the syntax errors are mapped elsewhere to something readable, but my error wasn't.

Suggestion: boilerplate project for graffiti adapters

Hi all!

Could you all consider setting up a default project for adapters that includes the build system and some of the boilerplate code? I'm looking at building an adapter for Waterline, and in researching this I found their adapter boilerplate. I think it would be great if there were a similar project which others could fork to quickly get started implementing adapters for other persistence layers.

Thanks!

Combine multiple scheme sources?

I have a use case where I'm using a hand built schema (which talks to other APIs) and a graffiti mongoose schema. I'd like to be able to combine them into one endpoint. Is this possible at the moment?

Thanks,
Jon

Query type in pre hook?

I'd like to be able to know what type of query I'm getting (mutation or query) in a pre hook so I can authorize based on that information.

IE: I'm trying to enable mutations only for admins. Is this possible?

Best,
Jon

Rethinkdb

Whats the status on the rethinkdb adapter?

Is it possible to use with Node 0.12.x?

I have the following error in console:

/Users/dmitri/myapp/node_modules/@risingstack/graffiti/node_modules/boom/lib/index.js:5
const Http = require('http');
^^^^^
SyntaxError: Use of const in strict mode.

Authorization? Security?

Let's say I have records that only certain users supposed to have access to, is there any current way to specify such limitations?

Custom rootValue and context parameters for graphql

HI, i saw this lib pass the same request object in graphql function at the 3rd and 4th parameters which are rootValue and context.
Why not make it customizable so we can pass the rootValue and context parameters for graphql.

Question: Optional config param for the hapi register method

Overview of the Issue

Team is it possible to provide an optional config param to the register method of the hapi plugin. This will help users with ability to configure authentication mode and other params easily. For instance if we change the register method for the hapi plugin like below

const plugin = {
  register: (server, { graphiql = true, context = {}, config = {}, schema = required() } = {}, next) => {
    const handler = (request, reply) => {
      const data = request.payload || request.query || {};
      const { query, variables } = data;

      if (accepts(request, 'html') && graphiql) {
        return reply(renderGraphiQL({ query, variables }));
      }

      if (query && query.includes('mutation') && isGet(request)) {
        return reply(methodNotAllowed('GraphQL mutation only allowed in POST request.'));
      }

      let parsedVariables = variables;
      try {
        parsedVariables = JSON.parse(variables);
      } catch (err) {
        // ignore
      }

      return graphql(schema, query, { request }, context, parsedVariables)
        .then((result) => {
          if (result.errors) {
            const message = result.errors.map((error) => error.message).join('\n');
            reply(badRequest(message));
            return;
          }

          reply(result);
        })
        .catch((err) => {
          reply(badRequest(err));
        });
    };

    server.route({
      method: 'POST',
      path: '/graphql',
      config,
      handler
    });

    if (graphiql) {
      server.route({
        method: 'GET',
        path: '/graphql',
        config,
        handler
      });
    }

    next();
  }
};

Then users can easily provide authentication and security by providing the auth mode in the config like below without going for the resolve hooks

{
    register: graffiti.hapi,
    options: {
      schema,
      context: {}, // custom context
      config: {
        description: 'graphQL endpoints',
        auth: 'jwt',
      },
    },
  }

Please help if there are any better ways to solve this.

Kind Regards,
Paul

What do the models look like?

Your example is incomplete. You have

app.use(graffiti.express({
  prefix: '/graphql',
  adapter: graffitiMongoose,
  models: [User, Cat]
}));

But don't tell us what User and Cat look like. That's kind of important, isn't it?

Is there a mapping between the models and the database? Or does graffiti expose all DB fields? Is that not a security risk?

How does it handle relationships? Or it doesn't, because it only supports Mongo documents which already hold the relationships?

How do you mutate nested object using graphql?

I have a mongoose schema like the one below:

import mongoose from 'mongoose'

const ProjectSchema = new mongoose.Schema({
  name: {
    type: String
  },
  owner: {
    type: String
  },
  member: {
    type: String
  },
  updatedDate: {
    type: Date
  },
  description: {
    type: String
  },
  folder: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Folder'
  },
  dataSources: [{
    name: {
      type: String
    },
    updatedDate: {
      type: Date
    },
  }],
  propjectHistory: [{
    no: {
      type: Number
    },
    member: {  // is this reference or just a string?
      type: String
    },
    action: {
      type: String
    },
    updatedDate: {
      type: Date
    },
  }]
})

const Project = mongoose.model('Project', ProjectSchema)

And I integrated with graphql using graffiti and graffiti-mongoose. However, the Graphiql documentation shows that I only have the ones below:

addProject(input: addProjectInput!):

name: String
owner: String
member: String
updatedDate: Date
description: String
folder: ID
clientMutationId: String!

I could successfully add project with a mutation query only using those parameters, but it seems that I cannot even send mutation query with projectHistory and dataSource, which are embedded inside project schema.

However, I can access projectHistory and dataSource when I send find queries.

I can't find any documentation about the problem.

regeneratorRuntime is not defined

I'm using babel 6 with the normal 2015 presets.

When I try to make any queries on my schema,

{
  user(id: "563d7ad3a802cba51904958a"){
    email
  }
}

I get this result:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "regeneratorRuntime is not defined"
}

I fixed this using by adding the babel polyfill to my code.

http://babeljs.io/docs/usage/polyfill/

This seems like it should be a dependency of graffiti-- not my application?

Thanks!
Jon

What about GraphiQL?

Can you add GraphiQL in this package to make it convenient to test queries? You can allow it only in development mode for example. It would be very convenient to develop.

Variables are not properly parsed from GraphiQL POST

GraphiQL is sending along the variables field as a string, but graphql is expecting a JSON object. Somewhere along the line, the variables string field needs to get converted to JSON, otherwise GraphQL never receives the variables!

Here is the offending line:
https://github.com/RisingStack/graffiti/blob/master/src/koa/koa.js#L32

Here's an example POST body getting passed over the network:

{
  "query":"mutation M($foo: updateUserInput!) { updateUser(input: $foo) { clientMutationId changedUser { id, gender } } }",
  "variables":"{ \"variables\": { \"foo\": { \"clientMutationId\": \"549b5e7c-0516-4fc9-8944-125401211592\", \"id\": \"VXNlcjo1NmFhYTM4OTZkZjhmMDgxNDM4ODliNzQ=\", \"gender\": \"female\" } } }"
}

The problem is that, by the time we get to the line referenced above, variables is still a string. It needs to be a JSON object. I'm just getting started with this stack, but it seems like you just need to JSON.parse() the variables object there, right? Or is there somewhere else in the stack where it should be getting converted?

Use Graffiti without io.js

Hi i get some trouble to get graffiti started with node

node_modules/@risingstack/graffiti/src/koa/index.js:14
    return function *(next) {
                    ^
SyntaxError: Unexpected token *
    at exports.runInThisContext (vm.js:73:16)
    at Module._compile (module.js:443:25)
    at Module._extensions..js (module.js:478:10)
    at Object.require.extensions.(anonymous function) [as .js] (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/api/register/node.js:214:7)
    at Module.load (module.js:355:32)
    at Function.Module._load (module.js:310:12)
    at Module.require (module.js:365:17)
    at require (module.js:384:17)

Here is my Solution

var graffiti = require('@risingstack/graffiti/src/hapi');
var graffitiMongoose = require('@risingstack/graffiti-mongoose');
var HapiReactViews = require('hapi-react-views');
var Path = require('path');

import User from './api/models/user';
import DbConnector from './api/dbconnector';

let connector = new DbConnector();
var server = new Hapi.Server();

server.connection({
  host: '0.0.0.0',
  port: 8000
});
server.register({
  register: graffiti.create(),
  options: {
    adapter: graffitiMongoose,
    models: [User]
  }
}, {
  routes: {
    prefix: '/graphql'
  }
}, function (err) {
  if (err) {
    console.error('Failed to load plugin graffiti :', err);
  }
});

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.