使用 GraphQL Subscriptions 和 TypeScript 构建聊天应用:第 1 部分
嘿!
如果您对GraphQL略知一二,那么您可能听说过订阅功能,以及它在构建实时应用程序中的实用性。在本系列博客中,我们将使用 Node.js、React 和 GraphQL 构建一个简单的聊天应用程序。我们将在本系列文章中使用 TypeScript,并遵循代码优先的原则!
安装依赖项
我们将使用Apollo Server、Express和TypeGraphQL作为该服务器。
使用 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
我们基本上使用 Express 作为 Apollo Server 的中间件集成,使用了apollo-server-express包。所有依赖项安装完成后,创建一个src
文件夹。所有 TS 文件都将存放在这里。这将帮助我们轻松管理编译。
我们还需要一个文件来根据我们的喜好设置 TypeScript。Ben Awadtsconfig.json
编写了一个很棒的实用程序,可以自动生成该文件。运行并选择。现在,我们就可以开始编写 GraphQL API 了!npx tsconfig.json
node
我们将遵循下面描述的文件结构!
├── server
│ ├── src
│ │ ├── entities
│ | | ├── Chat.ts
│ │ ├── resolvers
│ | | ├── chat.ts
│ │ ├── index.ts
│ ├── package.json
│ ├── tsconfig.json
│ ├── .env
构建我们的服务器
创建一个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);
});
让我们分解一下我们刚才写的内容。我们在一个异步函数中初始化了服务器main()
,以备不时之需await
,并声明了一个 express 应用程序。app
我们还允许从 进行 cors 访问localhost:3000
,稍后我们将在这里运行我们的 React 应用。另外,请记住在reflect-metadata
导入type-graphql
任何解析器之前先导入 shim 包。
然后,我们创建了一个新实例ApolloServer
,并将我们的 Express 中间件应用到其中。最后,我们使用 启动了服务器app.listen()
。我们还使用来从文件中dotenv
加载环境变量,即。在本例中,我们将考虑。PORT
.env
PORT=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;
}
好了,让我们理解一下我们刚刚写了什么!我们定义了一个导出的 TypeScript 类,Chat
其中包含多个装饰器。该类有三个属性成员,id
、message
和name
,每个成员都有各自的类型。这很简单,但让我们先来了解一下这些装饰器的作用。
使用 TypeGraphQL 装饰器的主要思想是使用 SDL(模式定义语言)自动从 TypeScript 类创建 GraphQL 模式定义。这样就无需在 TypeScript 中创建模式定义文件及其等效接口。
在这里,我们做的第一件事是Chat
用@ObjectType
装饰器来装饰类。它将类标记为type
来自 GraphQL SDL 或GraphQLObjectType
from graphql-js
。然后,我们声明需要映射到 GraphQL 字段的类属性。为此,我们使用了@Field
装饰器,它也用于从 TypeScript 类型反射系统收集元数据。默认情况下,此处实体中的所有字段都是不可空的!
该实体将导致在 SDL 中生成 GraphQL 模式的以下部分。
type Chat {
id: Float!
message: String!
name: String!
}
正如您所见,!
这里的所有字段都是必填项(),即不可为空!
现在,我们已经成功为每个聊天定义了 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;
}
}
这里有很多新代码,所以让我们先了解一下这里要做什么。除了 GraphQL 对象类型之外,TypeGraphQL 还允许我们以 REST 控制器类型的方式创建 GraphQL 查询、修改和订阅。
首先,我们基于实体定义一个数组Chat
,该数组将充当数据库的作用。然后,我们定义一个导出类ChatResolver
,其中包含方法getChat()
,它返回整个聊天数组,以及,它通过接收参数和createChat()
,将一个新的聊天对象附加到数组末尾。现在我们了解了这个类的常规功能,让我们了解一下这些装饰器添加了什么。name
message
第一个装饰器@Resolver()
使该类的行为类似于经典的 REST 控制器。因此,该类中的方法现在可以像 GraphQL 查询、修改和订阅处理程序一样运行。
这引出了下一个装饰器,即@Query(() => [Chat])
和@Mutation(() => Chat)
,它们允许我们将解析器类方法标记为 GraphQL 查询或突变解析器。我们还需要显式声明这些方法解析的类型,即它们的返回类型,在这里 是一个Chat
对象数组getChats()
, 是一个单个对象createChat()
。
最后,还有一个内联@Arg()
装饰器,它允许我们为特定的 GraphQL 查询/修改指定参数。我们在这个装饰器中传入这些参数的名称。
哇哦!我们的解析器现在可以正常工作了!让我们继续尝试运行服务器!但首先,导入ChatResolver
并index.ts
添加以下脚本package.json
"scripts": {
"watch": "tsc -w",
"dev": "nodemon dist/index.js",
"build": "tsc",
"start": "node dist/index.js"
},
最后,启动你的终端,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
}
}
要获取所有聊天记录,请运行以下查询
query {
getChats {
id
name
message
}
}
如您所见,我们的类方法已变成实际的 GraphQL 操作,它接受参数并返回Chat
对象字段!请记住,由于我们将聊天存储在内存数组中,因此重新启动服务器后,所有聊天记录都会消失。
在下一部分中,我们将探讨如何向我们的新 GraphQL API 添加订阅!
结论
访问本系列的下一篇文章,了解 GraphQL 订阅以及如何添加它们!
如果你想深入了解 GraphQL、Apollo Server 和 TypeGraphQL,并发现可以用它们实现的所有酷炫功能,请阅读官方文档。
此外,这里还有一份 很棒的资源列表, 可供进一步学习!
如果你遇到困难,这里有 包含所有代码的仓库part-1
!访问分支获取本文涵盖的代码。