Maira Bello
Written By
Maira Bello
Developer @ VTEX

Maira is a software engineer at VTEX, currently working with GraphQL every day. She loves web development and learning new technologies that make it even more enjoyable.

Getting Started

Defining the application’s GraphQL API

You’ll start by defining the GraphQL schema for your application. We’ll also refer to this as your application schema. Schemas are written in the GraphQL Schema Definition Language (SDL).

If you want to learn more about the GraphQL schema and its role in GraphQL servers, make sure to check out this excellent blog post which provides a comprehensive overview of everything you need to know regarding GraphQL schemas!

API requirements

Here you’re going to build the backend for a Hackernews clone, so let’s think about the requirements your API should provide:

  • Retrieve a list (feed) of link elements
  • Allow users to signup up with their name, email and password
  • Users who signed up should be able to login again with their email and password
  • Allow authenticated users to post new link elements
  • Allow authenticated users to upvote an existing link element
  • Send realtime updates to subscribed clients when a new link element is created
  • Send realtime updates to subscribed clients when an existing link element is upvoted

Bonus: There’s a super useful GraphQL Cheat Sheet for building schemas. Check it out!

Defining the application schema

It’s now the responsility of the application schema to define an API that allows for client applications to perform the operations defined above. So, you need to translate the requirements into corresponding GraphQL queries, mutations and subscriptions.

As you learned in the Core Concepts chapter, you can do so by writing the Query, Mutation and Subscription types (which are also called root types) in your schema.

Here is an application schema that caters the requirements defined above. For now, we don’t care where the User, Link and Vote types are coming from and how exactly they’re defined.

type Query {
  # Retrieve a list ("feed") of link elements
  feed(filter: String, skip: Int, first: Int): [Link!]!
}

type Mutation {
  # Allow users to signup up with their name, email and password
  signup(name: String!, email: String!, password: String!): AuthPayload!
  # Users who signed up should be able to login again with their email and password
  login(email: String!, password: String!): AuthPayload!
  # Allow authenticated users to post new link elements
  post(url: String!, description: String!): Link
  # Allow authenticated users to upvote an existing link element
  vote(linkId: ID!): Vote
}

type Subscription {
  # Send realtime updates to subscribed clients when a new link element is created
  newLink: LinkSubscriptionPayload
  # Send realtime updates to subscribed clients when an existing link element is upvoted
  newVote: VoteSubscriptionPayload
}

type AuthPayload {
  token: String
  user: User
}

Great! So this is the final application schema you want to have implemented in the end. Notice that the feed query allows to send filter and pagination (skip and first) arguments to constrain the list of link elements to be returned by the server.

In the following, you’ll gradually implement the defined queries, mutations and subscriptions one by one. The implementation process will look somewhat similar every time - this is also referred to as schema-driven development:

  1. Adjust the data model of your Prisma database service
  2. Deploy the Prisma database service to apply your changes
  3. Extend your application schema with a new root field
  4. Implement the resolver for the root field by delegating the execution to the corresponding Prisma resolver

Bootstrap your GraphQL server

It’s time to start creating your GraphQL server. You could do so by starting from scratch, use npm init -y to setup your package.json and manually add the required dependencies (such as graphql-yoga). However, in this tutorial you’ll use graphql create, a feature of the graphql-cli which will bootstrap your GraphQL server and give you a head start (think of it like create-react-app but for GraphQL servers instead of React applications).

The first step for you is to install the graphql-cli so you can make use of the graphql create command.

Note: For the purpose of this tutorial you don’t explicitly have to install the Prisma CLI because prisma-cli is listed as a development dependency in the boilerplate you’ll use. This allows to run its commands by prefixing it with yarn, e.g. yarn prisma deploy or yarn prisma playground. If you have prisma installed globally on your machine (which you can do with npm install -g prisma), you don’t need to use the yarn prefix throughout this tutorial.

With the CLI installed, you can now use the graphql create command to setup your GraphQL server. Note that this command is based on several GraphQL boilerplate projects that provide an initial set of features for various languages and technologies.

This now created a new directory called hackernews-node based on the node-basic GraphQL boilerplate project. The relevant contents of this directory are as follows:

.
├── package.json
├── src
│   ├── index.js
│   ├── schema.graphql
│   └── generated
│       └── prisma.graphql
└── database
    ├── prisma.yml
    └── datamodel.graphql

Here’s an overview of what the directories and files are used for:

  • src: This directory generally contains the JavaScript code for your application. It also holds the application schema (which is defined in src/schema.graphql) as well as the auto-generated Prisma schema (in src/generated/prisma.graphql) which we’ll discuss in a bit.
  • database: This directory stores everything related to your Prisma database service. This includes the root configuration file prisma.yml and the definitions of your application’s data model (in one or split across multiple files).

    • prisma.yml: This is the root configuration file for your “Prisma database” service. In here, you specify a name for your service, deployment information and your data model which will be used to generate the Prisma CRUD API.
    • datamodel.graphql: The data model is written in GraphQL SDL and provides the foundation for your database: The Prisma API defined in src/generated/prisma.graphql provides CRUD functionality which allows to easily create, read, update and delete instances of the types in your data model (e.g. a Link or a User type).

Background: Application schema vs Prisma schema (CRUD)

As you might have noticed, there are three different .graphql-files with certain SDL type definitions in your project. Let’s take a moment to understand where they’re coming from and what they’re used for in your setup:

  • src/schema.graphql (Application schema): Defines the GraphQL API that will be exposed to your client applications. For example, we’ll use it later on to expose the feed query our client apps can access to retrieve a list of Hackernews links from our server.
  • src/generated/prisma.graphql (Prisma schema): Defines the Prisma API with CRUD functionality for your database. This file is auto-generated based on your data model and should never be manually altered. This also means that whenever you make changes to your data model, this file is (automatically) updated as well.
  • database/datamodel.graphql: The data model contains the type definitions for the entities in our application domain. For each type in the data model, Prisma will generate queries and mutations allowing you to read and write database records (also called nodes) for that type.

Notice that datamodel.graphql is not actually a GraphQL schema in the actual sense, as its missing the root types. The data model is only used as the foundation to generate the actual schema for the Prisma API!

Why do you need two GraphQL schemas at all? The reason for that is simple, the Prisma schema alone would give clients read/write-access to all the data in your database. For the vast majority of applications however, you’ll rather want to expose an API that’s more tailored to the requirements of your clients. Plus, if you were to use only the Prisma schema, you wouldn’t be able to implement any authentication workflows or other business logic.

Understanding the initial setup

Before we continue with the implementation, let’s go and understand the initial setup of your GraphQL server.

If you check your package.json, you’ll notice that the boilerplate comes with two dependencies:

  • graphql-yoga: The package that contains everything you need for your GraphQL server (basically a thin convenience wrapper on top of Express.js, apollo-server, graphql-tools and more)
  • prisma-binding: This package allows to bind the resolvers of your application schema to the auto-generated resolvers of your Prisma database service. To learn more about schema bindings, you can read this article.

Both dependencies are used in index.js. The important part of that file is this:

.../hackernews-node/src/index.js
const server = new GraphQLServer({
  typeDefs: './src/schema.graphql',
  resolvers,
  context: req => ({
    ...req,
    db: new Prisma({
      typeDefs: 'src/generated/database.graphql',
      endpoint: 'http://localhost:60000/hackernews-node/dev',
      secret: 'mysecret123',
    }),
  }),
})

Here you instantiate your GraphQLServer with the following arguments:

  • typeDefs: These are the type definitions from your application schema imported from src/schema.graphql.
  • resolvers: This is a JavaScript object that mirrors the Query, Mutation and Subscription types and their fields from your application schema. Each field in the application schema is represented by a function with the same name in that object.
  • context: This is an object that get’s passed through the resolver chain and every resolvers can read from or write to.

Notice that the context object has the db field which contains an instance of Prisma from the prisma-binding package. This instance will allow your resolvers to simply delegate the execution of an incoming request to an appropriate resolver from the Prisma API.

When instantiating Prisma, you need to provide information about your Prisma database service:

  • typeDefs: The type definition from your Prisma schema
  • endpoint: The HTTP endpoint of your Prisma database service
  • secret: The secret which allows to access the Prisma database service (this is defined in prisma.yml)

Because you provide this information, the Prisma instance will get full access to your database service and can be used to resolve incoming request later on.

Unlock the next chapter
Which of the following files does not define a "proper" GraphQL schema.
src/schema.graphql
database/datamodel.graphql
src/generated/prisma.grahpql
Trick question: All of them do