// 1import { createServer } from "@graphql-yoga/node";// 2const port = Number(process.env.API_PORT) || 4000// 3const server = createServer({ port});// 4server.start().then(() => { console.log(?? GraphQL Server ready at http://localhost:</span><span class="token template-string interpolation interpolation-punctuation punctuation">${</span><span class="token template-string interpolation">port</span><span class="token template-string interpolation interpolation-punctuation punctuation">}</span><span class="token template-string string">/graphql);});

上面的代碼執(zhí)行以下操作:

  1. 從 GraphQL Yoga 導(dǎo)入函數(shù)createServer
  2. 創(chuàng)建一個(gè)變量來(lái)保存 API 的端口,如果環(huán)境中不存在 API,則默認(rèn)為4000
  3. 創(chuàng)建 GraphQL 服務(wù)器的實(shí)例
  4. 在端口上啟動(dòng)服務(wù)器,并讓控制臺(tái)知道它已啟動(dòng)并正在運(yùn)行4000

如果您啟動(dòng)服務(wù)器,您將可以訪問(wèn)正在運(yùn)行的(空的)GraphQL API:

npm run dev

注意:GraphQL 服務(wù)器已啟動(dòng)并正在運(yùn)行,但不可用,因?yàn)槟形炊x任何查詢或更改。

設(shè)置 Schema 構(gòu)建器

GraphQL 使用強(qiáng)類型架構(gòu)來(lái)定義用戶如何與 API 交互以及應(yīng)返回哪些數(shù)據(jù)。構(gòu)建 GraphQL 架構(gòu)有兩種不同的方法:代碼優(yōu)先和 SDL 優(yōu)先。

在本應(yīng)用程序中,您將使用名為 Pothos 的流行架構(gòu)構(gòu)建器采用代碼優(yōu)先方法。

要開始使用 Pothos,您首先需要安裝 core 包:

npm i @pothos/core

接下來(lái),創(chuàng)建 Pothos 架構(gòu)構(gòu)建器的實(shí)例作為可共享模塊。在該文件夾中,創(chuàng)建一個(gè)名為 for will hold this module的新文件:srcbuilder.ts

cd src
touch builder.ts

現(xiàn)在,從包中導(dǎo)入默認(rèn)導(dǎo)出并導(dǎo)出名為 :@pothos/corebuilder

// src/builder.ts

import SchemaBuilder from "@pothos/core";export const builder = new SchemaBuilder({});

定義標(biāo)量類型DATE

默認(rèn)情況下,GraphQL 僅支持一組有限的標(biāo)量數(shù)據(jù)類型:

但是,如果您回想一下 Prisma 架構(gòu),您會(huì)記得定義了一些使用數(shù)據(jù)類型的字段。要在 GraphQL API 中處理這些問(wèn)題,您需要定義自定義標(biāo)量類型。DateTimeDate

幸運(yùn)的是,由于開源社區(qū),可以使用預(yù)制的自定義標(biāo)量類型定義。您將使用的稱為graphql-scalars:

npm i graphql-scalars

您需要向架構(gòu)構(gòu)建器注冊(cè)標(biāo)量,讓它知道如何處理日期。架構(gòu)構(gòu)建器采用一個(gè)通用的,您可以在其中指定各種配置。Date

進(jìn)行以下更改以注冊(cè)標(biāo)量類型:Data

// src/builder.ts

import SchemaBuilder from "@pothos/core";// 1import { DateResolver } from "graphql-scalars";
// 2export const builder = new SchemaBuilder<{ Scalars: { Date: { Input: Date; Output: Date }; };}>({});
// 3builder.addScalarType("Date", DateResolver, {});

以下是上面代碼段中的更改內(nèi)容。你:

  1. 導(dǎo)入標(biāo)量類型的解析程序,該解析程序處理在 API 中將值轉(zhuǎn)換為正確的日期類型Date
  2. 注冊(cè)一個(gè)名為 using the configuration 的新標(biāo)量類型,并配置在訪問(wèn)和驗(yàn)證此類型的字段時(shí)要使用的 JavaScript 類型"Date"SchemaBuilderScalars
  3. 通過(guò)提供導(dǎo)入的DateDateResolver

在 GraphQL 對(duì)象類型和解析程序中,現(xiàn)在可以使用標(biāo)量類型。Date

添加 Pothos Prisma 插件

您需要做的下一件事是定義 GraphQL 對(duì)象類型。這些定義 API 將通過(guò)查詢公開的對(duì)象和字段。

Pothos 有一個(gè)很棒的 Prisma?插件,它使這個(gè)過(guò)程更加順暢,并在 GraphQL 類型和數(shù)據(jù)庫(kù)架構(gòu)之間提供類型安全。

注意:Pothos 可以在 Prisma 中以類型安全的方式使用,而無(wú)需使用插件,但是該過(guò)程非常手動(dòng)。

首先,安裝插件:

npm i @pothos/plugin-prisma

此插件提供了一個(gè) Prisma 生成器,可生成 Pothos 所需的類型。在以下位置將生成器添加到您的 Prisma 架構(gòu)中:prisma/schema.prisma

// prisma/schema.prisma
generator client { provider = "prisma-client-js"}
+generator pothos {+ provider = "prisma-pothos-types"+}
datasource db { provider = "postgresql" url = env("DATABASE_URL")}
model User { id Int @id @default(autoincrement()) name String createdAt DateTime @default(now()) messages Message[]}
model Message { id Int @id @default(autoincrement()) body String createdAt DateTime @default(now()) userId Int user User @relation(fields: [userId], references: [id])}

添加后,您將需要一種方法來(lái)生成 Pothos 的神器。在本系列的后面部分,每次部署此應(yīng)用程序時(shí),您都需要安裝此 API 的節(jié)點(diǎn)模塊并重新生成 Prisma Client,因此請(qǐng)繼續(xù)創(chuàng)建一個(gè)新的 in 來(lái)處理此問(wèn)題:scriptpackage.json

// package.json

{ // ... "scripts": { // ... "build": "npm i && npx prisma generate" }}

現(xiàn)在你可以運(yùn)行該命令來(lái)安裝你的 node 模塊并重新生成 Prisma 客戶端和 Pothos 輸出:

npm run build

當(dāng)您運(yùn)行上述命令時(shí),您應(yīng)該會(huì)看到 Prisma Client 和 Pothos 集成都已生成。

現(xiàn)在這些類型已生成,請(qǐng)前往 。在這里,你將導(dǎo)入和生成的 Pothos 類型,并將它們應(yīng)用于你的構(gòu)建器:src/builder.tsPrismaPlugin

// src/builder.ts
import SchemaBuilder from "@pothos/core";import { DateResolver } from "graphql-scalars";+import PrismaPlugin from "@pothos/plugin-prisma";+import type PrismaTypes from "@pothos/plugin-prisma/generated";
export const builder = new SchemaBuilder<{ Scalars: { Date: { Input: Date; Output: Date }; };}>({});
builder.addScalarType("Date", DateResolver, {});

添加生成的類型后,您會(huì)注意到 .SchemaBuilder

Pothos 足夠聰明,知道因?yàn)槟闶褂玫氖?Prisma 插件,所以你需要向構(gòu)建器提供一個(gè)實(shí)例。Pothos 使用它來(lái)推斷有關(guān) Prisma Client 中的類型的信息。在下一步中,您將創(chuàng)建該實(shí)例并將其添加到構(gòu)建器中。prisma

現(xiàn)在,在 builder 實(shí)例中注冊(cè) Prisma 插件和生成的類型,讓 Pothos 知道它們:

// src/builder.ts// ...
export const builder = new SchemaBuilder<{ Scalars: { Date: { Input: Date; Output: Date }; };+ PrismaTypes: PrismaTypes;}>({+ plugins: [PrismaPlugin],});
// ...

此時(shí),您將再次看到 TypeScript 錯(cuò)誤。這是因?yàn)楝F(xiàn)在需要為函數(shù)提供 Prisma Client 的實(shí)例。builder

在下一步中,您將實(shí)例化 Prisma 客戶端,并在 .builder

創(chuàng)建 Prisma Client 的可重用實(shí)例

您現(xiàn)在需要?jiǎng)?chuàng)建一個(gè)可重用的 Prisma Client 實(shí)例,該實(shí)例將用于查詢您的數(shù)據(jù)庫(kù),并提供上一步中構(gòu)建器所需的類型。

在名為 的文件夾中創(chuàng)建一個(gè)新文件。srcdb.ts

touch src/db.ts

在該文件中,導(dǎo)入 Prisma Client 并創(chuàng)建名為 的客戶端實(shí)例。導(dǎo)出該實(shí)例化的客戶端:prisma

// src/db.ts

import { PrismaClient } from "@prisma/client";export const prisma = new PrismaClient();

將變量導(dǎo)入并提供它以擺脫 TypeScript 錯(cuò)誤:prismasrc/builder.tsbuilder

// src/builder.ts
// ...
+import { prisma } from "./db";
export const builder = new SchemaBuilder<{ Scalars: { Date: { Input: Date; Output: Date }; }; PrismaTypes: PrismaTypes;}>({ plugins: [PrismaPlugin],+ prisma: {+ client: prisma,+ },});
// ...

Pothos Prisma 插件現(xiàn)已完全配置并準(zhǔn)備就緒。這采用 Prisma 生成的類型,并允許您在 GraphQL 對(duì)象類型和查詢中輕松訪問(wèn)這些類型。

最酷的是,您現(xiàn)在有一個(gè)單一的事實(shí)來(lái)源(Prisma 架構(gòu))來(lái)處理數(shù)據(jù)庫(kù)中的類型、用于查詢數(shù)據(jù)庫(kù)的 API 以及 GraphQL 架構(gòu)。

接下來(lái),您將看到它的實(shí)際效果!

定義 GraphQL 類型

此時(shí),您將使用通過(guò) Prisma 插件配置的構(gòu)建器定義 GraphQL 對(duì)象類型。

注意:如果您已經(jīng)在 Prisma 架構(gòu)中定義了數(shù)據(jù)的形狀,那么手動(dòng)定義 GraphQL 對(duì)象類型似乎是多余的。Prisma 架構(gòu)定義數(shù)據(jù)庫(kù)中數(shù)據(jù)的形狀,而 GraphQL 架構(gòu)定義 API 中可用的數(shù)據(jù)。

在 中創(chuàng)建一個(gè)名為 的新文件夾。然后在該新文件夾中創(chuàng)建一個(gè)文件:srcmodelsUser.ts

mkdir src/models
touch src/models/User.ts

在這里,您將定義將通過(guò) GraphQL API 公開的對(duì)象類型及其相關(guān)查詢。導(dǎo)入實(shí)例:Userbuilder

// src/models/User.ts

import { builder } from "../builder";

由于您使用的是 Pothos 的 Prisma 插件,因此該實(shí)例現(xiàn)在有一個(gè)名為builderprismaObject您將用于定義對(duì)象類型。

該方法采用兩個(gè)參數(shù):

  1. name:此新類型表示的 Prisma 模型的名稱
  2. options:正在定義的類型的配置

使用該方法創(chuàng)建類型:"User"

// src/models/User.ts
import { builder } from "../builder";
+builder.prismaObject("User", {})

注意:如果您在字段中輸入之前在一組空引號(hào)中按 +,您應(yīng)該會(huì)獲得一些不錯(cuò)的自動(dòng)完成功能,其中包含 Prisma 架構(gòu)中的可用模型列表,這要?dú)w功于 Prisma 插件。CtrlSpacename

在對(duì)象中,添加一個(gè)使用 Pothos 的 “expose” 函數(shù)定義 和 字段的鍵:optionsfieldsidnamemessages

// src/models/User.ts

import { builder } from "../builder";
builder.prismaObject("User", { fields: t => ({ id: t.exposeID("id"), name: t.exposeString("name"), messages: t.relation("messages") })})

注意:當(dāng)您開始鍵入字段名稱時(shí),按 + 將為您提供目標(biāo)模型中與您正在使用的 “expose” 函數(shù)的數(shù)據(jù)類型匹配的字段列表。CtrlSpace

上面的函數(shù)定義了一個(gè) GraphQL 類型定義,并在實(shí)例中注冊(cè)了它。從 生成架構(gòu)實(shí)際上并不會(huì)將 GraphQL 架構(gòu)存儲(chǔ)在您可以查看的文件系統(tǒng)中,但是生成的類型定義將如下所示:builderbuilderUser

type User {
id: ID! messages: [Message!]! name: String!}

接下來(lái),在同一文件夾中添加另一個(gè)名為 :Message.ts

touch Message.ts

此文件與文件類似,不同之處在于它將定義模型。User.tsMessage

定義 、 和 字段。請(qǐng)注意,該字段在您的 Prisma 架構(gòu)中具有類型,并且需要一個(gè)自定義配置來(lái)定義您定義的自定義標(biāo)量類型:idbodycreatedAtcreatedAtDateTimedate

// src/models/Message.ts

import { builder } from "../builder";
builder.prismaObject("Message", { fields: (t) => ({ id: t.exposeID("id"), body: t.exposeString("body"), createdAt: t.expose("createdAt", { type: "Date", }), }),});

此函數(shù)將生成以下 GraphQL 對(duì)象類型:

type Message {
body: String! createdAt: Date! id: ID!}

實(shí)施您的查詢

目前,您已為 GraphQL 架構(gòu)定義了對(duì)象類型,但您尚未定義實(shí)際訪問(wèn)該數(shù)據(jù)的方法。為此,您首先需要初始化一個(gè)Query類型.

在文件底部,使用 的 函數(shù)初始化類型:src/builder.tsQuerybuilderqueryType

// src/builder.ts

// ...
builder.queryType({});

這將注冊(cè)一個(gè)特殊的 GraphQL 類型,該類型包含每個(gè)查詢的定義,并充當(dāng) GraphQL API 的入口點(diǎn)。您可以在文件中定義此類型,以確保查詢生成器定義了類型,這樣您以后就可以向其添加查詢字段。builder.tsQuery

在此函數(shù)中,您可以直接添加查詢定義,但是,您將在代碼庫(kù)中單獨(dú)定義這些定義,以便更好地組織代碼。queryType

將實(shí)例導(dǎo)入到 :prismasrc/models/User.ts

// src/models/User.ts
import { builder } from "../builder";+import { prisma } from "../db";
// ...

然后,使用 的builderqueryField函數(shù)中,定義一個(gè)公開您定義的對(duì)象類型的查詢:"users"User

// src/models/User.ts
// ...
// 1builder.queryField("users", (t) => // 2 t.prismaField({ // 3 type: ["User"], // 4 resolve: async (query, root, args, ctx, info) => { return prisma.user.findMany({ ...query }); }, }));

上面的代碼段:

  1. 向 GraphQL 架構(gòu)的類型添加一個(gè)名為Query"users"
  2. 定義一個(gè)字段,該字段解析為 Prisma 架構(gòu)中的某種類型
  3. 讓 Pothos 知道此字段將解析為您的 Prisma Client 類型的數(shù)組User
  4. 為此字段設(shè)置解析程序功能。

注意:函數(shù)的參數(shù)位于參數(shù)列表的開頭。這是 Pothos 在使用用于以高性能方式加載數(shù)據(jù)和關(guān)系的函數(shù)時(shí)填充的特定字段。如果您來(lái)自 GraphQL 背景,這可能會(huì)造成混淆,因?yàn)樗鼤?huì)更改參數(shù)的預(yù)期順序。resolvequeryprismaField

為了更好地可視化所發(fā)生的事情,以下是本節(jié)中的代碼將生成的類型和查詢:Queryusers

type Query {
users: [User!]!}

應(yīng)用 GraphQL 架構(gòu)

現(xiàn)在,您已經(jīng)定義并實(shí)施了所有 GraphQL 對(duì)象類型和查詢。需要的最后一部分是將所有這些類型和查詢注冊(cè)到一個(gè)位置,并根據(jù)您的配置生成 GraphQL 架構(gòu)。

在 中創(chuàng)建一個(gè)名為 的新文件 。srcschema.ts

touch schema.ts

此文件將簡(jiǎn)單地導(dǎo)入模型,導(dǎo)致文件中的代碼運(yùn)行,并運(yùn)行實(shí)例的函數(shù)以生成 GraphQL 架構(gòu):buildertoSchema

// src/schema.ts

import { builder } from "./builder";
import "./models/Message";import "./models/User";
export const schema = builder.toSchema({});

該函數(shù)生成 GraphQL 架構(gòu)的抽象語(yǔ)法樹 (AST) 表示形式。下面,您可以看到 AST 和 GraphQL 表示形式的樣子:toSchemaGraphQLAST

scalar Date

type Message { body: String! createdAt: Date! id: ID!}
type Query { users: [User!]!}
type User { id: ID! messages: [Message!]! name: String!}

在您的文件中,導(dǎo)入您剛剛創(chuàng)建的變量。該函數(shù)的配置對(duì)象采用一個(gè)名為 key 的 key,該 key 將接受生成的 GraphQL 架構(gòu):src/index.tsschemacreateServerschema

// src/index.ts
import { createServer } from "@graphql-yoga/node";+import { schema } from "./schema";
const port = Number(process.env.API_PORT) || 4000
const server = createServer({ port,+ schema,});
server.start().then(() => { console.log(?? GraphQL Server ready at http://localhost:</span><span class="token template-string interpolation interpolation-punctuation punctuation">${</span><span class="token template-string interpolation">port</span><span class="token template-string interpolation interpolation-punctuation punctuation">}</span><span class="token template-string string">/graphql);});

匪夷所思!您的 GraphQL 架構(gòu)是使用代碼優(yōu)先方法定義的,您的 GraphQL 對(duì)象和查詢類型與您的 Prisma 架構(gòu)模型同步,并且您的 GraphQL 服務(wù)器正在獲得生成的 GraphQL 架構(gòu)。

此時(shí),請(qǐng)運(yùn)行服務(wù)器,以便您可以使用 API:

npm run dev

運(yùn)行上述命令后,在瀏覽器中打開?http://localhost:4000/graphql?以訪問(wèn) GraphQL Playground。您應(yīng)該會(huì)看到一個(gè)如下所示的頁(yè)面:

在屏幕的左上角,點(diǎn)擊 Explorer 按鈕以查看 API 的可用查詢和更改:

如果您單擊 users 查詢類型,屏幕右側(cè)將自動(dòng)填充對(duì)用戶數(shù)據(jù)的查詢。

通過(guò)點(diǎn)擊 “execute query” 按鈕來(lái)運(yùn)行該查詢,以查看 API 的運(yùn)行情況:

隨意使用不同的選項(xiàng)來(lái)選擇要查詢的字段以及要包含的 “messages” 關(guān)系中的哪些數(shù)據(jù)。

總結(jié)和下一步是什么

在本文中,您構(gòu)建了整個(gè) GraphQL API。該 API 是利用 Prisma 生成的類型以類型安全的方式構(gòu)建的。這些與 Pothos Prisma 插件一起,使您能夠確保 ORM 中的類型、GraphQL 對(duì)象類型、GraphQL 查詢類型和解析程序都與數(shù)據(jù)庫(kù)架構(gòu)同步。

在此過(guò)程中,您需要:

原文來(lái)源:https://www.prisma.io/blog/e2e-type-safety-graphql-react-3-fbV2ZVIGWg

上一篇:

如何使用 PostgREST 和 Apache APISIX 構(gòu)建高效、安全的 RESTful API 解決方案

下一篇:

Spring Boot中API集成的多種方法
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)