Using GraphQL with Parse Server

June 20, 2017

Today we will see how to use GraphQL with Parse Server. For anyone unfamiliar with it, GraphQL is a query language specification that was initiated by Facebook. Because GraphQL is not a database query language but a specification for that language, we will be able to use it with Parse.

In this article, we will use Docker and assume that you already know the basics of GraphQL. If not, start by reading the introduction to GraphQL.

Configuration

We will start by configuring our Docker environment. Create a parse-server-graphql folder and clone the Parse Server Example repository:

mkdir parse-server-graphql && cd parse-server-graphql
git clone https://github.com/ParsePlatform/parse-server-example parse-server

Then create a dashboard folder and add the following Dockerfile:

FROM node:6
 
ENV PARSE_DASHBOARD_VERSION 1.0.25
RUN npm install -g parse-dashboard@${PARSE_DASHBOARD_VERSION}
ENV PORT 4040
EXPOSE $PORT
 
CMD ["parse-dashboard"]

Finally, go back to the root of the parse-server-graphql folder and add the following docker-compose.yml file:

version: '2'
services:
  mongo:
    image: mongo:3
    container_name: 'mongo-db'
    volumes:
      - ./mongo/data:/db/data
    ports:
      - '27017:27017'
  api:
    build: ./parse-server
    container_name: 'parse-server'
    ports:
      - '1337:1337'
    environment:
      PORT: 1337
      DATABASE_URI: mongodb://mongo:27017
      APP_ID: 'parse-graphql'
      MASTER_KEY: 'master'
      PARSE_MOUNT: '/parse'
      SERVER_URL: 'http://192.168.99.100:1337/parse'
    volumes:
      - ./parse-server/cloud:/parse/cloud
      - ./parse-server/public:/parse/public
      - ./parse-server/graphql:/parse/graphql
    depends_on:
      - mongo
  dashboard:
    build: ./dashboard
    container_name: 'parse-dashboard'
    environment:
      PORT: 4040
      PARSE_DASHBOARD_ALLOW_INSECURE_HTTP: 'True'
      PARSE_DASHBOARD_SERVER_URL: 'http://192.168.99.100:1337/parse'
      PARSE_DASHBOARD_MASTER_KEY: 'master'
      PARSE_DASHBOARD_APP_ID: 'parse-graphql'
      PARSE_DASHBOARD_APP_NAME: 'GraphQL with Parse Server'
      PARSE_DASHBOARD_USER_ID: 'user'
      PARSE_DASHBOARD_USER_PASSWORD: 'password'
    ports:
      - '4040:4040'

Schema

Now that our Docker environment is configured, we will move on to creating the GraphQL schema.

In the parse-server folder, create a new folder named graphql and add the schema.js file inside it:

mkdir graphql && cd graphql
touch schema.js

The first thing we will do is import the different types used by our schema, as well as Parse.

import {
  GraphQLBoolean,
  GraphQLFloat,
  GraphQLID,
  GraphQLInt,
  GraphQLList,
  GraphQLNonNull,
  GraphQLObjectType,
  GraphQLSchema,
  GraphQLString,
} from 'graphql'
import Parse from 'parse/node'

Type

In this example, we will declare a userType that represents the User object from Parse Server.

const userType = new GraphQLObjectType({
  name: 'User',
  description: 'A simple user',
  fields: () => ({
    id: {
      type: GraphQLID,
      resolve: obj => obj.id,
    },
    username: {
      type: GraphQLString,
      description: 'The username of the user.',
      resolve: obj => obj.get('username'),
    },
    emailVerified: {
      type: GraphQLBoolean,
      description: 'Define if the user has validate his email.',
      resolve: obj => obj.get('emailVerified'),
    },
    updatedAt: {
      type: GraphQLString,
      description: 'Last time user update data changes.',
      resolve: obj => obj.get('updatedAt').toString(),
    },
    createdAt: {
      type: GraphQLString,
      description: 'Date when the user was created',
      resolve: obj => obj.get('createdAt').toString(),
    },
  }),
})

Here we defined our userType, which is a GraphQLObjectType. We also defined its name, its description, and all the fields it contains. For each field, we also define the type, the description, and most importantly the resolve function.

The resolve function lets us define how to retrieve the value for the field in question. In our case, obj represents a Parse.Object class, so we retrieve the value of its field as follows:

obj.get('field_name')

Query

Now we will define the queries in our schema. We define two queries:

  • user: returns a user based on their ID.
  • users: returns the list of users.
const user = {
  type: userType,
  args: {
    id: {
      description: 'The id of the user',
      type: new GraphQLNonNull(GraphQLString),
    },
  },
  resolve: (root, { id }) => {
    return new Parse.Query(Parse.User).equalTo('objectId', id).first()
  },
}
 
const users = {
  type: new GraphQLList(userType),
  resolve: root => {
    return new Parse.Query(Parse.User).find()
  },
}

For each query, we define the result type returned by the query:

  • user : userType
  • users : GraphQLList(userType)

Then, for the user query, we define a required parameter: the user's ID.

Finally, as with the fields of userType, we define the resolve function. Here, we simply return the result of a Parse.Query.

Mutation

To be able to add new users, we will define a mutation:

const createUser = {
  type: userType,
  description: 'Create a new user',
  args: {
    username: {
      type: GraphQLString,
      description: 'The username of the user.',
    },
    email: {
      type: GraphQLString,
      description: 'The email adress of the user.',
    },
    password: {
      type: GraphQLString,
      description: 'The password of the user.',
    },
  },
  resolve: (value, { username, email, password }) => {
    var user = new Parse.User()
    user.set('username', username)
    user.set('password', password)
    user.set('email', email)
    return user.signUp()
  },
}

In the same way as for a query, we define the type, then the different parameters required to create the user. Finally, we define the resolve function. Parse lets us add a new user as follows:

  • Declare a new User object.
  • Define the attribute values.
  • Execute the signUp function.

To finish, we return the user.signUp result.

Defining the schema

Before creating the schema, we will add two final types that will be the root of our query and the entry points into our schema. We define queryType, whose fields will be each query we defined earlier, and mutationType, whose fields will be the mutation we created.

var queryType = new GraphQLObjectType({
  name: 'queries',
  description: 'all qqueries',
  fields: () => ({
    // Queries goes here
    user,
    users,
  }),
})
var mutationType = new GraphQLObjectType({
  name: 'mutation',
  description: 'all mutations',
  fields: () => ({
    // Mutations goes here
    createUser,
  }),
})

Finally, we define the schema.

export default new GraphQLSchema({
  query: queryType,
  mutation: mutationType,
})

Adding GraphQL to Parse

We have defined the schema. Before we can test, all that remains is to add GraphQL to our Parse server.

We will need several additional packages to use GraphQL:

yarn add express-graphql graphql
yarn add babel-cli babel-preset-es2015 --dev

Then, in Parse's index.js file, add the following imports:

import GraphQLHTTP from 'express-graphql'
import schema from './graphql/schema'
import Parse from 'parse/node'

Finally, just before the HTTP server declaration, add the following code:

// Initialize Parse
Parse.initialize(process.env.APP_ID || 'myAppId')
Parse.serverURL = process.env.SERVER_URL || 'http://localhost:1337/parse'
 
// GraphQL
app.use(
  '/graphql',
  GraphQLHTTP(request => {
    return {
      graphiql: true,
      pretty: true,
      schema: schema,
    }
  })
)

First, we initialize Parse, then we declare our GraphQL server on the '/graphql' route. We pass in the schema we defined and enable graphiql.

GraphiQL is an interface that lets you test all of your queries and mutations.

Everything is ready; all that remains is to start Docker:

docker-compose up

Then go to the URL your-docker-machine-ip:1337/graphql.