Creating graphql server and first resolver with Apollo
In this story, I’ll share how I’ve implemented my first graphql server with an apollo-server.
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.
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.
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
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.
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.
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 toCRUD
actual databases which is fromSequelize
.pubsub
is used for graphql-subscription which is using websocket.appSecret
is ourJWT
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
.
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).
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.
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'
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;
}
5. Implement resolvers
I’ll implement signUp
mutation resolver.
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.