一、环境准备
1. Node.js环境安装
bash
# 安装Node版本管理器
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
# 安装Node.js LTS版本
nvm install --lts
nvm use--lts
# 验证安装
node --version
npm --version
2. 项目初始化
bash
# 创建项目目录
mkdir graphql-server
cd graphql-server
# 初始化项目
npm init -y
# 安装依赖
npm install apollo-server graphql mongoose express
二、GraphQL服务器搭建
1. 基础服务器配置
javascript
// index.js
const{ApolloServer}=require('apollo-server');
const typeDefs =require('./schema');
const resolvers =require('./resolvers');
const server =newApolloServer({
typeDefs,
resolvers,
context:({ req })=>({
token: req.headers.authorization
}),
formatError:(error)=>{
console.error(error);
return error;
}
});
server.listen().then(({ url })=>{
console.log(`🚀 Server ready at ${url}`);
});
2. Schema定义
javascript
// schema.js
const{ gql }=require('apollo-server');
const typeDefs = gql`
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String!
author: User!
}
type Query {
users: [User!]!
user(id: ID!): User
posts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createUser(name: String!, email: String!): User!
createPost(title: String!, content: String!, authorId: ID!): Post!
}
`;
module.exports = typeDefs;
三、数据库集成
1. MongoDB连接配置
javascript
// db.js
const mongoose =require('mongoose');
mongoose.connect('mongodb://localhost:27017/graphql',{
useNewUrlParser:true,
useUnifiedTopology:true
});
const db = mongoose.connection;
db.on('error', console.error.bind(console,'MongoDB connection error:'));
db.once('open',()=> console.log('Connected to MongoDB'));
2. 模型定义
javascript
// models/User.js
const mongoose =require('mongoose');
const userSchema =new mongoose.Schema({
name:String,
email:String,
posts:[{ type: mongoose.Schema.Types.ObjectId,ref:'Post'}]
});
module.exports = mongoose.model('User', userSchema);
四、解析器实现
1. Query解析器
javascript
// resolvers/queries.js
const queries ={
users: async (_, __,{ dataSources })=>{
return await dataSources.users.getAll();
},
user: async (_,{ id },{ dataSources })=>{
return await dataSources.users.findById(id);
},
posts: async (_, __,{ dataSources })=>{
return await dataSources.posts.getAll();
}
};
2. Mutation解析器
javascript
// resolvers/mutations.js
const mutations ={
createUser: async (_,{ name, email },{ dataSources })=>{
return await dataSources.users.create({ name, email });
},
createPost: async (_,{ title, content, authorId },{ dataSources })=>{
return await dataSources.posts.create({
title,
content,
authorId
});
}
};
五、性能优化
1. 数据加载优化
javascript
// dataloaders/user.js
constDataLoader=require('dataloader');
constUser=require('../models/User');
const batchUsers = async (ids)=>{
const users = await User.find({ _id:{ $in: ids }});
return ids.map(id => users.find(user => user.id === id));
};
module.exports =newDataLoader(batchUsers);
2. 查询优化
javascriptCopy// 实现字段级缓存
const resolvers ={
User:{
posts:{
resolve: async (user, _,{ loaders })=>{
return await loaders.postsByUser.load(user.id);
},
cacheControl:{
maxAge:60
}
}
}
};
六、安全配置
1. 认证中间件
javascript
// middleware/auth.js
const jwt =require('jsonwebtoken');
const authenticate = async (req)=>{
const token = req.headers.authorization;
if(!token)returnnull;
try{
const decoded = jwt.verify(token, process.env.JWT_SECRET);
return decoded.user;
}catch(err){
returnnull;
}
};
module.exports = authenticate;
2. 请求限制
javascript
const{ApolloServer}=require('apollo-server-express');
const rateLimit =require('express-rate-limit');
const limiter = rateLimit({
windowMs:15*60*1000,// 15分钟
max:100// 限制100次请求
});
app.use('/graphql', limiter);
七、部署配置
1. PM2部署配置
javascript
// ecosystem.config.js
module.exports ={
apps:[{
name:'graphql-server',
script:'./index.js',
instances:'max',
autorestart:true,
watch:false,
max_memory_restart:'1G',
env:{
NODE_ENV:'production'
}
}]
};
2. Nginx反向代理
nginx
server {
listen 80;
server_name api.yourdomain.com;
location /graphql {
proxy_pass http://localhost:4000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}