Getting started with Node.js, GraphQL, and TypeScript

Luka
5 min readMar 28, 2023

--

GraphQL is a query language and runtime for APIs that was developed by Facebook. Unlike traditional REST APIs, which expose a fixed set of endpoints and return predefined data structures, GraphQL APIs allow clients to specify exactly what data they need and get back only that data in a predictable format. This makes it easier to build efficient, flexible APIs that can evolve over time.

In this tutorial, we’ll walk through how to set up a Node.js server with GraphQL and TypeScript. We’ll cover the basics of GraphQL, including queries, mutations, resolvers, and nested resolvers.

Prerequisites

Before we get started, make sure you have the following installed on your system:

  • Node.js (v14 or later)
  • npm or yarn package manager

Step 1: Initialize a new Node.js project

To get started, let’s create a new Node.js project. Open up a terminal and run the following commands:

mkdir my-graphql-project
cd my-graphql-project
npm init -y

This will create a new directory called my-graphql-project and initialize a new Node.js project inside it.

Step 2: Install dependencies

Next, we’ll install the dependencies we need for our project. We’ll use the following packages:

  • apollo-server-express: A package for creating GraphQL servers with Express.js
  • graphql: The main GraphQL library
  • typescript: A superset of JavaScript that adds type checking and other features

Run the following command to install these packages:

npm install apollo-server-express graphql typescript

Step 3: Set up TypeScript

Now that we have TypeScript installed, let’s set up our project to use it. Create a new file called tsconfig.json in the root of your project with the following contents:

{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"lib": ["es2017"],
"outDir": "dist",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"sourceMap": true,
"declaration": true,
"declarationDir": "dist/types"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts"]
}

This sets up the TypeScript compiler with some basic options, including the outDir option, which specifies the output directory for compiled TypeScript files, and the rootDir option, which specifies the root directory for TypeScript source files.

Step 4: Create a GraphQL schema

Now that our project is set up with TypeScript, let’s create a GraphQL schema. Create a new file called schema.graphql in the src directory with the following contents:

type Query {
hello: String
}

type Mutation {
addUser(input: AddUserInput!): User!
}

type User {
id: ID!
name: String!
email: String!
role: String!
}

input AddUserInput {
name: String!
email: String!
role: String!
}

This defines a simple GraphQL schema with a Query type, a Mutation type, and a User type. The Query type has a single field called hello that returns a string. The Mutation type has a single field called addUser that takes an AddUserInput object as an argument and returns a User object. The User type has four fields: id, name, email, and role.

Step 5: Define resolvers

Next, we need to define resolvers for our schema. Resolvers are functions that tell GraphQL how to fetch data for a particular field. Create a new file called resolvers.ts in the src directory with the following contents:

import { AddUserInput, User } from './types';

const users: User[] = [
{ id: '1', name: 'John Doe', email: 'john.doe@example.com', role: 'admin' },
{ id: '2', name: 'Jane Doe', email: 'jane.doe@example.com', role: 'user' },
];

const resolvers = {
Query: {
hello: () => 'Hello world!',
},
Mutation: {
addUser: (_parent, { input }: { input: AddUserInput }): User => {
const id = String(users.length + 1);
const user = { id, ...input };
users.push(user);
return user;
},
},
};

export default resolvers;

In this file, we define a users array that contains some sample user data. We then define a resolvers object with two fields: Query and Mutation. The hello resolver for the Query type simply returns a string.

The addUser resolver for the Mutation type takes an input argument of type AddUserInput and adds a new user to the users array with a unique ID. The resolver then returns the new user object.

Step 6: Set up the server

Now that we have our schema and resolvers defined, let’s set up the server. Create a new file called server.ts in the src directory with the following contents:

import express from 'express';
import { ApolloServer, gql } from 'apollo-server-express';
import resolvers from './resolvers';

const typeDefs = gql`
${require('fs').readFileSync(require.resolve('./schema.graphql'), 'utf8')}
`;

const server = new ApolloServer({ typeDefs, resolvers });

const app = express();
server.applyMiddleware({ app });

const PORT = process.env.PORT || 3000;

app.listen(PORT, () => {
console.log(`Server listening on http://localhost:${PORT}${server.graphqlPath}`);
});

In this file, we first import the necessary packages, including express, ApolloServer, and our resolvers object.

We then read the schema.graphql file using the readFileSync method from the fs module and pass the resulting string to the gql function to create a GraphQL schema object.

We create a new instance of ApolloServer with our typeDefs and resolvers, and then set up an Express app and apply the Apollo middleware to it.

Finally, we start the server by calling the listen method on the app object and logging a message to the console.

Before you move to the next step, ensure that your package.json file has required script. With the above steps, it would look like this

{
"name": "my-graphql-project",
"version": "1.0.0",
"description": "A simple GraphQL API with TypeScript",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/server.ts",
"start": "node dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Luc",
"license": "MIT",
"dependencies": {
"apollo-server-express": "^3.7.0",
"express": "^4.17.1",
"graphql": "^16.3.0"
},
"devDependencies": {
"@types/express": "^4.17.13",
"@types/graphql": "^15.0.5",
"@types/node": "^16.11.1",
"nodemon": "^2.0.15",
"ts-node": "^10.4.0",
"typescript": "^4.4.4"
}
}

Step 7: Start the server

We’re now ready to start the server. Run the following command to start the server:

npm run dev

This will start the server in development mode using Nodemon, which will automatically restart the server whenever you make changes to your code.

In this tutorial, we walked through how to set up a Node.js server with GraphQL and TypeScript. We covered the basics of GraphQL, including queries, mutations, resolvers, and nested resolvers. We also discussed the benefits of using GraphQL over REST, such as increased flexibility and efficiency.

With this setup, you can now start building a GraphQL API for your project. You can add new types to the schema as needed and define resolvers to fetch data from your database or other data sources.

Keep in mind that this is just a basic setup to get you started. Depending on the complexity of your project, you may need to add additional packages or configurations to handle things like authentication, caching, and error handling.

Overall, however, Node.js, GraphQL, and TypeScript provide a powerful combination for building modern, scalable APIs that can easily evolve over time.

--

--