Creating graphql server and first resolver with Apollo

Hyo
dooboolab
Published in
5 min readFeb 1, 2020

In this story, I’ll share how I’ve implemented my first graphql server with an apollo-server.

Figure 1: This is how it looks like in graphql playground looks like.

The stacks I’ve chosen to implement our server are as follows.

If you are new to implementing graphql, I’d recommend you to understand the concepts of server programming in the first hand because they should be preceded in order to understand the further concepts.

Figure 2: The main package to install

The apollo-server-express provides an easy way to implement graphql resolvers over express framework. This means that you can use everything that’s in express itself. I will show you how to implement resolver on top of it.

As I’ve noted above, I’ve decided to use sequelize as our database orm. If you have ever tried Prisma, it is like a photon introduced in Prisma2. Since sequelize also supports similar functionality, we can replace this in our graphql server.

Figure 3: Example for ORM (Image by https://mixedcode.com/Article/Index?aidx=1076)

The first step I’d recommend you to confirm yourself before diving into building your sever is confirming your database schemas. Although the graphql focuses on SDL, I believe that SDL is also affected by the well structured relational database because they are working internally.

1. Create database models

Figure4: Our example database tables
Figure5: Our example for relation database model

We designed our RDBMS. Then we’ll configure sequelize orm. By following getting started guide from sequelize, we’ll create our models and sync with the actual database.

Figure6: User model in sequelize

The model file for User is quite long here since we’ve also support typescript. Currently, Sequelize typescript support isn’t as nice as it looks. We’ll also create Notification model and Post model.

Figure7: Notification model in sequelize
Figure8: Post model in sequelize

After that, we’ll create ./db/index.ts file and sync our model with the actual database.

import { Sequelize } from 'sequelize';
import config from '../../config/config';
const sequelize = new Sequelize(
config.database,
config.username,
config.password,
config,
);
if (process.env.NODE_ENV !== 'test') {
sequelize.sync();
}
export default sequelize;

The config file looks like below which is in ./config/config.js.

const config = {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
dialect: process.env.DB_CONNECTOR,
};

We are using config file as pure javascript file since this file is used for Sequelize migration which is similar to Lift in Prisma2. If you change config.js to config.ts, Sequelize migration command won’t find file to run its commandment.

Sequelize supports 5 mostly used RDB which are Postgres, MySQL, MariaDB, SQLite and Microsoft SQL. We are using Mysql in this example.

2. Setup Apollo Server

I’ll explain above code by lines.

In 17~33, I wrote functions that verify JWT token which will check user authentication.

In 35~73, I initialize Apollo Server. In 38~41, we pass down custom middlewares through the context.

getUser: () => Promise<User>;
models: ModelType;
pubsub: PubSub;
appSecret: string;
  • getUser is used to validate later in resolvers if the user has signed in.
  • models is used to CRUD actual databases which is from Sequelize.
  • pubsub is used for graphql-subscription which is using websocket.
  • appSecret is our JWT secret.

In the next lines of code, we initialize the apollo server with subscriptions together.

3. Make schemas with graphql SDL

Lets defined our schemas in ./schemas.

Figure9: Schema files structure

The root of the schema files is schema.graphql and here we define all the resolvers for Query, Mutation and Subscription.

# import User from "user.graphql"
# import Notification from "notification.graphql"
# import Post from "post.graphql"
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
notifications: [Notification!]!
}
type Mutation {
signInGoogle(socialUser: SocialUserInput!): AuthPayload!
signInFacebook(socialUser: SocialUserInput!): AuthPayload!
signInApple(socialUser: SocialUserInput!): AuthPayload!
signUp(user: UserInput!): AuthPayload!
addNotificationToken(notification: NotificationInput!): Notification
updateProfile(user: UserInput!): User
}
type Subscription {
userAdded: User
userUpdated: User
}

Next is our frustrating part which we have to define our SDL for our Sequelize model (Yes we have to do things twice).

Figure10: Simple look on redefining the Sequelize model to User schema.

If you are using Primsa, you don’t have to do things twice but since we are still waiting for stable Prisma2, we are currently using it in a conservative way. This approach will make you understand how other graphql ORM like Prisma works.

If you look at Figure8 more closely, you can see that model relations are added as an array.

Figure11: Example of querying relation with graphql

It is because as you can see in Figure11, graphql fetches associated models as shown above. We make that logic later in resolvers.

4. Setup and run codegen

After you’ve defined all the schemas, you should generate codes that translate SDL into your current programming language. Since we are using typescript, we will use typescript plugins.

Let’s create codegen.yml file as below. This is provided via graphql-codegen 🎉

overwrite: true
schema: './schemas/schema.graphql'
documents: null
generates:
src/generated/graphql.ts:
config:
contextType: ../context#MyContext
plugins:
- 'typescript'
- 'typescript-resolvers'
Figure12: Result after running `graphql-codegen`
Figure13: `graphql.ts` is generated

The contextType defined in codegen.yml is a typedef for context that is passed down to context in server.ts. We defined this in .src/context as below.

import { ModelType } from './models';
import { PubSub } from 'graphql-subscriptions';
import { User } from './models/User';
export interface MyContext {
getUser: () => Promise<User>;
models: ModelType;
pubsub: PubSub;
appSecret: string;
}
Figure14: The typescript will throw an error if the resolver isn’t defined.

5. Implement resolvers

Figure15: Folder structure for resolvers

I’ll implement signUp mutation resolver.

Figure16: Implementation for `signUp` mutation
Figure17: `signUp` resolver result

We’ve successfully built our first resolver 🎉

I’ll continue this post on how to add more resolvers and query associations effectively.

This series of posts is referred from our boilerplate project ts-apollo-sequelize.

--

--