Build a scalable GraphQL server using TypeScript and Apollo Server

Photo by Brian McGowan on Unsplash

Prerequisite:

1. Initialize a blank project using TypeScript CLI

## Create a folder
mkdir apollo-server
cd apollo-server
## Init npm package
npm init --y
## Install required modules
npm install --save-dev ts-node typescript
# (ts-node) will be use to run typescript code directly## Init typescript config
npx tsc --init
// apollo-server/tsconfig.json{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"outDir": "./lib",
"rootDir": "./src",
"removeComments": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
  "version": "1.0.0",
"description": "",
"main": "lib/index.js",
"scripts": {
"build": "tsc",
"start": "node lib/index.js",
"start:ts": "ts-node src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "xdeepakv",
// apollo-server/src/index.tsconst username = "Deepak"console.log(`Hello ${username}`)
npm run build
npm run start
## Output: Hello Deepak
npm run start:ts## Output: Hello Deepak

2. Creating a custom Apollo Server

npm install apollo-server graphql
// apollo-server/src/index.tsimport { ApolloServer } from "apollo-server";// Take custom port from ENV
const PORT = process.env.PORT || 4000;
// Instance of ApolloServer
const server = new ApolloServer({
typeDefs: ``,
resolvers: {},
});
server.listen(PORT).then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
const typeDefs = `
type User {
firstname: String
lastname: String
age: Int
address: String
}
type Query {
users:[User]
}
`;
const resolvers = {
Query: {
users: () => [],
},
};
import { ApolloServer } from "apollo-server";
const PORT = process.env.PORT || 4000;
// rest of the codeconst server = new ApolloServer({
typeDefs,
resolvers,
});
server.listen(PORT).then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Apollo Server Playground
import { ApolloServer } from "apollo-server-express";
import express from "express";
const app = express();

// Rest of the code
const server = new ApolloServer({
typeDefs,
resolvers,
});
server.applyMiddleware({ app });app.get("health", (_, res) => res.send("OK"));app.listen(PORT, () => {
console.log(`🚀 Server ready at
http://localhost:4000${server.graphqlPath}`);
});

3. Project modularisation

mkdir src/typedefs
touch src/typeDefs/default.ts src/typeDefs/index.ts src/typeDefs/users.ts
// src/typeDefs/default.tsconst { gql } = require("apollo-server-express");export default gql`
type Query {
_: Boolean
}
type Mutation {
_: Boolean
}
`;
// src/typeDefs/users.tsimport { gql } from "apollo-server-express";const typeDefs = gql`
type User {
firstname: String
lastname: String
age: Int
address: String
}
extend type Query {
users: [User]
}
`;
export default typeDefs;
// src/typeDefs/index.tsimport users from "./users";
import defaultSchema from "./default";
export default [defaultSchema, users];
// src/index.tsimport { ApolloServer } from "apollo-server-express";
import express from "express";
import typeDefs from "./typeDefs";// Rest of the codeconst server = new ApolloServer({
typeDefs,
resolvers,
});
// Rest of the codeapp.listen(PORT, () => {
console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`);
});
## create resolvers folder and some couple of filesmkdir src/resolvers
touch src/resolvers/index.ts src/resolvers/users.ts
// src/resolvers/index.tsimport users from "./users";export default [users];// src/resolvers/users.tsconst resolvers = {
Query: {
users: () => [
{
firstname: "deepak",
lastname: "vishwakarma",
},
{
firstname: "deepak",
lastname: "sharma",
},
],
},
};
export default resolvers;
import { ApolloServer } from "apollo-server-express";
import express from "express";
import typeDefs from "./typeDefs";
import resolvers from "./resolvers";
// Rest of the codeconst server = new ApolloServer({
typeDefs,
resolvers,
});
// Rest of the code
curl --location --request POST 'http://localhost:4000/graphql' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"{\n users {\n firstname\n }\n}"}'
## Output:{"data":{"users":[{"firstname":"deepak"},{"firstname":"deepak"}]}}

4. Working with data models(database)

mkdir models
touch src/models/database.ts src/models/index.ts src/models/User.ts
// src/models/database.tsimport { promisify } from "util";
import { RunResult, verbose } from "sqlite3";
const sqlite3 = verbose();
const db = new sqlite3.Database(":memory:");
// Some hacks to convert callback functions to promiseconst all = promisify(db.all).bind(db);const run = async (query: string, args: any[]): Promise<{id?: number}> => {
return new Promise((res, rej) => {
db.run(query, args, function (this: RunResult, err: Error) {
if (err) rej(err);
else res({ id: this.lastID });
});
});
};
// on init create database table usersexport const init = () => {
db.serialize(() => {
db.run(`CREATE TABLE users (
firstname TEXT NOT NULL,
lastname TEXT,
age INTEGER NOT NULL,
address TEXT
)`);
});
};
export { all, run };
export default db;
// src/models/User.tsimport { all, run } from "./database";interface User {
firstname: string;
lastname: string;
age: number;
address?: string;
}
const TABLE_NAME = "users";
const createUser = async (user: User) => {
return await run(
`INSERT INTO ${TABLE_NAME} (firstname,lastname,age) VALUES(?,?,?)`,
[user.firstname, user.lastname, user.age]
);
};
const getUsers = async () => {
return await all(
`SELECT rowid as id,firstname,lastname,age FROM ${TABLE_NAME}`
);
};
export default { createUser , getUsers };
// src/models/index.tsimport User from "./User";
export interface Models {
models: {
User: typeof User;
};
}
export default { User };

5. Uses Models in Resolvers

import typeDefs from "./typeDefs";
import resolvers from "./resolvers";
// Import database and init database instance.
import { init } from "./models/database";
init();
// Import models
import models from "./models/";
const app = express();const PORT = process.env.PORT || 4000;// Pass the context
const server = new ApolloServer({
typeDefs,
resolvers,
context: () => ({
models,
}),

});
// Rest of the code.
// src/resolvers/users.tsimport { Models } from "../models/";const resolvers = {
Query: {
users: async (_: any, __: any, { models }: Models) => {
return await models.User.getUsers();
},
}
};
export default resolvers;

6. Adding User creation Mutation

// src/typeDefs/users.tsimport { gql } from "apollo-server-express";const typeDefs = gql`
type User {
id: String
firstname: String
lastname: String
age: Int
address: String
}
input UserInput {
firstname: String
lastname: String
age: Int
address: String
}

extend type Query {
users: [User]
}
extend type Mutation{
createUser(input: UserInput!): User
}

`;
export default typeDefs;
// src/resolvers/users.tsconst resolvers = {
Query: {
// rest of the code
},
Mutation: {
createUser: async (_: any, { input: user }: any, { models }: Models) => {
const { id } = await models.User.createUser(user);
return { ...user, id };
},
},

};
export default resolvers;

--

--

--

I am a polyglot Blockchain Developer. Mainly work on JavaScript and Nodejs. I work for a multinational Bank as Tech Lead to define the best architecture.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Redux-persist: The Good Parts

12 reasons why ReactJS has taken over the world

Slick Slider

Using IIFE in JavaScript and creating your own Each function

Rubik_Tesselation

Reverse Words in a String

“ Top 7 Things Must Know About JavaScript ”

Translating API responses into type-safe interfaces with TypeScript

Don’t be afraid and just `ng update`

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Deepak Vishwakarma

Deepak Vishwakarma

I am a polyglot Blockchain Developer. Mainly work on JavaScript and Nodejs. I work for a multinational Bank as Tech Lead to define the best architecture.

More from Medium

Authentification with NestJs and clean architecture

Stable database access in Node.js with TypeScript and io-ts.

Image of stones in very calm waters

5 NestJS Techniques to Build Efficient and Maintainable Apps

Stop using ANY in typescript