Введение
GraphQL — это язык запросов для API, который позволяет клиентам получать только необходимые данные. В сочетании с TypeScript он становится мощным инструментом для создания типобезопасных, масштабируемых и документированных API. В этом пошаговом туториале мы на практике создадим полноценный GraphQL API на TypeScript с использованием Apollo Server и TypeGraphQL.
Вы узнаете:
- Как настроить проект с TypeScript и Apollo Server
- Как определить схему GraphQL с помощью декораторов TypeGraphQL
- Как написать резолверы с полной типизацией
- Как подключить базу данных и выполнять запросы
Этот материал будет полезен как новичкам, так и разработчикам, уже знакомым с REST API, но желающим освоить современный подход к построению API.
1. Настройка проекта и установка зависимостей
Начнем с инициализации проекта. Убедитесь, что у вас установлены Node.js (версия 16+) и npm/yarn.
mkdir graphql-ts-tutorial
cd graphql-ts-tutorial
npm init -y
Установите необходимые пакеты:
npm install apollo-server graphql reflect-metadata type-graphql class-validator
npm install -D typescript @types/node ts-node
Создайте файл tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
Обратите внимание на флаги experimentalDecorators и emitDecoratorMetadata — они необходимы для работы TypeGraphQL.
2. Определение схемы и типов с помощью TypeGraphQL
Создадим папку src и файл src/types/User.ts. Здесь мы опишем модель пользователя.
import { ObjectType, Field, ID, InputType } from "type-graphql";
@ObjectType()
export class User {
@Field(() => ID)
id!: number;
@Field()
name!: string;
@Field()
email!: string;
@Field()
createdAt!: Date;
}
Объяснение:
@ObjectType()— помечает класс как тип GraphQL.@Field()— указывает, какие поля будут доступны в API.@Field(() => ID)— для скалярного типа ID.
Теперь создадим InputType для мутаций (создание пользователя):
@InputType()
export class CreateUserInput {
@Field()
name!: string;
@Field()
email!: string;
}
Этот тип будет использоваться в резолверах для валидации входных данных.
3. Написание резолверов
Создадим файл src/resolvers/UserResolver.ts. Здесь мы реализуем логику запросов и мутаций.
import { Resolver, Query, Mutation, Arg } from "type-graphql";
import { User, CreateUserInput } from "../types/User";
// Имитация базы данных
let users: User[] = [
{ id: 1, name: "Иван Иванов", email: "ivan@example.com", createdAt: new Date() },
{ id: 2, name: "Мария Петрова", email: "maria@example.com", createdAt: new Date() }
];
let nextId = 3;
@Resolver()
export class UserResolver {
@Query(() => [User])
async users(): Promise<User[]> {
return users;
}
@Query(() => User, { nullable: true })
async user(@Arg("id") id: number): Promise<User | undefined> {
return users.find(u => u.id === id);
}
@Mutation(() => User)
async createUser(@Arg("data") data: CreateUserInput): Promise<User> {
const newUser: User = {
id: nextId++,
name: data.name,
email: data.email,
createdAt: new Date()
};
users.push(newUser);
return newUser;
}
}
Ключевые моменты:
@Query— декоратор для запросов (GET).@Mutation— для изменений данных (POST/PUT/DELETE).@Arg— извлекает аргументы из запроса.- Тип возвращаемого значения строго задается в дженерике
@Query(() => [User]).
4. Сборка сервера и запуск
Создадим главный файл src/index.ts:
import "reflect-metadata";
import { ApolloServer } from "apollo-server";
import { buildSchema } from "type-graphql";
import { UserResolver } from "./resolvers/UserResolver";
async function bootstrap() {
const schema = await buildSchema({
resolvers: [UserResolver],
emitSchemaFile: true // генерирует schema.graphql
});
const server = new ApolloServer({ schema });
const { url } = await server.listen(4000);
console.log(`🚀 Сервер запущен на ${url}`);
}
bootstrap();
Добавьте скрипт в package.json:
"scripts": {
"start": "ts-node src/index.ts",
"build": "tsc"
}
Запустите сервер:
npm start
Теперь откройте http://localhost:4000 — вы увидите GraphQL Playground. Попробуйте выполнить запрос:
query {
users {
id
name
email
}
}
И мутацию для создания пользователя:
mutation {
createUser(data: { name: "Алексей", email: "alex@example.com" }) {
id
name
}
}
5. Дополнительные возможности и лучшие практики
Чтобы сделать ваш API более промышленным, добавьте:
- Валидацию — используйте
class-validatorс декораторами@IsEmail(),@Length()в InputType. - Авторизацию — через middleware или декоратор
@Authorized