graphql
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGraphQL with Node.js Skill
Node.js 开发 GraphQL 技能详解
Master building flexible, efficient GraphQL APIs using Node.js with Apollo Server, type-safe schemas, and real-time subscriptions.
掌握使用Node.js结合Apollo Server、类型安全的Schema以及实时订阅来构建灵活、高效的GraphQL API。
Quick Start
快速开始
GraphQL API in 4 steps:
- Define Schema - Types, queries, mutations
- Write Resolvers - Data fetching logic
- Setup Server - Apollo Server configuration
- Connect Data - Database integration
4步构建GraphQL API:
- 定义Schema - 类型、查询、变更
- 编写解析器 - 数据获取逻辑
- 配置服务器 - Apollo Server 配置
- 连接数据 - 数据库集成
Core Concepts
核心概念
Schema Definition (SDL)
Schema定义(SDL)
graphql
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User
deleteUser(id: ID!): Boolean!
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
type Subscription {
postCreated: Post!
}graphql
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
author: User!
}
type Query {
user(id: ID!): User
users(limit: Int, offset: Int): [User!]!
post(id: ID!): Post
}
type Mutation {
createUser(input: CreateUserInput!): User!
updateUser(id: ID!, input: UpdateUserInput!): User
deleteUser(id: ID!): Boolean!
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
type Subscription {
postCreated: Post!
}Apollo Server Setup
Apollo Server 配置
javascript
const { ApolloServer } = require('@apollo/server');
const { expressMiddleware } = require('@apollo/server/express4');
const { readFileSync } = require('fs');
const typeDefs = readFileSync('./schema.graphql', 'utf-8');
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
return dataSources.userAPI.getUser(id);
},
users: async (_, { limit = 10, offset = 0 }, { dataSources }) => {
return dataSources.userAPI.getUsers(limit, offset);
}
},
Mutation: {
createUser: async (_, { input }, { dataSources }) => {
return dataSources.userAPI.createUser(input);
}
},
User: {
posts: async (parent, _, { dataSources }) => {
return dataSources.postAPI.getPostsByAuthor(parent.id);
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
app.use('/graphql', express.json(), expressMiddleware(server, {
context: async ({ req }) => ({
user: req.user,
dataSources: {
userAPI: new UserAPI(),
postAPI: new PostAPI()
}
})
}));javascript
const { ApolloServer } = require('@apollo/server');
const { expressMiddleware } = require('@apollo/server/express4');
const { readFileSync } = require('fs');
const typeDefs = readFileSync('./schema.graphql', 'utf-8');
const resolvers = {
Query: {
user: async (_, { id }, { dataSources }) => {
return dataSources.userAPI.getUser(id);
},
users: async (_, { limit = 10, offset = 0 }, { dataSources }) => {
return dataSources.userAPI.getUsers(limit, offset);
}
},
Mutation: {
createUser: async (_, { input }, { dataSources }) => {
return dataSources.userAPI.createUser(input);
}
},
User: {
posts: async (parent, _, { dataSources }) => {
return dataSources.postAPI.getPostsByAuthor(parent.id);
}
}
};
const server = new ApolloServer({ typeDefs, resolvers });
await server.start();
app.use('/graphql', express.json(), expressMiddleware(server, {
context: async ({ req }) => ({
user: req.user,
dataSources: {
userAPI: new UserAPI(),
postAPI: new PostAPI()
}
})
}));Learning Path
学习路径
Beginner (2-3 weeks)
入门阶段(2-3周)
- ✅ Understand GraphQL concepts
- ✅ Define schemas with SDL
- ✅ Write basic resolvers
- ✅ Query and mutation basics
- ✅ 理解GraphQL核心概念
- ✅ 使用SDL定义Schema
- ✅ 编写基础解析器
- ✅ 查询与变更基础
Intermediate (4-6 weeks)
进阶阶段(4-6周)
- ✅ Field resolvers and relationships
- ✅ Input validation
- ✅ Error handling
- ✅ DataLoader for N+1
- ✅ 字段解析器与关联关系
- ✅ 输入验证
- ✅ 错误处理
- ✅ 使用DataLoader解决N+1问题
Advanced (8-10 weeks)
高级阶段(8-10周)
- ✅ Subscriptions (real-time)
- ✅ Authentication and authorization
- ✅ Federation for microservices
- ✅ Performance optimization
- ✅ 实时订阅(Subscriptions)
- ✅ 身份验证与授权
- ✅ 微服务联邦(Federation)
- ✅ 性能优化
DataLoader for N+1 Problem
使用DataLoader解决N+1查询问题
javascript
const DataLoader = require('dataloader');
const createUserLoader = () =>
new DataLoader(async (userIds) => {
const users = await User.find({ _id: { $in: userIds } });
const userMap = new Map(users.map(u => [u.id, u]));
return userIds.map(id => userMap.get(id));
});
// Use in resolver
User: {
posts: (parent, _, { loaders }) => {
return loaders.userLoader.load(parent.authorId);
}
}javascript
const DataLoader = require('dataloader');
const createUserLoader = () =>
new DataLoader(async (userIds) => {
const users = await User.find({ _id: { $in: userIds } });
const userMap = new Map(users.map(u => [u.id, u]));
return userIds.map(id => userMap.get(id));
});
// Use in resolver
User: {
posts: (parent, _, { loaders }) => {
return loaders.userLoader.load(parent.authorId);
}
}Authentication & Authorization
身份验证与授权
javascript
const { shield, rule, allow } = require('graphql-shield');
const isAuthenticated = rule()((parent, args, { user }) => {
return user !== null;
});
const isAdmin = rule()((parent, args, { user }) => {
return user?.role === 'admin';
});
const permissions = shield({
Query: { '*': allow, users: isAuthenticated },
Mutation: {
createPost: isAuthenticated,
deleteUser: isAdmin
}
});javascript
const { shield, rule, allow } = require('graphql-shield');
const isAuthenticated = rule()((parent, args, { user }) => {
return user !== null;
});
const isAdmin = rule()((parent, args, { user }) => {
return user?.role === 'admin';
});
const permissions = shield({
Query: { '*': allow, users: isAuthenticated },
Mutation: {
createPost: isAuthenticated,
deleteUser: isAdmin
}
});Error Handling
错误处理
javascript
const { ApolloError, UserInputError } = require('apollo-server-express');
class NotFoundError extends ApolloError {
constructor(message) {
super(message, 'NOT_FOUND');
}
}
// In resolvers
const resolvers = {
Query: {
user: async (_, { id }) => {
const user = await User.findById(id);
if (!user) throw new NotFoundError(`User ${id} not found`);
return user;
}
}
};javascript
const { ApolloError, UserInputError } = require('apollo-server-express');
class NotFoundError extends ApolloError {
constructor(message) {
super(message, 'NOT_FOUND');
}
}
// In resolvers
const resolvers = {
Query: {
user: async (_, { id }) => {
const user = await User.findById(id);
if (!user) throw new NotFoundError(`User ${id} not found`);
return user;
}
}
};Unit Test Template
单元测试模板
javascript
const { createTestClient } = require('apollo-server-testing');
describe('GraphQL API', () => {
let query, mutate;
beforeAll(() => {
const server = new ApolloServer({ typeDefs, resolvers });
const testClient = createTestClient(server);
query = testClient.query;
mutate = testClient.mutate;
});
it('should return users list', async () => {
const GET_USERS = `query { users { id name } }`;
const res = await query({ query: GET_USERS });
expect(res.errors).toBeUndefined();
expect(res.data.users).toBeDefined();
});
});javascript
const { createTestClient } = require('apollo-server-testing');
describe('GraphQL API', () => {
let query, mutate;
beforeAll(() => {
const server = new ApolloServer({ typeDefs, resolvers });
const testClient = createTestClient(server);
query = testClient.query;
mutate = testClient.mutate;
});
it('should return users list', async () => {
const GET_USERS = `query { users { id name } }`;
const res = await query({ query: GET_USERS });
expect(res.errors).toBeUndefined();
expect(res.data.users).toBeDefined();
});
});Troubleshooting
常见问题排查
| Problem | Cause | Solution |
|---|---|---|
| N+1 query issue | Field resolvers | Use DataLoader |
| Slow queries | No caching | Implement Apollo Cache |
| Type errors | Schema mismatch | Validate with codegen |
| Auth bypass | Missing guards | Use graphql-shield |
| 问题 | 原因 | 解决方案 |
|---|---|---|
| N+1查询问题 | 字段解析器设计 | 使用DataLoader |
| 查询缓慢 | 未启用缓存 | 实现Apollo缓存 |
| 类型错误 | Schema不匹配 | 使用代码生成工具验证 |
| 授权绕过 | 缺少防护机制 | 使用graphql-shield |
When to Use
适用场景
Use GraphQL when:
- Clients need flexible data fetching
- Multiple clients with different data needs
- Avoiding over-fetching/under-fetching
- Real-time updates needed
- API evolution without versioning
在以下场景选择GraphQL:
- 客户端需要灵活的数据获取方式
- 存在多个数据需求不同的客户端
- 避免过度获取/获取不足数据
- 需要实时更新功能
- API演进无需版本迭代
Related Skills
相关技能
- Express REST API (alternative approach)
- Database Integration (data sources)
- JWT Authentication (securing API)
- Express REST API(替代方案)
- 数据库集成(数据源)
- JWT身份验证(API安全)