Trace Node.js Monitoring Start my free trial!

hapi

Start using GraphQL with Graffiti

Update: we've released a Mongoose adapter for Graffiti. Here's how to get started with it.

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

Graffiti grabs your existing models, transforms them into a GraphQL schema and exposes it over HTTP.

Get Graffiti

Why we made Graffiti for GraphQL

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.

Use cases of Graffiti

There are a couple of areas where Graffiti is extremely useful:

For existing applications

If you are already running an HTTP REST API and uses an ORM then with adding a couple of lines of code you can expose a GraphQL endpoint.

For new applications

Graffiti comes to the rescue when you just about to start developing a new backend for your endpoint consumers - the only thing you have to define is your models using one of the supported ORMs.

Setup Graffiti

Adding to Graffiti to your project is as easy as:

import express from 'express';  
import graffiti from [email protected]/graffiti';  
import {getSchema} from [email protected]/graffiti-mongoose';  
import mongooseSchema from './schema';

const app = express();  
app.use(graffiti.express({  
  schema: getSchema(mongooseSchema)
}));

For complete, working examples check out our Graffiti examples folder.

You can play with a running Relay application using Graffiti here. Navigate to /graphql to explore the schema with GraphiQL.

You can use a GraphQL schema generated by an adapter or your own GraphQLSchema instance with Graffiti.

GraphQL over Websocket / mqtt

With Graffiti you are not limited to HTTP - with the adapters you can easily expose the GraphQL interface over any transport protocol.

Roadmap

We have a fully functional Graffiti adapter for Mongoose and we have plans to support other ORMs too. Also, please note, that some of the following items depend only on the adapters, and not on the main project itself.

  • Query support (done)
  • Mutation support (done)
  • Yeoman generator (planned)
  • Relay support (done)
  • Adapters
    • for MongoDB: graffiti-mongoose (done)
    • for RethinkDB: graffiti-thinky (in progress)
    • for SQL: graffiti-bookshelf (in progress)

Contributing

If you are interested in contributing, just say hi in the main Graffiti repository.

Hapi on Steroids - Using Generator Functions with Hapi

You may be familiar with our Koa tutorial series: we took a look on how generator functions work and how you can do a basic application using Koa.

Also, we are huge fans of hapi - if you haven't seen our hapi node.js tutorial, now is the time!

So the question came: wouldn't it be great if we could use generator functions with hapi?

The Motivation

Using error-first callbacks fetching users from your database may look like something like this:

Users.find({  
    age: 32
}, function (err, users) {
    if (err) {
        request.server.log([
          'error',
          'users',
          err
        ]);
        return reply(boom.badImplementation());
    }

    return reply(users);
});

The same logic with hapi on steroids looks like this:

var users;

try {  
  users = yield Users.find({age: 32})    
} catch (exception) {
    request.server.log([
      'error',
      'users',
      exception
    ]);
    return reply(boom.badImplementation());
}

return reply(users);  

How to Use Generators With Hapi

In short: we wrapped all of the route handlers with co in hapi. It is just a thin layer, but it enables you to write all your underlying logic using generators.

var co = require('co');

function get (request, reply) {  
  co(function* () {
    return yield db.getUsers();
  })
    .then(function (response) {
      reply(response);
    })
    .catch(function (err) {
      request.server.log([
        'error',
        'login',
        err
      ]);
      return reply(boom.badImplementation());
    });
}

module.exports.get = get;  

Tests

You may ask: okay, okay, but how to test it, I am used to hapi.inject? Gladly the solution is pretty easy. If you are using co-mocha you can do something like this in your before hook:

before(function () {  
  var _this = this;

  _this.server = yield server.create();

  this.inject = function (options) {
    return new Promise(function (resolve) {
      _this.server.inject(options, function (res) {
        return resolve(res);
      });
    });
  };
});

With this solution, a test for an endpoint will become this:

describe('GET /v1/info', function () {  
  it('should return with a proper status code and body', function* () {
    var res = yield this.inject({
      method: 'GET',
      url: '/v1/info'
    });

    expect(res.result).to.eql({
      status: 'ok'
    });
  });
});

The future

It is great to see more and more ES6 features landing in either Node.js or io.js. With tools like Babel one can use an either bigger set of language features.

We are curious to hear your stories on how you use ES6 in production today, what problems you are facing - don't hesitate to comment! :)

Getting Started with Hapi 8

hapi framework

A rich framework for building applications and services

hapi enables developers to focus on writing reusable application logic instead of spending time building infrastructure.

hapi is currectly being used by companies like Walmart (not just used but actively developed and maintained), Yahoo, PayPal or Mozilla - even the new npmjs website is built using it.

Starting your first server

Before dive into it, make sure that your are using version 8 of hapi, as some API calls are different in previous versions.

Installing hapi

You can grab the latest version of hapi from NPM:

npm install hapi --save  

Launch the hapi-ness

Let's take a closer look on what is happening here!

  • First, we create a new hapi instance.
  • Then our connection is defined - it is important, because in hapi you can define more, not just one. It can be useful when you want to create an API and a Web app as well in the same project, one listening on port 8001, the other one on port 8002.
  • The third step was to define a route handler. A route handler has a request and a reply argument, the first one contains information on the incoming request, while with the reply we can tell hapi how to respond to them.
  • Lastly, we start our server with server.start

Diving deeper

To be able to use hapi's full power we have to understand some of the key features/mechanisms of hapi:

  • lifecycle of a request
  • plugins
  • server methods

Lifecycle of a request

hapi enables us a very granular control over incoming requests. The following happens to an incoming request:

Hapi request lifecycle

We can modify each request at the extension points, using server.ext(). Let's take a look at an example when we want to set a new request path values on all the incoming requests:

Plugins

In hapi plugins make it really easy to break your application up into isolated small applications with seperate business logic. Ideally all your application code goes to plugins, your server should only provide configuration to them.

Writing a plugin

Writing plugins is very simple - you only have to implement a register function with the following signature: function (server, options, next):

Register takes three arguments: server, options and next. server is a reference to our server instance, so here we can add route handlers as well, or access server methods that will be discussed later on. The options is a plain JavaScript object that the user passes to the plugin. After all done configuration is done we have to call next to signal hapi once we have finished registering our plugin - this can be useful also if you do some async operation in your application's bootstrapping phase.

Load plugings

To be able to use the previously created plugin we only have to load it and start the server:

Easy, huh? :)

If you want to register more plugins at the same time, then you can pass an array of plugins to the server.register.

Configuration

If you want to pass a configuration to the server and want to access it in every plugin you can do the following when creating a hapi server:

After that this configuration object will be accessible on the server object using server.settings.app - yes, even in your plugins!

Server methods

Server methods can be used to share functions by attaching them to the server instance. You can add a server methods like this:

Later on this function can be accessed on the server object using server.methods.twitter.fetch - again, in your plugins as well, as you can register server methods in plugins too!

Next

The features and mechanisms covered in this post hopefully get you interested in start using hapi.

For a complete API reference check out the GitHub repository.

Check out our guide: learn how to use generator functions with Hapi.