Apollo GraphQL Server 🚀 With TypeScript
In this article, I am going to show how to build GraphQL express server with apollo server and typescript including unit test setup with jest. This can also use as a starter template to build apollo express server quickly and can be developed further for respective api requirement.
Let's start building this starter template project
Setting up the project
Create a new folder and start a project with package.json
file
$ mkdir typescript-apollo-express-graphql-api
$ cd typescript-apollo-express-graphql-api
$ npm init --yesInstall & Initialize TypeScript
Install Typescript and generate a tsconfig.json file using npx. We will also need nodemon to compile our code on change, and ts-node to exec TypeScript files.
$ npm i typescript nodemon ts-node --save-dev
$ npx tsc --init --rootDir src --outDir dist --lib dom,es6 --module commonjs --removeComments
Setting up Express, Apollo and creating a simple GraphQL API
$ npm i apollo-server-express helmet compression cors express graphql http ncp graphql-tools
$ npm i @types/compression @types/express @types/graphql @types/node --save-dev
Create a /src
directory with a server.ts
file.
$ mkdir src && cd src && touch server.ts
If you are new to GraphQL, here is a link introducing GraphQL.
Creating GraphQL schema and resolvers
First, let’s create our GraphQL Schema. In the /src
folder, create a /service
folder and 1file inside: /serviceSchema.ts
Define schema & query like this:
import { gql } from "apollo-server-express";
export const ServiceTypeDefs = gql`
type User {
name: String
}
type Query {
getAllUsers: [User]
}
`;
Let's create a graphQL resolver in \service
folder in \serviceResolver.ts
import { ApolloError } from "apollo-server-express";
const ServiceResolvers = {
Query: {
getAllUsers: async (_: any, args: any) => {
try {
const mockUsers = [{ name: "xyz" }, { name: "abc" }];
return mockUsers;
} catch (error) {
throw new ApolloError(error);
}
},
},
};
export default ServiceResolvers;
Now we have to make a executable schema from the above schema & resolver. We will use makeExecutableSchema function from graphql-tools library. You can read more details in graphql-tools docs.
Create schema.ts
in \src
folder and write below code:
import { makeExecutableSchema } from "graphql-tools";
import { ServiceTypeDefs } from "./service/serviceSchema";
import serviceResolvers from "./service/serviceResolver";
import ServiceResolvers from "./service/serviceResolver";
export const schema = makeExecutableSchema({
typeDefs: ServiceTypeDefs,
resolvers: ServiceResolvers,
});
Setup server with express & apollo server
in our \server.ts
file
import express from "express";
import { ApolloServer } from "apollo-server-express";
import { createServer } from "http";
import compression from "compression";
import cors from "cors";
import helmet from "helmet";
import { schema } from "./schema";
const PORT = process.env.PORT || 3000;
const app = express();
app.use("*", cors());
app.use(helmet());
app.use(compression());
const server = new ApolloServer({
schema,
});
server.applyMiddleware({ app, path: "/graphql" });
const httpServer = createServer(app);
httpServer.listen({ port: PORT }, (): void =>
console.log(`🚀GraphQL-Server is running on http://localhost:3000/graphql`)
);
Last step: Compile the code
We have to adjust our script in package.json
file.
"scripts": {
"start": "node 'dist/server.js'",
"build": "tsc -p . && ncp src dist",
"test": "jest",
"test:watch": "jest --verbose --detectOpenHandles",
"start:dev": "npm run build:dev",
"build:dev": "nodemon 'src/server.ts' --exec 'ts-node' src/server.ts -e ts,graphql"
},
Aaaaand… That’s it! Running $npm run build
in your terminal will compile your code and put it in the /dist
folder. Now run the compiled code with $npm run start
Open graphql playground on your localhost by opening http://localhost:3000/graphql
in your browser. You can perform query in playground.
query{
getAllUsers{
name
}
}
Additional optional part (but most recommended to keep graphQL best practices) Unit Testing with jest:
For the best practice and keep consistent folder structure we can create a test
folder inside each service
folder(If you have multiple).
Create service.test.ts
inside /src/service/test
folder.
Install jest package:
$npm i jest ts-jest @types/jest --save-dev
create jest.config.js
file in root directory of the project
module.exports = {
globals: {
"ts-jest": {
tsConfig: "tsconfig.json",
},
},
moduleFileExtensions: ["ts", "js"],
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
},
testMatch: ["**/test/**/*.test.(ts|js)"],
testEnvironment: "node",
moduleNameMapper: {
"src(.*)$": "<rootDir>/src/$1",
},
};
Let's write a simple unit test in our service.test.ts
file.
import { ServiceTypeDefs } from "../serviceSchema";
import ServiceResolvers from "../serviceResolver";
import { graphql } from "graphql";
import { makeExecutableSchema } from "graphql-tools";
// create a mocked schema for the tests
const schema = makeExecutableSchema({
typeDefs: ServiceTypeDefs,
resolvers: ServiceResolvers,
});
describe("User Schema", () => {
test("Test getAllUsers query", async () => {
const query = `
{
user: getAllUsers {
name
}
}
`;
return graphql(schema, query).then((result: any) => {
const users = result.data.user;
expect(users.length).toBe(2);
});
});
});
The above query would simply check if the numbers of user return from query are exactly 2 or not. You can read more about testing with jest in jest documentation
Now let's adjust our script in package.json
to automate unit testing with jest
"scripts": {
"start": "node 'dist/server.js'",
"build": "tsc -p . && ncp src dist",
"test": "jest",
"test:watch": "jest --verbose --detectOpenHandles",
"start:dev": "npm run build:dev",
"build:dev": "nodemon 'src/server.ts' --exec 'ts-node' src/server.ts -e ts,graphql"
},
Now we can run all test with $npm run test
and jest will automatically run all tests and display test results in terminal.
MISSION ACCOMPLISHED!!
Woohoo... We have made apollo server with typeScript. We can use it as a starter template to build powerful graphQL backend server.
Here is the github repo with all the code
I will soon add full series of Advanced Apollo Federation here so please follow US & stay upToDate.