apollo-server

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Apollo Server 5.x Guide

Apollo Server 5.x 指南

Apollo Server is an open-source GraphQL server that works with any GraphQL schema. Apollo Server 5 is framework-agnostic and runs standalone or integrates with Express, Fastify, and serverless environments.
Apollo Server是一款开源的GraphQL服务器,可与任何GraphQL模式配合使用。Apollo Server 5与框架无关,可独立运行,也可与Express、Fastify和无服务器环境集成。

Quick Start

快速开始

Step 1: Install

步骤1:安装

bash
npm install @apollo/server graphql
For Express integration:
bash
npm install @apollo/server @as-integrations/express5 express graphql cors
bash
npm install @apollo/server graphql
如需与Express集成:
bash
npm install @apollo/server @as-integrations/express5 express graphql cors

Step 2: Define Schema

步骤2:定义模式

typescript
const typeDefs = `#graphql
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;
typescript
const typeDefs = `#graphql
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

Step 3: Write Resolvers

步骤3:编写解析器

typescript
const resolvers = {
  Query: {
    books: () => [
      { title: "The Great Gatsby", author: "F. Scott Fitzgerald" },
      { title: "1984", author: "George Orwell" },
    ],
  },
};
typescript
const resolvers = {
  Query: {
    books: () => [
      { title: "The Great Gatsby", author: "F. Scott Fitzgerald" },
      { title: "1984", author: "George Orwell" },
    ],
  },
};

Step 4: Start Server

步骤4:启动服务器

Standalone (Recommended for prototyping):
The standalone server is great for prototyping, but for production services, we recommend integrating Apollo Server with a more fully-featured web framework such as Express, Koa, or Fastify. Swapping from the standalone server to a web framework later is straightforward.
typescript
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";

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

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`Server ready at ${url}`);
Express:
typescript
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@as-integrations/express5";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import express from "express";
import http from "http";
import cors from "cors";

const app = express();
const httpServer = http.createServer(app);

const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});

await server.start();

app.use(
  "/graphql",
  cors(),
  express.json(),
  expressMiddleware(server, {
    context: async ({ req }) => ({ token: req.headers.authorization }),
  }),
);

await new Promise<void>((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log("Server ready at http://localhost:4000/graphql");
独立模式(推荐用于原型开发):
独立服务器非常适合原型开发,但对于生产服务,我们建议将Apollo Server与功能更完善的Web框架(如Express、Koa或Fastify)集成。后续从独立服务器切换到Web框架的过程非常简单。
typescript
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";

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

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`Server ready at ${url}`);
Express集成:
typescript
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@as-integrations/express5";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import express from "express";
import http from "http";
import cors from "cors";

const app = express();
const httpServer = http.createServer(app);

const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});

await server.start();

app.use(
  "/graphql",
  cors(),
  express.json(),
  expressMiddleware(server, {
    context: async ({ req }) => ({ token: req.headers.authorization }),
  }),
);

await new Promise<void>((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log("Server ready at http://localhost:4000/graphql");

Schema Definition

模式定义

Scalar Types

标量类型

  • Int
    - 32-bit integer
  • Float
    - Double-precision floating-point
  • String
    - UTF-8 string
  • Boolean
    - true/false
  • ID
    - Unique identifier (serialized as String)
  • Int
    - 32位整数
  • Float
    - 双精度浮点数
  • String
    - UTF-8字符串
  • Boolean
    - 真/假
  • ID
    - 唯一标识符(序列化为字符串)

Type Definitions

类型定义

graphql
type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]!
}

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

input CreatePostInput {
  title: String!
  content: String
}

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

type Mutation {
  createPost(input: CreatePostInput!): Post!
}
graphql
type User {
  id: ID!
  name: String!
  email: String
  posts: [Post!]!
}

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

input CreatePostInput {
  title: String!
  content: String
}

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

type Mutation {
  createPost(input: CreatePostInput!): Post!
}

Enums and Interfaces

枚举与接口

graphql
enum Status {
  DRAFT
  PUBLISHED
  ARCHIVED
}

interface Node {
  id: ID!
}

type Article implements Node {
  id: ID!
  title: String!
}
graphql
enum Status {
  DRAFT
  PUBLISHED
  ARCHIVED
}

interface Node {
  id: ID!
}

type Article implements Node {
  id: ID!
  title: String!
}

Resolvers Overview

解析器概述

Resolvers follow the signature:
(parent, args, contextValue, info)
  • parent: Result from parent resolver (root resolvers receive undefined)
  • args: Arguments passed to the field
  • contextValue: Shared context object (auth, dataSources, etc.)
  • info: Field-specific info and schema details (rarely used)
typescript
const resolvers = {
  Query: {
    user: async (_, { id }, { dataSources }) => {
      return dataSources.usersAPI.getUser(id);
    },
  },
  User: {
    posts: async (parent, _, { dataSources }) => {
      return dataSources.postsAPI.getPostsByAuthor(parent.id);
    },
  },
  Mutation: {
    createPost: async (_, { input }, { dataSources, user }) => {
      if (!user) throw new GraphQLError("Not authenticated");
      return dataSources.postsAPI.create({ ...input, authorId: user.id });
    },
  },
};
解析器遵循以下签名:
(parent, args, contextValue, info)
  • parent:父解析器的结果(根解析器接收undefined)
  • args:传递给字段的参数
  • contextValue:共享上下文对象(身份验证、数据源等)
  • info:字段特定信息和模式详情(很少使用)
typescript
const resolvers = {
  Query: {
    user: async (_, { id }, { dataSources }) => {
      return dataSources.usersAPI.getUser(id);
    },
  },
  User: {
    posts: async (parent, _, { dataSources }) => {
      return dataSources.postsAPI.getPostsByAuthor(parent.id);
    },
  },
  Mutation: {
    createPost: async (_, { input }, { dataSources, user }) => {
      if (!user) throw new GraphQLError("Not authenticated");
      return dataSources.postsAPI.create({ ...input, authorId: user.id });
    },
  },
};

Context Setup

上下文设置

Context is created per-request and passed to all resolvers.
typescript
interface MyContext {
  token?: string;
  user?: User;
  dataSources: {
    usersAPI: UsersDataSource;
    postsAPI: PostsDataSource;
  };
}

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

// Standalone
const { url } = await startStandaloneServer(server, {
  context: async ({ req }) => ({
    token: req.headers.authorization || "",
    user: await getUser(req.headers.authorization || ""),
    dataSources: {
      usersAPI: new UsersDataSource(),
      postsAPI: new PostsDataSource(),
    },
  }),
});

// Express middleware
expressMiddleware(server, {
  context: async ({ req, res }) => ({
    token: req.headers.authorization,
    user: await getUser(req.headers.authorization),
    dataSources: {
      usersAPI: new UsersDataSource(),
      postsAPI: new PostsDataSource(),
    },
  }),
});
上下文是按请求创建的,并传递给所有解析器。
typescript
interface MyContext {
  token?: string;
  user?: User;
  dataSources: {
    usersAPI: UsersDataSource;
    postsAPI: PostsDataSource;
  };
}

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

// 独立模式
const { url } = await startStandaloneServer(server, {
  context: async ({ req }) => ({
    token: req.headers.authorization || "",
    user: await getUser(req.headers.authorization || ""),
    dataSources: {
      usersAPI: new UsersDataSource(),
      postsAPI: new PostsDataSource(),
    },
  }),
});

// Express中间件
expressMiddleware(server, {
  context: async ({ req, res }) => ({
    token: req.headers.authorization,
    user: await getUser(req.headers.authorization),
    dataSources: {
      usersAPI: new UsersDataSource(),
      postsAPI: new PostsDataSource(),
    },
  }),
});

Reference Files

参考文档

Detailed documentation for specific topics:
  • Resolvers - Resolver patterns and best practices
  • Context and Auth - Authentication and authorization
  • Plugins - Server and request lifecycle hooks
  • Data Sources - RESTDataSource and DataLoader
  • Error Handling - GraphQLError and error formatting
  • Troubleshooting - Common issues and solutions
特定主题的详细文档:
  • Resolvers - 解析器模式与最佳实践
  • Context and Auth - 身份验证与授权
  • Plugins - 服务器与请求生命周期钩子
  • Data Sources - RESTDataSource与DataLoader
  • Error Handling - GraphQLError与错误格式化
  • Troubleshooting - 常见问题与解决方案

Key Rules

核心规则

Schema Design

模式设计

  • Use ! (non-null) for fields that always have values
  • Prefer input types for mutations over inline arguments
  • Use interfaces for polymorphic types
  • Keep schema descriptions for documentation
  • 对始终有值的字段使用**!**(非空)
  • 优先使用输入类型处理突变,而非内联参数
  • 对多态类型使用接口
  • 为模式添加描述以方便文档生成

Resolver Best Practices

解析器最佳实践

  • Keep resolvers thin - delegate to services/data sources
  • Always handle errors explicitly
  • Use DataLoader for batching related queries
  • Return partial data when possible (GraphQL's strength)
  • 保持解析器简洁 - 将逻辑委托给服务/数据源
  • 始终显式处理错误
  • 使用DataLoader批量处理相关查询
  • 尽可能返回部分数据(GraphQL的优势)

Performance

性能优化

  • Use
    @defer
    and
    @stream
    for large responses
  • Implement DataLoader to solve N+1 queries
  • Consider persisted queries for production
  • Use caching headers and CDN where appropriate
  • 对大型响应使用
    @defer
    @stream
  • 实现DataLoader解决N+1查询问题
  • 考虑在生产环境使用持久化查询
  • 适当使用缓存头和CDN

Ground Rules

基础规则

  • ALWAYS use Apollo Server 5.x patterns (not v4 or earlier)
  • ALWAYS type your context with TypeScript generics
  • ALWAYS use
    GraphQLError
    from
    graphql
    package for errors
  • NEVER expose stack traces in production errors
  • PREFER
    startStandaloneServer
    for prototyping only
  • USE an integration with a server framework like Express, Koa, Fastify, Next, etc. for production apps
  • IMPLEMENT authentication in context, authorization in resolvers
  • 始终使用Apollo Server 5.x的模式(而非v4或更早版本)
  • 始终使用TypeScript泛型为上下文添加类型
  • 始终使用
    graphql
    包中的
    GraphQLError
    处理错误
  • 切勿在生产环境错误中暴露堆栈跟踪
  • 仅在原型开发时优先使用
    startStandaloneServer
  • 生产应用请使用与Express、Koa、Fastify、Next等服务器框架的集成
  • 在上下文中实现身份验证,在解析器中实现授权