GraphQL API with TypeScript: Step-by-Step Practical Tutorial

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction

\n\n

GraphQL is a query language for APIs that allows clients to request only the data they need. Combined with TypeScript, it becomes a powerful tool for creating type-safe, scalable, and documented APIs. In this step-by-step tutorial, we will build a full GraphQL API in TypeScript using Apollo Server and TypeGraphQL.

\n\n

You will learn:

\n
    \n
  • How to set up a project with TypeScript and Apollo Server
  • \n
  • How to define a GraphQL schema using TypeGraphQL decorators
  • \n
  • How to write resolvers with full typing
  • \n
  • How to connect a database and execute queries
  • \n
\n\n

This material will be useful for both beginners and developers already familiar with REST APIs who want to learn a modern approach to building APIs.

\n\n

1. Project Setup and Dependency Installation

\n\n

Let's start by initializing the project. Make sure you have Node.js (version 16+) and npm/yarn installed.

\n\n
mkdir graphql-ts-tutorial\ncd graphql-ts-tutorial\nnpm init -y
\n\n

Install the required packages:

\n\n
npm install apollo-server graphql reflect-metadata type-graphql class-validator\nnpm install -D typescript @types/node ts-node
\n\n

Create the tsconfig.json file:

\n\n
{\n  "compilerOptions": {\n    "target": "ES2020",\n    "module": "commonjs",\n    "lib": ["ES2020"],\n    "experimentalDecorators": true,\n    "emitDecoratorMetadata": true,\n    "strict": true,\n    "esModuleInterop": true,\n    "outDir": "./dist",\n    "rootDir": "./src"\n  },\n  "include": ["src/**/*"]\n}
\n\n

Note the experimentalDecorators and emitDecoratorMetadata flags — they are required for TypeGraphQL to work.

\n\n

2. Defining the Schema and Types with TypeGraphQL

\n\n

Let's create the src folder and the file src/types/User.ts. Here we will describe the user model.

\n\n
import { ObjectType, Field, ID, InputType } from "type-graphql";\n\n@ObjectType()\nexport class User {\n  @Field(() => ID)\n  id!: number;\n\n  @Field()\n  name!: string;\n\n  @Field()\n  email!: string;\n\n  @Field()\n  createdAt!: Date;\n}
\n\n

Explanation:

\n
    \n
  • @ObjectType() — marks the class as a GraphQL type.
  • \n
  • @Field() — specifies which fields will be available in the API.
  • \n
  • @Field(() => ID) — for the scalar ID type.
  • \n
\n\n

Now let's create an InputType for mutations (creating a user):

\n\n
@InputType()\nexport class CreateUserInput {\n  @Field()\n  name!: string;\n\n  @Field()\n  email!: string;\n}
\n\n

This type will be used in resolvers for input data validation.

\n\n

3. Writing Resolvers

\n\n

Let's create the file src/resolvers/UserResolver.ts. Here we will implement the query and mutation logic.

\n\n
import { Resolver, Query, Mutation, Arg } from "type-graphql";\nimport { User, CreateUserInput } from "../types/User";\n\n// Simulated database\nlet users: User[] = [\n  { id: 1, name: "Ivan Ivanov", email: "ivan@example.com", createdAt: new Date() },\n  { id: 2, name: "Maria Petrova", email: "maria@example.com", createdAt: new Date() }\n];\n\nlet nextId = 3;\n\n@Resolver()\nexport class UserResolver {\n  @Query(() => [User])\n  async users(): Promise<User[]> {\n    return users;\n  }\n\n  @Query(() => User, { nullable: true })\n  async user(@Arg("id") id: number): Promise<User | undefined> {\n    return users.find(u => u.id === id);\n  }\n\n  @Mutation(() => User)\n  async createUser(@Arg("data") data: CreateUserInput): Promise<User> {\n    const newUser: User = {\n      id: nextId++,\n      name: data.name,\n      email: data.email,\n      createdAt: new Date()\n    };\n    users.push(ne                

Blogs

Book Recommendations