GraphQL API с TypeScript: пошаговый туториал

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

GraphQL API с TypeScript: пошаговый туториал

GraphQL — это язык запросов для API, который позволяет клиентам запрашивать только те данные, которые им нужны. В сочетании с TypeScript, статическая типизация помогает избежать ошибок на этапе разработки и делает код более предсказуемым. В этом туториале мы создадим полноценный GraphQL API с использованием Apollo Server и TypeScript.

Мы разберём: настройку проекта, определение схемы и типов, реализацию резолверов, мутации для изменения данных и интеграцию с базой данных (in-memory для простоты). К концу статьи вы сможете запустить собственный GraphQL сервер.

1. Подготовка проекта и установка зависимостей

Первым делом создадим новый проект Node.js и установим необходимые пакеты.

mkdir graphql-typescript-tutorial
cd graphql-typescript-tutorial
npm init -y
npm install apollo-server graphql
npm install -D typescript @types/node ts-node nodemon

Создадим файл tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "rootDir": "./src",
    "outDir": "./dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

Структура проекта будет такой:

  • src/index.ts — точка входа
  • src/schema.ts — GraphQL схема
  • src/resolvers.ts — резолверы
  • src/types.ts — TypeScript типы

2. Определение типов и схемы GraphQL

GraphQL схема описывает, какие данные доступны и как их запрашивать. Для примера создадим простую систему управления пользователями и постами.

Создадим src/types.ts:

export interface User {
  id: number;
  name: string;
  email: string;
}

export interface Post {
  id: number;
  title: string;
  content: string;
  authorId: number;
}

export interface CreateUserInput {
  name: string;
  email: string;
}

export interface CreatePostInput {
  title: string;
  content: string;
  authorId: number;
}

Теперь создадим src/schema.ts с определением типов GraphQL:

import { gql } from 'apollo-server';

export const typeDefs = gql`
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
  }

  input CreateUserInput {
    name: String!
    email: String!
  }

  input CreatePostInput {
    title: String!
    content: String!
    authorId: ID!
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
    posts: [Post!]!
    post(id: ID!): Post
  }

  type Mutation {
    createUser(input: CreateUserInput!): User!
    createPost(input: CreatePostInput!): Post!
  }
`;

Обратите внимание: мы используем input типы для мутаций — это стандартная практика в GraphQL.

3. Реализация резолверов с TypeScript

Резолверы — это функции, которые возвращают данные для каждого поля схемы. Создадим src/resolvers.ts:

import { User, Post, CreateUserInput, CreatePostInput } from './types';

// In-memory база данных
let users: User[] = [
  { id: 1, name: 'Alice', email: 'alice@example.com' },
  { id: 2, name: 'Bob', email: 'bob@example.com' },
];

let posts: Post[] = [
  { id: 1, title: 'GraphQL Basics', content: '...', authorId: 1 },
  { id: 2, title: 'TypeScript Guide', content: '...', authorId: 2 },
];

let nextUserId = 3;
let nextPostId = 3;

export const resolvers = {
  Query: {
    users: (): User[] => users,
    user: (_: unknown, args: { id: string }): User | undefined =>
      users.find(u => u.id === Number(args.id)),
    posts: (): Post[] => posts,
    post: (_: unknown, args: { id: string }): Post | undefined =>
      posts.find(p => p.id === Number(args.id)),
  },

  Mutation: {
    createUser: (_: unknown, args: { input: CreateUserInput }): User => {
      const newUser: User = {
        id: nextUserId++,
        name: args.input.name,
        email: args.input.email,
      };
      users.push(newUser);
      return newUser;
    },
    createPost: (_: unknown, args: { input: CreatePostInput }): Post => {
      // Проверяем, существует ли автор
      const author = users.find(u => u.id === Number(args.input.authorId));
      if (!author) {
        throw new Error('Author not found');
      }
      const newPost: Post = {
        id: nextPostId++,
        title: args.input.title,
        content: args.input.content,
        authorId: Number(args.input.authorId),
      };
      posts.push(newPost);
      return newPost;
    },
  },

  // Резолверы для связей между типами
  User: {
    posts: (parent: User): Post[] =>
      posts.filter(p => p.authorId === parent.id),
  },

  Post: {
    author: (parent: Post): User | undefined =>
      users.find(u => u.id === parent.authorId),
  },
};

Ключевой момент: TypeScript автоматически проверяет типы аргументов и возвращаемых значений. Если вы попытаетесь вернуть неверный тип, компилятор выдаст ошибку.

4. Запуск сервера и тестирование

Создадим src/index.ts:

import { ApolloServer } from 'apollo-server';
import { typeDefs } from './schema';
import { resolvers } from './resolvers';

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

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

Добавим скрипты в package.json:

"scripts": {
  "dev": "nodemon --exec ts-node src/index.ts",
  "build": "tsc",
  "start": "node dist/index.js"
}

Запустите сервер:

npm run dev

Откройте http://localhost:4000 — вы увидите GraphQL Playground. Попробуйте выполнить запросы:

Пример запроса (Query):

query {
  users {
    id
    name
    email
    posts {
      title
    }
  }
}

Пример мутации:

m                

Блоги

Рекомендаций по книгам