Back

The Graphcool Framework: A path to instant GraphQL greatness

Chris BallWednesday, November 8, 2017
GraphCool framework diagram

Graphcool is a great service. We got a chance to put it through the paces on a recent React Native project, and I was impressed. Graphcool began as a GraphQL Backend-as-a-Service and recently released the (open source!) Graphcool Framework. The Graphcool Framework manages to bring an instant GraphQL backend to your local machine with minimal effort on your part. All you have to do is define the schema.

Running the Graphcool Framework locally enables you to easily manage your project and develop apps the way you’d develop anything else — using your favorite editor and git. When you’re ready to deploy, you can host the API yourself or use Graphcool’s hosted infrastructure.

Let’s take a look and see how easy it is to get going.

Setup

1. Install the graphcool cli: npm install -g graphcool. 2.Initialize a new Graphcool API. For smaller projects, it’s fine to use a subfolder in the main project. For larger projects, or projects that may have many different front-end clients, you’re better off using a separate git repo. In this example, we’re going to keep things separate.

CLI of the setup for graphcool

When it’s done, initialize a new git repo & commit the changes.

cd api

git init && git commit . -m “New Graphcool API"

Running Locally

Running Graphcool locally is optional, but doing so enables you to work anywhere: 🚂 ✈️ ⛺️ ⛱.

  1. Make sure you have Docker installed. Here’s a link for Mac. Other platforms are available under the “Get Docker” menu. If you don’t have Docker yet, you’ll see this when trying to start your local environment:

An example of graphcool local command

Oops, no Docker.

2. Start your local Graphcool service using graphcool local up. This will download a few different Docker images, so make sure you’re not tethering to your phone.

CLI UI of docker being set up

Installing local Graphcool Docker containers

3. Deploy those changes to the container using graphcool deploy. Doing this the first time will prompt for your default target (typically dev).

It will also prompt you for the location to deploy. For staging & production you’ll likely choose one of the “Shared Cluster” options, but we’re going to choose “local”.

If asked for the service name, just keep the default (api in our case).

CLI UI - Naming conventions as you deploy

A successful Graphcool deploy!

Not only did Graphcool Framework configure everything for us, we also have a new User type ready for use. Graphcool uses this schema information to build us a CRUD API automatically.

Testing it out

If you’re not familiar with GraphiQL, it is an in-browser IDE for GraphQL that allows you to inspect your schema and try out queries, mutations, and subscriptions. GraphiQL is built-in to Graphcool (it’s is called the playground internally).

Since GraphiQL knows about the entire schema, you get some pretty great autocomplete features. To open it up, just use graphcool playground.

Let’s make sure we can properly query Users:

GraphQL UI showing a query

And create them:

GraphQL UI of opening a new tab to create a Query

If we go back to our allUsers query, we can see that things have persisted and we’re good to go.

GraphQL UI of receiving a response from all users.

If you want to see detailed information about your schema, just use the green schema tab on the right of the screen.

GraphQL Schema tab opened to show what's available

We get a lot for free!

Custom Functions

Automatic CRUD operations are great, but custom functions are some next level awesomeness. Graphcool has 3 types of custom functions available.

  • Hooks
  • Subscriptions
  • Resolvers

Let’s check our custom resolver function that was added by graphcool init:

GraphQL UI - Custom resolver function example

Testing out the default hello function

Define a custom function

Let’s assume for some reason, we want to give users a default dateOfBirth if one is not provided. We can leverage Graphcool’s operationBefore hook for that.

Add the definition to our graphcool.yml file. We’ll tell Graphcool to run the setBirthDate function before a User is created:

functions: hello: handler: code: src/hello.js type: resolver schema: src/hello.graphql setBirthDate: handler: code: src/setBirthDate.js type: operationBefore operation: User.create

If you want to remove sensitive information from the return payload, there’s a corresponding operationAfter type.

Create the function

  • First, create the function to run. Make sure and name the file using the same path you specified in graphcool.yml.
export default async event => { const data = { ...event.data }; data.dateOfBirth = data.dateOfBirth || new Date(1999, 1, 1); return { data }; };

Deploying our changes

For your local Graphcool server to reflect these changes, just run graphcool deploy again.

Running our createUser mutation again, shows that the user does get a dateOfBirth properly set to party like its 1999.

Graphql UI creating a user with the mutation function to receive Date of Birth

Webhooks

There two ways to handle custom functions using Graphcool. We’ve already used the first, which runs a function local to the server. The other option (which is great for integrating with existing systems) is to use a webhook.

First, we define the webhook in graphcool.yml just like we defined setBirthDate.

For this example, I’ve set up a demo Heroku app that returns some mock data.

functions: hello: handler: code: src/hello.js type: resolver schema: src/hello.graphql setBirthDate: handler: code: src/setBirthDate.js type: operationBefore operation: User.create queryLegacySystem: type: resolver schema: src/queryLegacySystem.graphql handler: webhook: url: https://vast-eyrie-90387.herokuapp.com/ headers: Content-Type: application/json

If you want to set up a local server to test the webhook, here’s the express code we’re using:

const express = require('express') const app = express() app.use(express.json()); const data = [{"id":1,"first_name":"Martainn","last_name":"Moorwood","email":"mmoorwood0@blogger.com","gender":"Male","ip_address":"52.105.79.153","location":{"city":"Cincinnati","state":"Ohio"}}, {"id":2,"first_name":"Shannan","last_name":"Jakubovitch","email":"sjakubovitch1@biglobe.ne.jp","gender":"Male","ip_address":"57.51.60.154","location":{"city":"Charlotte","state":"North Carolina"}}, {"id":3,"first_name":"Alyson","last_name":"Itzkowicz","email":"aitzkowicz2@twitter.com","gender":"Female","ip_address":"210.162.183.75","location":{"city":"Chicago","state":"Illinois"}}, {"id":4,"first_name":"Lalo","last_name":"Ference","email":"lference3@elegantthemes.com","gender":"Male","ip_address":"61.254.152.136","location":{"city":"Reading","state":"Pennsylvania"}}, {"id":5,"first_name":"Creight","last_name":"McGeaney","email":"cmcgeaney4@senate.gov","gender":"Male","ip_address":"121.9.2.0","location":{"city":"Las Vegas","state":"Nevada"}}, {"id":6,"first_name":"Anabel","last_name":"Dutnall","email":"adutnall5@artisteer.com","gender":"Female","ip_address":"174.237.252.181","location":{"city":"Melbourne","state":"Florida"}}, {"id":7,"first_name":"Chicky","last_name":"Rustadge","email":"crustadge6@blog.com","gender":"Male","ip_address":"240.115.81.219","location":{"city":"Merrifield","state":"Virginia"}}, {"id":8,"first_name":"Janette","last_name":"Duval","email":"jduval7@springer.com","gender":"Female","ip_address":"231.173.106.49","location":{"city":"Fairbanks","state":"Alaska"}}, {"id":9,"first_name":"Eran","last_name":"Beddin","email":"ebeddin8@ted.com","gender":"Female","ip_address":"165.190.138.171","location":{"city":"Independence","state":"Missouri"}}, {"id":10,"first_name":"Gilberte","last_name":"Bratcher","email":"gbratcher9@pinterest.com","gender":"Female","ip_address":"250.227.243.236","location":{"city":"Brooklyn","state":"New York"}}, {"id":11,"first_name":"Courtnay","last_name":"Trenoweth","email":"ctrenowetha@bandcamp.com","gender":"Male","ip_address":"35.163.119.198","location":{"city":"Washington","state":"District of Columbia"}}, {"id":12,"first_name":"Kalila","last_name":"Pinock","email":"kpinockb@gravatar.com","gender":"Female","ip_address":"173.250.231.15","location":{"city":"Bronx","state":"New York"}}, {"id":13,"first_name":"Gerrard","last_name":"Meakes","email":"gmeakesc@miitbeian.gov.cn","gender":"Male","ip_address":"253.174.49.32","location":{"city":"Des Moines","state":"Iowa"}}, {"id":14,"first_name":"Kiley","last_name":"Honnan","email":"khonnand@prlog.org","gender":"Male","ip_address":"49.50.71.136","location":{"city":"Chula Vista","state":"California"}}, {"id":15,"first_name":"Susan","last_name":"Fetherston","email":"sfetherstone@prnewswire.com","gender":"Female","ip_address":"186.238.114.230","location":{"city":"Orlando","state":"Florida"}}, {"id":16,"first_name":"Ilysa","last_name":"Hutchinges","email":"ihutchingesf@slideshare.net","gender":"Female","ip_address":"58.121.6.64","location":{"city":"Harrisburg","state":"Pennsylvania"}}, {"id":17,"first_name":"Merrilee","last_name":"Coppledike","email":"mcoppledikeg@google.co.uk","gender":"Female","ip_address":"59.43.136.210","location":{"city":"Springfield","state":"Illinois"}}, {"id":18,"first_name":"Codi","last_name":"Moreinis","email":"cmoreinish@ebay.co.uk","gender":"Female","ip_address":"243.138.168.112","location":{"city":"Harrisburg","state":"Pennsylvania"}}, {"id":19,"first_name":"Gilles","last_name":"Behrendsen","email":"gbehrendseni@simplemachines.org","gender":"Male","ip_address":"168.119.83.215","location":{"city":"Mansfield","state":"Ohio"}}, {"id":20,"first_name":"Hinda","last_name":"Crabbe","email":"hcrabbej@skype.com","gender":"Female","ip_address":"0.84.221.30","location":{"city":"Denver","state":"Colorado"}}] app.post('/', function (req, res) { console.log(req.body) res.send({ data: { users: data }}); }) app.listen(3000, function () { console.log('Example app listening on port 3000!') })

When testing out webhooks of any kind, it’s a good idea to use ngrok to tunnel a publicly accessible https url back to your local machine.

Finally, create the schema file at the path you specified in graphcool.yml above. There’s no need to add a JavaScript file since we’re calling a webhook instead.

type LegacySystemPayload { users: Json } extend type Query { queryLegacySystem(userId: ID!): LegacySystemPayload }

Run graphcool deploy and let’s try it out.

GraphQL UI to show the entire response of users

There’s one slight gotcha to keep in mind. The response from your existing server needs to be wrapped in a top level data key to be recognized by GraphQL.

Staging & Production Environments

We’re off and running with a dev environment, but what about staging and production? Turns out that’s easy! Just use the --target (or -t) option available to most cli commands.

Creating a new staging environment

There’s a Graphcool in your CI!

In a follow up article, we’ll take a look at how to run Graphcool inside a CI service to enable true E2E testing of a React Native app.

The good news for now is that syncing up Graphcool staging deploys with your CI process is pretty painless. It works like this:

  1. npm install -g graphcool
  2. run tests
  3. graphcool deploy -t staging

Bonus

By the way, if you have an error in your functions along the way, you get wonderful little error messages when trying to deploy. This combined with graphcool logs makes debugging issues pretty painless.

CLI to deploy to AWS

Nice, informative error messages

Recommended links for additional reading:

graphcool-lib examples
Permission rules examples
Wrapping an existing REST API
Graphcool Reference Docs

Update 11/17/17: Removed setBirthDate.graphql as it’s not required for operationBefore hooks.

Share this post

twitterfacebooklinkedin

Related Posts:

Interested in working with us?

Give us some details about your project, and our team will be in touch with how we can help.

Get in Touch