使用 GraphQL + Node.js 构建高性能 API

大家好,我是欧阳瑞(Rich Own)。今天想聊聊我这几年做全栈开发最喜欢用的 API 设计方式——GraphQL,配合 Node.js,真的能让后端开发效率提升一个档次。

为什么选择 GraphQL?

用过 RESTful 的同学都知道,有时候为了一个页面,得调用好几个接口,要么是接口返回的数据太多(Overfetching),要么是不够(Underfetching)。GraphQL 完美解决了这个问题,客户端想要什么数据自己决定。

项目初始化

先建个新项目:

mkdir graphql-server && cd graphql-server
npm init -y
npm install express express-graphql graphql cors
npm install --save-dev nodemon

定义 Schema

schema.js 里定义我们的数据模型。假设我们要做一个极客社区的用户系统:

const { buildSchema } = require('graphql');

const schema = buildSchema(`
  type User {
    id: ID!
    name: String!
    bio: String
    github: String
    projects: [Project]
  }

  type Project {
    id: ID!
    title: String!
    description: String
    techStack: [String]
    isWeb3: Boolean
  }

  type Query {
    user(id: ID!): User
    allUsers: [User]
    project(id: ID!): Project
  }

  type Mutation {
    createUser(name: String!, bio: String, github: String): User
    addProject(userId: ID!, title: String!, description: String, techStack: [String], isWeb3: Boolean): Project
  }
`);

module.exports = schema;

准备数据

为了演示,先搞点模拟数据:

// data.js
const users = [
  {
    id: '1',
    name: '欧阳瑞',
    bio: '全栈开发工程师 / Web3 探索者',
    github: 'richown',
    projects: ['1', '2']
  }
];

const projects = [
  {
    id: '1',
    title: 'CyberPunkNFT',
    description: '赛博朋克风格 NFT 平台',
    techStack: ['Solidity', 'Next.js', 'Three.js'],
    isWeb3: true
  },
  {
    id: '2',
    title: 'DAO Toolkit',
    description: '去中心化治理工具集',
    techStack: ['React', 'GraphQL', 'Hardhat'],
    isWeb3: true
  }
];

module.exports = { users, projects };

Resolver 实现

编写 resolvers.js

const { users, projects } = require('./data');

const resolvers = {
  user: ({ id }) => users.find(u => u.id === id),
  allUsers: () => users,
  project: ({ id }) => projects.find(p => p.id === id),
  createUser: ({ name, bio, github }) => {
    const newUser = {
      id: String(users.length + 1),
      name,
      bio,
      github,
      projects: []
    };
    users.push(newUser);
    return newUser;
  },
  addProject: ({ userId, title, description, techStack, isWeb3 }) => {
    const newProject = {
      id: String(projects.length + 1),
      title,
      description,
      techStack: techStack || [],
      isWeb3: isWeb3 || false
    };
    projects.push(newProject);
    const user = users.find(u => u.id === userId);
    if (user) {
      user.projects.push(newProject.id);
    }
    return newProject;
  }
};

module.exports = resolvers;

注意 User 类型里的 projects 字段需要一个 Resolver:

const root = {
  ...resolvers,
  User: {
    projects: (user) => user.projects.map(pid => projects.find(p => p.id === pid))
  }
};

module.exports = root;

启动服务器

server.js

const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const cors = require('cors');
const schema = require('./schema');
const root = require('./resolvers');

const app = express();

app.use(cors());
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true, // 开启 GraphiQL 图形化界面
}));

const PORT = 4000;
app.listen(PORT, () => {
  console.log(`🚀 GraphQL Server running at http://localhost:${PORT}/graphql`);
});

更新 package.json

{
  "scripts": {
    "dev": "nodemon server.js"
  }
}

运行:

npm run dev

试试看!

打开 http://localhost:4000/graphql,GraphiQL 界面会自动打开。

试试这个 Query:

query {
  user(id: "1") {
    name
    bio
    github
    projects {
      title
      techStack
      isWeb3
    }
  }
}

再试试 Mutation:

mutation {
  createUser(name: "Hash Fan", bio: "喜欢鬃狮蜥的开发者", github: "hashfan") {
    id
    name
  }
}

进阶:结合 Prisma

实际开发中,我们当然不会用内存数据。配合 Prisma ORM 体验极佳:

npm install @prisma/client
npm install --save-dev prisma
npx prisma init

定义 schema.prisma,然后生成客户端,改造 Resolver 即可。

写在最后

GraphQL 配合 Node.js 真的是绝配,前端后端都爽。我的鬃狮蜥 Hash 虽然看不懂代码,但它很喜欢我用 GraphQL 开发时高效的样子——因为我能更快搞定工作陪它。

有兴趣的同学欢迎留言交流,我是欧阳瑞,我们下次见!


技术栈:Node.js · GraphQL · Express · Prisma

更多推荐