TypeGraphQL与Restify集成:REST风格的GraphQL服务器

TypeGraphQL与Restify集成:REST风格的GraphQL服务器

【免费下载链接】type-graphql Create GraphQL schema and resolvers with TypeScript, using classes and decorators! 【免费下载链接】type-graphql 项目地址: https://gitcode.com/gh_mirrors/ty/type-graphql

你是否在REST API与GraphQL之间犹豫不决?TypeGraphQL与Restify的集成方案让你无需取舍——既保留RESTful API的直观结构,又获得GraphQL的灵活查询能力。本文将带你实现一个兼具两种架构优势的服务器,通过装饰器和类定义GraphQL模式,同时利用Restify的路由系统构建REST风格的端点。

技术架构概览

TypeGraphQL通过装饰器将TypeScript类转换为GraphQL模式,而Restify提供了高性能的RESTful路由系统。二者结合可实现:

  • 强类型的GraphQL模式定义
  • REST风格的API端点设计
  • 统一的错误处理与中间件支持

技术架构示意图

核心依赖组件

  • TypeGraphQLsrc/index.ts提供核心装饰器与模式构建功能
  • Restify:REST风格HTTP服务器框架
  • Apollo Server:GraphQL查询处理核心
  • reflect-metadata:元数据反射支持

环境准备与项目初始化

安装依赖包

npm install type-graphql restify @apollo/server graphql reflect-metadata
npm install -D typescript @types/restify

配置TypeScript

确保tsconfig.json包含必要配置:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strictPropertyInitialization": false
  }
}

实现步骤

1. 定义GraphQL类型与解析器

创建食谱类型examples/simple-usage/recipe.type.ts

import { ObjectType, Field, Int } from "type-graphql";

@ObjectType()
export class Recipe {
  @Field(type => Int)
  id: number;

  @Field()
  title: string;

  @Field(type => [String])
  ingredients: string[];
}

实现解析器examples/simple-usage/recipe.resolver.ts

import { Resolver, Query, Arg } from "type-graphql";
import { Recipe } from "./recipe.type";

@Resolver(of => Recipe)
export class RecipeResolver {
  private recipes: Recipe[] = [
    { id: 1, title: " pancakes", ingredients: ["flour", "eggs", "milk"] }
  ];

  @Query(returns => [Recipe])
  async getRecipes(): Promise<Recipe[]> {
    return this.recipes;
  }

  @Query(returns => Recipe)
  async getRecipe(@Arg("id") id: number): Promise<Recipe | undefined> {
    return this.recipes.find(recipe => recipe.id === id);
  }
}

2. 构建GraphQL模式

使用TypeGraphQL的buildSchema创建可执行模式docs/bootstrap.md

import "reflect-metadata";
import { buildSchema } from "type-graphql";
import { RecipeResolver } from "./recipe.resolver";

async function createSchema() {
  return buildSchema({
    resolvers: [RecipeResolver],
    emitSchemaFile: true
  });
}

3. 集成Restify服务器

创建服务器入口文件src/server.ts

import restify from "restify";
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
import { createSchema } from "./schema";
import express from "express";

async function bootstrap() {
  // 创建Restify服务器
  const server = restify.createServer({
    name: "typegraphql-restify-server",
    version: "1.0.0"
  });

  // 配置中间件
  server.use(restify.plugins.acceptParser(server.acceptable));
  server.use(restify.plugins.queryParser());
  server.use(restify.plugins.bodyParser());

  // 创建Express适配器
  const app = express();
  server.use(expressMiddleware(server));

  // 构建GraphQL模式
  const schema = await createSchema();

  // 设置Apollo Server
  const apolloServer = new ApolloServer({ schema });
  await apolloServer.start();

  // 挂载GraphQL中间件
  app.use("/graphql", expressMiddleware(apolloServer));
  
  // 定义REST风格端点
  server.get("/recipes", async (req, res) => {
    const recipes = await apolloServer.executeOperation({
      query: "{ getRecipes { id title ingredients } }"
    });
    res.send(recipes.body.singleResult.data?.getRecipes);
  });

  server.get("/recipes/:id", async (req, res) => {
    const recipe = await apolloServer.executeOperation({
      query: `{ getRecipe(id: ${req.params.id}) { id title ingredients } }`
    });
    res.send(recipe.body.singleResult.data?.getRecipe);
  });

  // 启动服务器
  server.listen(4000, () => {
    console.log(`Server running at http://localhost:4000`);
    console.log(`GraphQL endpoint: http://localhost:4000/graphql`);
  });
}

bootstrap().catch(console.error);

4. 实现REST与GraphQL统一处理

通过Restify中间件实现请求分发:

// 添加错误处理中间件
server.on("restifyError", (req, res, err) => {
  res.send(500, {
    error: err.message,
    code: err.restCode
  });
});

// 健康检查端点
server.get("/health", (req, res) => {
  res.send({ status: "ok", timestamp: new Date() });
});

测试与验证

启动服务器

tsc && node dist/server.js

测试REST端点

# 获取所有食谱
curl http://localhost:4000/recipes

# 获取单个食谱
curl http://localhost:4000/recipes/1

访问GraphQL Playground

打开浏览器访问http://localhost:4000/graphql,执行查询:

query {
  getRecipes {
    id
    title
    ingredients
  }
}

GraphQL Playground

高级特性集成

中间件支持

添加认证中间件examples/middlewares-custom-decorators/middlewares/

import { Middleware } from "type-graphql";

export const AuthMiddleware: Middleware = async ({ context }, next) => {
  if (!context.user) {
    throw new Error("未授权访问");
  }
  return next();
};

错误处理

实现统一错误处理src/errors/

import { ApolloServerErrorCode } from "@apollo/server/errors";

export class RecipeNotFoundError extends Error {
  constructor() {
    super("Recipe not found");
    this.name = "RecipeNotFoundError";
  }
}

// 在Apollo Server中注册
const apolloServer = new ApolloServer({
  schema,
  formatError: (err) => {
    if (err.extensions.code === ApolloServerErrorCode.GRAPHQL_VALIDATION_FAILED) {
      return new Error("Invalid query");
    }
    return err;
  }
});

部署与优化

性能调优

参考benchmarks/simple/中的性能测试结果,建议:

  • 使用apollo-serverpersistedQueries减少网络传输
  • 为复杂查询添加缓存层
  • 启用TypeScript编译优化

部署架构

部署架构图

推荐部署流程:

  1. 使用Docker容器化应用
  2. 配置Nginx反向代理
  3. 启用HTTPS加密
  4. 实施健康检查与自动重启

总结与扩展

通过TypeGraphQL与Restify的集成,我们构建了一个兼具REST直观性和GraphQL灵活性的API服务。该架构特别适合:

  • 从REST API向GraphQL平滑迁移
  • 需要同时支持两种查询风格的场景
  • 重视类型安全与开发效率的团队

后续可探索:

关注项目CONTRIBUTING.md获取最新更新,欢迎提交PR完善REST集成方案。

【免费下载链接】type-graphql Create GraphQL schema and resolvers with TypeScript, using classes and decorators! 【免费下载链接】type-graphql 项目地址: https://gitcode.com/gh_mirrors/ty/type-graphql

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值