Getting Started With the Graphcool Framework

Graphcool, a popular GraphQL backend as a service solution recently moved away from the pure BaaS model and into an open source model instead with the Graphcool framework. Using the Graphcool framework, it’s easier than ever to spin-up and deploy GraphQL backends on either Graphcool’s shared infrastructure, locally in a Docker container, or on something like AWS. Graphcool framework backends are known as services.

In this quick intro we’ll cover how to get started with creating and deploying a basic GraphQL service to one of Graphcool’s shared clusters.

Note that this post assumes basic knowledge of GraphQL's schema definition language.

Installation

First, you’ll need the Graphcool CLI installed globally on your machine:

$ npm install graphcool -g

# or, using Yarn:
$ yarn global add graphcool

Then, you’ll also want to install GraphQL Playground on your machine for an easy IDE to interact with your GraphQL API.

Getting Started

Initialize a new Graphcool service using the init command. Here we’ll create a simple todo service:

$ graphcool init todo-service

The command creates a base service with the following starting file structure:

├─ types.graphql
├─ src
│  ├─ hello.js
│  └─ hello.graphql
├─ graphcool.yml
└─ package.json

Here’s a quick breakdown of the different files:

  • types.graphql: This is for defining your GraphQL schema, using GraphQL’s schema definition language (SDL).
  • graphcool.yml: Configuration file for your service. This is where permission rules and functions are configured.
  • hello.js & hello.graphql: A sample Resolver function, which can be deleted. Three types of functions are possible using Graphcool: Resolver functions for extending your API, Hook functions for data transformation and validation and Subscription functions to perform tasks when certain events happen with the service (e.g.: a node is created, updated or deleted).
  • package.json: If your service defines functions that depend on any npm package, this is where the dependencies would be added.

Configuration & Setup

Now that you have a basic service in place, let’s slightly change the graphcool.yml and types.graphql files to accomodate our simple needs for a todo app.

First, our schema definition:

types.graphql

type User @model {
  id: ID! @isUnique
  createdAt: DateTime!
  updatedAt: DateTime!

  name: String

  todos: [Todo!]! @relation(name: "UserTodos")
}

type Todo @model {
  id: ID! @isUnique
  createdAt: DateTime!
  updatedAt: DateTime!

  title: String!
  description: String!
  completed: Boolean! @defaultValue(value: false)

  owner: User! @relation(name: "UserTodos")
}

We only have two types for our service: User and Todo. We also added a one to many relation between a user and todo nodes called UserTodos. A user can have many todos, and todos have exactly one owner.

The id, createdAt and updatedAt fields are read-only and are managed by Graphcool. The createdAt and updatedAt fields are optional.


Then, our service definition:

graphcool.yml

types: ./types.graphql

permissions:
  - operation: "User.create"
  - operation: "User.read"
  - operation: "Todo.create"
    authenticated: true
  - operation: UserTodos.connect
    authenticated: true
  - operation: "Todo.read"
    authenticated: true
    query: ./permissions/todo.graphql
  - operation: "Todo.update"
    authenticated: true
    query: ./permissions/todo.graphql
  - operation: "Todo.delete"
    authenticated: true
    query: ./permissions/todo.graphql

First our service definition references our schema definition, and then we set a few permission rules our Todo and User fields.

Notice how the permissions for Todo nodes also reference a query file. That file should be a GraphQL query that returns a boolean to indicate if the operation should be permitted or not. Here’s a sample of such query:

./permissions/todo.graphql

query ($node_id: ID!, $user_id: ID!) {
  SomeTodoExists(
    filter: {
      id: $node_id,
      owner: {
        id: $user_id
      }
    }
  )
}

The query gets the user id for the authenticated user as well as the node id for the node that’s trying to access the permission. We can query using a filter that the node in question exists and that its owner is the currently authenticated user.

Also, you’ll notice that we have to explicitly define a permission for our relation (UserTodos.connect). Without such permission, a todo wouldn’t be permitted to be associated with its owner.

Authentication

We’ve setup permissions for Todo nodes that require a user to be authenticated, but we haven’t added any form of authentication yet. This can be done easily using a pre-defined template provided by Graphcool. Here’s we’ll demonstrate how to setup the email-password auth template.

Using the Graphcool CLI, add the template to your service using the add-template command:

$ graphcool add-template graphcool/templates/auth/email-password

This will add a bunch of files in a src folder for 3 resolver functions: signup, authenticate and loggedInUser. It’ll also add new dependencies to the service’s package.json file. You don’t need to modify these files.

At this point, you can run npm install or yarn install to ensure that the dependencies are installed locally.

The add-template command also added the function definition to graphcool.yml and a User type in types.graphql, but these additions are commented, as to not interfere with your current definitions. You’ll need to uncomment the additions in graphcool.yml and merge the additions to the User type.

This is what our service definition file now looks like:

graphcool.yml

types: ./types.graphql

functions:
  signup:
    type: resolver
    schema: src/email-password/signup.graphql
    handler:
      code: src/email-password/signup.ts

  authenticate:
    type: resolver
    schema: src/email-password/authenticate.graphql
    handler:
      code: src/email-password/authenticate.ts

  loggedInUser:
    type: resolver
    schema: src/email-password/loggedInUser.graphql
    handler:
      code: src/email-password/loggedInUser.ts

permissions:
  # ...our permissions, same as before

And this is what our schema definition now looks like:

types.graphql

type User @model {
  id: ID! @isUnique
  createdAt: DateTime!
  updatedAt: DateTime!

  email: String! @isUnique
  password: String!

  name: String

  todos: [Todo!]! @relation(name: "UserTodos")
}

type Todo @model {
  id: ID! @isUnique
  createdAt: DateTime!
  updatedAt: DateTime!

  title: String!
  description: String!
  completed: Boolean! @defaultValue(value: false)

  owner: User! @relation(name: "UserTodos")
}

The two additions to our schema are the email required and unique field and the password required field.

Deployment

With all of this in place, we’re ready to deploy our simple todo GraphQL service. We can deploy locally to a Docker container or deploy online to a shared cluster. Let’s do the latter here, but keep in mind that you’ll need a user account with Graphcool to deploy to a shared cluster.

Deploying is as simple as running the graphcool deploy command:

$ graphcool deploy

First you’ll be prompted to choose a location for the shared cluster, then a target name, which can be anything you’d like (here we’ll call it dev) and finally a name for your service. You’ll then be prompted to authenticate to your Graphcool account.

After the deployment is successful, you’ll get back the endpoint information for your GraphQL service. To get that information again in the future, you can simply run graphcool info.

A new file .graphcoolrc is also added locally to the project with content that looks like this for your deployment target information:

.graphcoolrc

targets:
  dev: shared-us-west-2/___SERVICE_ID_____
  default: dev

Testing Out Using GraphQL Playground

Now that our service is live, we can test out some CRUD operations using the GraphQL Playground IDE. Simply add the URL for the simple API you got back when deploying as the endpoint URL.

Let’s create a new user in the playground using the following mutation:

mutation {
  createUser(email: "bob@alligator.io", password: "DontSay", name: "Bob") {
    id
  }
}

This will create and user and return its ID. To authenticate as that user and get an authorization token, we can run the authenticateUser mutation:

mutation authenticateUser {
  authenticateUser(email: "bob@alligator.io", password: "DontSay") {
    token
  }
}

In the GraphQL playground you’ll need to add an Authorization HTTP header with the value Bearer followed by a space and the token received back from the user authentication mutation. To test that the authorization header is working, you can run the following query, which should return the user id:

query isLoggedIn {
  loggedInUser {
    id
  }
}

Now that you’ve created a user and authenticated, we can create todo items. Here for example we’re create 2 todo nodes:

mutation createTodo1 {
  createTodo(
    title: "Buy milk",
    description: "whole milk please",
    ownerId: "cja4dk2id82vy0155jhqlhowv" # the user id
  ) {
    completed
    description
    title
  }
}

mutation createTodo2 {
  createTodo(
    title: "Repair the shed",
    description: "arrg,
    I need to get my tools",
    ownerId: "cja4dk2id82vy0155jhqlhowv",
    completed: true
  ) {
    completed
    description
    title
  }

And finally, we can run the following query to get all the todo items:

query getTodos {
  allTodoes {
    description
    title
  }
}

That’s it! You now have a working GraphQL backend. You can evolve and add to the schema and simply re-deploy to make changes to your service.

🏓 This introduction barely scratched the surface, so we'll dive peeper in future posts, especially as it relates to building a frontend to interface with our GraphQL service. You can also refer to the official Graphcool docs to learn more.

  Tweet It
✖ Clear

🕵 Search Results

🔎 Searching...