使用 GraphQL Subscriptions 和 TypeScript 构建聊天应用:第 1 部分

2025-06-08

使用 GraphQL Subscriptions 和 TypeScript 构建聊天应用:第 1 部分

嘿!

如果您对GraphQL略知一二,那么您可能听说过订阅功能,以及它在构建实时应用程序中的实用性。在本系列博客中,我们将使用 Node.js、React 和 GraphQL 构建一个简单的聊天应用程序。我们将在本系列文章中使用 TypeScript,并遵循代码优先的原则!

安装依赖项

我们将使用Apollo ServerExpressTypeGraphQL作为该服务器。

使用 TypeScript 在 Node.js 中开发 GraphQL API 总是有点麻烦,因为你必须手动创建所有类型,其中很多类型以后会导致冗余,但 TypeGraphQL 使用类和装饰器确实让这一切变得简单。

让我们首先npm init -y在一个全新的目录中运行以生成package.json并安装所需的依赖项。

yarn add apollo-server-express class-validator cors dotenv express graphql reflect-metadata type-graphql
yarn add -D @types/cors @types/express @types/node typescript
Enter fullscreen mode Exit fullscreen mode

我们基本上使用 Express 作为 Apollo Server 的中间件集成,使用了apollo-server-express包。所有依赖项安装完成后,创建一个src文件夹。所有 TS 文件都将存放在这里。这将帮助我们轻松管理编译。

我们还需要一个文件来根据我们的喜好设置 TypeScript。Ben Awadtsconfig.json编写了一个很棒的实用程序,可以自动生成该文件。运行并选择。现在,我们就可以开始编写 GraphQL API 了!npx tsconfig.jsonnode

我们将遵循下面描述的文件结构!

├── server
│     ├── src
│     │   ├── entities
│     |   |   ├── Chat.ts
│     │   ├── resolvers
│     |   |   ├── chat.ts
│     │   ├── index.ts
│     ├── package.json
│     ├── tsconfig.json
│     ├── .env
Enter fullscreen mode Exit fullscreen mode

构建我们的服务器

创建一个index.ts文件并使用以下代码初始化我们的服务器,

import "reflect-metadata";
import { ApolloServer } from "apollo-server-express";
import express from "express";
import { buildSchema } from "type-graphql";
import cors from "cors";
import dotenv from "dotenv";

dotenv.config();

const main = async () => {
  const app = express();

  app.use(cors({ origin: "http://localhost:3000", credentials: true }));

  const apolloServer = new ApolloServer({
    schema: await buildSchema({
      resolvers: [ChatResolver],
      validate: false,
    }),
  });

  apolloServer.applyMiddleware({
    app,
    cors: false,
  });

  app.listen(process.env.PORT, () => {
    console.log(
      `Server ready at http://localhost:${process.env.PORT}${apolloServer.graphqlPath}`
    );
  });
};

main().catch((err) => {
  console.log(err);
});
Enter fullscreen mode Exit fullscreen mode

让我们分解一下我们刚才写的内容。我们在一个异步函数中初始化了服务器main(),以备不时之需await,并声明了一个 express 应用程序。app我们还允许从 进行 cors 访问localhost:3000,稍后我们将在这里运行我们的 React 应用。另外,请记住在reflect-metadata导入type-graphql任何解析器之前先导入 shim 包。

然后,我们创建了一个新实例ApolloServer,并将我们的 Express 中间件应用到其中。最后,我们使用 启动了服务器app.listen()。我们还使用来从文件中dotenv加载环境变量,即。在本例中,我们将考虑PORT.envPORT=9000

但你可能已经注意到了,这段代码无法运行,因为我们还没有ChatResolver。事实上,我们还没有为 GraphQL API 创建一个解析器,所以让我们先创建一个解析器。

但在此之前,我们需要定义实体。可以将其视为通用类型,您将基于它编写 GraphQL 解析器,例如查询、修改和订阅,以及数据库操作。这正是 TypeGraphQL 的用武之地。由于我们的聊天系统是临时的,因此我们不会在这里使用数据库,但您明白我的意思!

定义我们的实体

因此创建entities/Chat.ts文件并Chat使用以下代码定义我们的实体!

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

@ObjectType()
export class Chat {
  @Field()
  id: number;

  @Field()
  message: string;

  @Field()
  name: string;
}
Enter fullscreen mode Exit fullscreen mode

好了,让我们理解一下我们刚刚写了什么!我们定义了一个导出的 TypeScript 类,Chat其中包含多个装饰器。该类有三个属性成员,idmessagename,每个成员都有各自的类型。这很简单,但让我们先来了解一下这些装饰器的作用。

使用 TypeGraphQL 装饰器的主要思想是使用 SDL(模式定义语言)自动从 TypeScript 类创建 GraphQL 模式定义。这样就无需在 TypeScript 中创建模式定义文件及其等效接口。

在这里,我们做的第一件事是Chat@ObjectType装饰器来装饰类。它将类标记为type来自 GraphQL SDL 或GraphQLObjectTypefrom graphql-js。然后,我们声明需要映射到 GraphQL 字段的类属性。为此,我们使用了@Field装饰器,它也用于从 TypeScript 类型反射系统收集元数据。默认情况下,此处实体中的所有字段都是不可空的!

该实体将导致在 SDL 中生成 GraphQL 模式的以下部分。

type Chat {
  id: Float!
  message: String!
  name: String!
}
Enter fullscreen mode Exit fullscreen mode

正如您所见,!这里的所有字段都是必填项(),即不可为空!

现在,我们已经成功为每个聊天定义了 GraphQL 模式及其类型!现在,让我们在Chat实体上定义一个 GraphQL 解析器。

查询和变异

创建一个resolvers/chat.ts文件并输入以下内容,

import { Mutation, Query, Resolver, Arg } from "type-graphql";
import { Chat } from "../entities/Chat";

const chats: Chat[] = [];

@Resolver()
export class ChatResolver {
  @Query(() => [Chat])
  getChats(): Chat[] {
    return chats;
  }

  @Mutation(() => Chat)
  createChat(
    @Arg("name") name: string,
    @Arg("message") message: string
  ): Chat {
    const chat = { id: chats.length + 1, name, message };
    chats.push(chat);
    return chat;
  }
}
Enter fullscreen mode Exit fullscreen mode

这里有很多新代码,所以让我们先了解一下这里要做什么。除了 GraphQL 对象类型之外,TypeGraphQL 还允许我们以 REST 控制器类型的方式创建 GraphQL 查询、修改和订阅。

首先,我们基于实体定义一个数组Chat,该数组将充当数据库的作用。然后,我们定义一个导出类ChatResolver,其中包含方法getChat(),它返回整个聊天数组,以及,它通过接收参数createChat(),将一个新的聊天对象附加到数组末尾。现在我们了解了这个类的常规功能,让我们了解一下这些装饰器添加了什么。namemessage

第一个装饰器@Resolver()使该类的行为类似于经典的 REST 控制器。因此,该类中的方法现在可以像 GraphQL 查询、修改和订阅处理程序一样运行。

这引出了下一个装饰器,即@Query(() => [Chat])@Mutation(() => Chat),它们允许我们将解析器类方法标记为 GraphQL 查询或突变解析器。我们还需要显式声明这些方法解析的类型,即它们的返回类型,在这里 是一个Chat对象数组getChats(), 是一个单个对象createChat()

最后,还有一个内联@Arg()装饰器,它允许我们为特定的 GraphQL 查询/修改指定参数。我们在这个装饰器中传入这些参数的名称。

哇哦!我们的解析器现在可以正常工作了!让我们继续尝试运行服务器!但首先,导入ChatResolverindex.ts添加以下脚本package.json

"scripts": {
    "watch": "tsc -w",
    "dev": "nodemon dist/index.js",
    "build": "tsc",
    "start": "node dist/index.js"
},
Enter fullscreen mode Exit fullscreen mode

最后,启动你的终端,yarn watch分别运行yarn dev这两个环境!watch 命令允许你修改 TS 文件,这些修改会立即编译成dist/目录中的 JS 文件。然后我们使用nodemon, 来运行编译好的 JS 文件,并在任何更改发生时重新启动。这样就能得到一个非常接近生产环境和开发环境的版本!

访问localhost:9000/graphql以查看您的 GraphQL 游乐场,您可以在其中运行查询!

在 GraphQL Playground 中运行 GraphQL 操作

现在,访问localhost:9000/graphql以查看您的 GraphQL Playground,让我们执行查询和变异。

要添加新聊天,您需要运行以下命令:

mutation {
  createChat(name: "John", message: "first chat") {
    id
    name
    message
  }
}
Enter fullscreen mode Exit fullscreen mode

要获取所有聊天记录,请运行以下查询

query {
  getChats {
    id
    name
    message
  }
}
Enter fullscreen mode Exit fullscreen mode

如您所见,我们的类方法已变成实际的 GraphQL 操作,它接受参数并返回Chat对象字段!请记住,由于我们将聊天存储在内存数组中,因此重新启动服务器后,所有聊天记录都会消失。

在下一部分中,我们将探讨如何向我们的新 GraphQL API 添加订阅!

结论

访问本系列的下一篇文章,了解 GraphQL 订阅以及如何添加它们!

如果你想深入了解 GraphQL、Apollo Server 和 TypeGraphQL,并发现可以用它们实现的所有酷炫功能,请阅读官方文档。

Apollo 服务器文档

TypeGraphQL 文档

GraphQL 文档

此外,这里还有一份 很棒的资源列表, 可供进一步学习!

如果你遇到困难,这里有  包含所有代码的仓库part-1!访问分支获取本文涵盖的代码。

如有任何疑问,请联系我的 社交媒体 或 GitHub

鏂囩珷鏉ユ簮锛�https://dev.to/gdsckiitdev/build-a-chat-app-with-graphql-subscriptions-typescript-part-1-2p70
PREV
React 及其生命周期方法概述
NEXT
Angular 中的内存泄漏调试