remix-graphql

remix-graphql

Remix 框架的 GraphQL 集成工具包

remix-graphql 是一个为 Remix 框架设计的 GraphQL 工具包。它提供了处理 loader 和 action 请求、设置本地 schema 和解析器、执行远程 API 查询,以及在资源路由中创建 GraphQL API 等功能。该工具简化了 GraphQL 在 Remix 中的集成过程,有助于提升开发效率。

RemixGraphQLAPIReact数据加载Github开源项目

remix-graphql

RemixGraphQL可以和谐共存❤️ 本包含有基本的实用函数,可以帮助你实现这一点。

更具体地说,最新版本的remix-graphql可以帮助你完成以下任务:

  • 使用GraphQL查询和变更处理loader和action请求
    • 你可以定义本地schema和解析器来处理请求
    • 你也可以对远程API执行GraphQL请求
  • 将GraphQL API设置为资源路由

以下是一些未来可能实现的酷点子:

  • 将多个loader的查询批处理为单个API请求

目录

安装

你可以使用你喜欢的包管理器安装remix-graphql。它依赖于graphql包,所以请确保也安装了该包。

# 使用`npm` npm install graphql remix-graphql # 或使用`yarn` yarn add graphql remix-graphql

它还列出了一些Remix包作为对等依赖。(如果你使用Remix CLI设置项目,你很可能已经安装了它们。)如果遇到意外错误,请再次检查是否安装了以下包:

  • @remix-run/dev
  • @remix-run/react
  • @remix-run/serve
  • remix

如何从remix-graphql导入

此模块不适用于浏览器环境,它仅在服务器上工作。你可以通过从扩展名为.server.js(或.server.ts)的文件中导入,强制Remix编译器永远不会在客户端包中包含remix-graphql的内容。

// 这样做不行,实际上会抛出错误: import { anything } from "remix-graphql"; // 应该这样做: import { anything } from "remix-graphql/index.server";

定义你的schema

remix-graphql保持简单,让你自己决定定义GraphQL schema的最佳方式。在所有需要"将schema传递给remix-graphql"的地方,相应的函数都期望一个GraphQLSchema对象。

这意味着以下所有方法都可以用来定义schema:

  • 使用graphql包中的GraphQLSchema类(显然...)
  • 使用SDL定义schema,在对象中定义解析器函数,并使用makeExecutableSchema(来自@graphql-tools/schema)合并两者
  • 使用nexusmakeSchema

我们建议从一个文件中导出schema,例如app/graphql/schema.server.ts。通过使用.server.ts扩展名,你可以确保这些代码不会被发送到浏览器。(这是对Remix编译器的提示,在构建浏览器包时应忽略此模块。)

使用GraphQL处理loader和action请求

loadersactions都只是简单的函数,给定一个Request返回一个Response。使用remix-graphql,你可以使用GraphQL来处理这个请求!以下是一个完整且可工作的示例,展示了它是如何工作的:

// app/routes/index.tsx import type { GraphQLError } from "graphql"; import { Form } from "remix"; import type { ActionFunction, LoaderFunction } from "@remix-run/node";; import { processRequestWithGraphQL } from "remix-graphql/index.server"; // 从你导出schema的地方导入 import { schema } from "~/graphql/schema"; const ALL_POSTS_QUERY = /* GraphQL */ ` query Posts($limit: Int) { posts(limit: $limit) { id title likes author { name } } } `; export const loader: LoaderFunction = (args) => processRequestWithGraphQL({ // 传递Remix传给loader函数的参数。 args, // 提供你的schema。 schema, // 提供应该执行的GraphQL操作。这也可以是一个mutation, // 它被命名为`query`是为了与通过HTTP发送GraphQL请求的常见命名保持一致。 query: ALL_POSTS_QUERY, // 可选地提供执行操作时应使用的变量。如果不传递,`remix-graphql`将从以下位置派生变量: // - ...路由参数。 // - ...提交的`formData`(如果存在)。 variables: { limit: 10 }, // 可选地传递一个对象,其属性应包含在执行上下文中。 context: {}, // 可选地传递一个函数,为成功执行的操作派生自定义HTTP状态码。 deriveStatusCode( // 执行的结果。 executionResult: ExecutionResult, // 默认情况下会返回的状态码,即如果不传递`deriveStatusCode`函数。 defaultStatusCode: number ) { return defaultStatusCode; }, }); const LIKE_POST_MUTATION = /* GraphQL */ ` mutation LikePost($id: ID!) { likePost(id: $id) { id likes } } `; // `processRequestWithGraphQL`函数可以用于loader和action! export const action: ActionFunction = (args) => processRequestWithGraphQL({ args, schema, query: LIKE_POST_MUTATION }); export default function IndexRoute() { const { data } = useLoaderData<LoaderData>(); if (!data) { return "哎呀,出了点问题 :("; } return ( <main> <h1>博客文章</h1> <ul> {data.posts.map((post) => ( <li key={post.id}> {post.title}(作者:{post.author.name} <br /> {post.likes} 个赞 <Form method="post"> {/* `remix-graphql`会自动将所有提交的表单数据 转换为GraphQL操作的同名变量 */} <input hidden name="id" value={post.id} /> <button type="submit">点赞</button> </Form> </li> ))} </ul> </main> ); } type LoaderData = { data?: { posts: { id: string; title: string; likes: number; author: { name: string }; }[]; }; errors?: GraphQLError[]; };

自动类型生成

在上面示例的末尾,你可以看到从loader函数返回的数据类型必须手动定义。由于GraphQL是强类型的,如果你想的话,可以自动化这个过程!

首先,你需要从你的schema生成内省数据作为JSON,并将其存储在本地文件中。为此,你可以创建一个简单的脚本,如下所示:

// app/graphql/introspection.{js,ts} import fs from "fs"; import { introspectionFromSchema } from "graphql"; import path from "path"; import { schema } from "./schema"; fs.writeFileSync( path.join(__dirname, "introspection.json"), JSON.stringify(introspectionFromSchema(schema)) );

通常,你不希望将生成的JSON文件提交到版本控制中,所以我们建议将其添加到你的.gitignore文件中。

为了更方便地运行这个脚本,在你的package.json中创建一个简单的NPM脚本:

{ "scripts": { // 如果你用JavaScript创建了脚本 "introspection": "node app/graphql/introspection.js", // 如果你用TypeScript创建了脚本(确保在这种情况下安装 // `esbuild-register`作为开发依赖) "introspection": "node --require esbuild-register app/graphql/introspection.ts" } }

要实际从你的查询和变更生成类型,我们推荐使用GraphQL Code Generator。为此,你需要安装几个依赖:

# 使用`npm` npm install --save-dev @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations # 或使用`yarn` yarn add -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-operations

快完成了!现在在你项目的根目录创建一个名为codegen.yml的配置文件,内容如下:

overwrite: true # 之前生成的内省数据存储的路径 schema: "app/graphql/introspection.json" # 匹配所有包含操作定义的文件的glob documents: "app/routes/**/*.{ts,tsx}" generates: # 这是生成的类型将被存储的路径 app/graphql/types.ts: plugins: - "typescript" - "typescript-operations" config: skipTypename: true

现在你终于可以生成类型了!为了方便起见,再添加一个NPM脚本:

{ "scripts": { "introspection": "node --require esbuild-register app/graphql/introspection.ts", "codegen": "npm run introspection && graphql-codegen --config codegen.yml" } }

运行npm run codegen(或yarn codegen)现在将自动为所有查询和变更创建返回数据的类型。(附注:这也是验证所有操作是否对你的schema有效的好方法!)

**还有一件事:**注意到我们在上面的示例中包含查询和变更的字符串之前的/* GraphQL */注释了吗?这很重要!它是对@graphql-codegen的提示,表明这个字符串应该被解析为GraphQL。没有它,你将无法获得字符串中定义的操作的任何类型。

现在可以像这样修改上面的示例:

// 添加这个导入... import type { PostsQuery } from "~/graphql/types"; // ...并像这样更改`LoaderData`类型 type LoaderData = { data?: PostsQuery; errors?: GraphQLError[] };

向远程GraphQL API发送请求

也许你不想把GraphQL API写在Remix应用中,或者你想使用第三方GraphQL API,比如GitHub的公共API。在这两种情况下,remix-graphql都可以帮到你!

// app/routes/$username.tsx import type { GraphQLError } from "graphql"; import type { LoaderFunction } from "@remix-run/node"; import { sendGraphQLRequest } from "remix-graphql/index.server"; const LOAD_USER_QUERY = /* GraphQL */ ` query LoadUser($username: String!) { user(login: $username) { name } } `; export const loader: LoaderFunction = (args) => sendGraphQLRequest({ // 传递Remix传给loader函数的参数。 args, // 提供远程GraphQL API的端点。 endpoint: "https://api.github.com/graphql", // 可选地为请求添加头部。 headers: { authorization: `Bearer ${process.env.GITHUB_TOKEN}` }, // 提供要发送到远程API的GraphQL操作。 query: LOAD_USER_QUERY, // 可选地提供执行操作时应使用的变量。如果不传递,`remix-graphql`将从以下位置派生变量: // - ...路由参数。 // - ...提交的`formData`(如果存在)。 // 这意味着以下是默认值,也可以省略。 variables: args.params, }); export default function UserRoute() { const { data } = useLoaderData<LoaderData>(); if (!data) { return "哎呀,出了点问题 :("; } if (!data.user) { return "找不到用户 :("; } return <h1>{data.user.name}</h1>; } type LoaderData = { data?: { user: { name: string | null; } | null; }; errors?: GraphQLError[]; };

如果你想在加载器中执行比单个 GraphQL 查询更多的操作,完全可以做到!函数 sendGraphQLRequest 会返回对远程 API 进行获取请求的 Response 对象,因此你可以在加载器中根据需要对其进行任何操作。

import { json } from "remix"; import type { LoaderFunction } from "@remix-run/node"; import { sendGraphQLRequest } from "remix-graphql/index.server"; const LOAD_USER_QUERY = /* GraphQL */ ` query LoadUser($username: String!) { user(login: $username) { name } } `; export const loader: LoaderFunction = (args) => { try { const loadUserRes = await sendGraphQLRequest({ args, endpoint: "https://api.github.com/graphql", headers: { authorization: `Bearer ${process.env.GITHUB_TOKEN}` }, query: LOAD_USER_QUERY, }).then((res) => res.json()); /* 你可以在这里执行任何额外的操作... */ const otherStuff = 42; return json({ username: loadUserRes.data.user.name, otherStuff }); } catch { throw new Response("加载数据时出现问题 :("); } };

在 Remix 应用中设置 GraphQL API

你可以使用 Remix 的资源路由为你的 GraphQL API 创建一个专用端点。你只需创建一个路由(例如 app/routes/graphql.ts)并粘贴以下代码。通过同时使用加载器和操作,你的端点可以支持 GET 和 POST 请求!

// app/routes/graphql.ts import { getActionFunction, getLoaderFunction, } from "remix-graphql/index.server"; import type { DeriveStatusCodeFunction } from "remix-graphql/index.server"; // 从你导出的地方导入 schema import { schema } from "~/graphql/schema"; // 处理 GET 请求 export const loader = getLoaderFunction({ // 提供你的 schema schema, // 可选:传递一个对象,其属性应包含在执行上下文中 context: {}, // 可选:传递一个函数,为成功执行的操作派生自定义 HTTP 状态码 deriveStatusCode, }); // 处理 POST 请求 export const action = getActionFunction({ // 提供你的 schema schema, // 可选:传递一个对象,其属性应包含在执行上下文中 context: {}, // 可选:传递一个函数,为成功执行的操作派生自定义 HTTP 状态码 deriveStatusCode, }); // 此函数等同于默认行为 const deriveStatusCode: DeriveStatusCodeFunction = ( // 执行的结果 executionResult, // 默认情况下返回的状态码,即不传递 `deriveStatusCode` 函数时的状态码 defaultStatusCode ) => defaultStatusCode;

上下文

在定义 schema 和编写解析器时,通常会提供一个上下文对象。remix-graphql 导出的所有函数都接受一个可选的 context 属性作为参数对象。如果传递,它必须是一个对象。它的所有属性都将包含在传递给解析器的上下文对象中。

remix-graphql 还导出了一个 Context 类型,包含了添加到执行上下文对象的所有属性。这个类型接受一个可选的泛型,你可以通过它向上下文对象添加任何自定义属性。

import type { PrismaClient } from "@prisma/client"; import type { Context } from "remix-graphql/index.server"; type ContextWithDatabase = Context<{ db: PrismaClient }>;

以下小节突出显示了 remix-graphql 添加到上下文对象的所有属性。

request

这是传递给 Remix 中加载器或操作函数的 Request 对象。它始终是上下文对象的一部分。

redirect

在处理 UI 路由的加载器或操作时,Remix 中的一个常见模式是重定向。(Remix 甚至提供了一个 redirect 实用函数,可以从任何加载器或操作函数返回。)在 remix-graphql 中,你可以通过使用上下文对象中提供的 redirect 函数来实现这一点。

这个函数的签名如下:

function redirect( // 重定向的 URL url: string, // 可选:包含在 HTTP 响应中的头部值 headers?: HeadersInit ): void;

注意,这个函数只在处理 UI 路由中的 GraphQL 请求时才是上下文对象的一部分,即使用 processRequestWithGraphQL 时。在处理资源路由中的 GraphQL 请求时,即使用 getActionFunctiongetLoaderFunction 时,它不是上下文对象的一部分。

编辑推荐精选

讯飞智文

讯飞智文

一键生成PPT和Word,让学习生活更轻松

讯飞智文是一个利用 AI 技术的项目,能够帮助用户生成 PPT 以及各类文档。无论是商业领域的市场分析报告、年度目标制定,还是学生群体的职业生涯规划、实习避坑指南,亦或是活动策划、旅游攻略等内容,它都能提供支持,帮助用户精准表达,轻松呈现各种信息。

AI办公办公工具AI工具讯飞智文AI在线生成PPTAI撰写助手多语种文档生成AI自动配图热门
讯飞星火

讯飞星火

深度推理能力全新升级,全面对标OpenAI o1

科大讯飞的星火大模型,支持语言理解、知识问答和文本创作等多功能,适用于多种文件和业务场景,提升办公和日常生活的效率。讯飞星火是一个提供丰富智能服务的平台,涵盖科技资讯、图像创作、写作辅助、编程解答、科研文献解读等功能,能为不同需求的用户提供便捷高效的帮助,助力用户轻松获取信息、解决问题,满足多样化使用场景。

热门AI开发模型训练AI工具讯飞星火大模型智能问答内容创作多语种支持智慧生活
Spark-TTS

Spark-TTS

一种基于大语言模型的高效单流解耦语音令牌文本到语音合成模型

Spark-TTS 是一个基于 PyTorch 的开源文本到语音合成项目,由多个知名机构联合参与。该项目提供了高效的 LLM(大语言模型)驱动的语音合成方案,支持语音克隆和语音创建功能,可通过命令行界面(CLI)和 Web UI 两种方式使用。用户可以根据需求调整语音的性别、音高、速度等参数,生成高质量的语音。该项目适用于多种场景,如有声读物制作、智能语音助手开发等。

Trae

Trae

字节跳动发布的AI编程神器IDE

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
咔片PPT

咔片PPT

AI助力,做PPT更简单!

咔片是一款轻量化在线演示设计工具,借助 AI 技术,实现从内容生成到智能设计的一站式 PPT 制作服务。支持多种文档格式导入生成 PPT,提供海量模板、智能美化、素材替换等功能,适用于销售、教师、学生等各类人群,能高效制作出高品质 PPT,满足不同场景演示需求。

讯飞绘文

讯飞绘文

选题、配图、成文,一站式创作,让内容运营更高效

讯飞绘文,一个AI集成平台,支持写作、选题、配图、排版和发布。高效生成适用于各类媒体的定制内容,加速品牌传播,提升内容营销效果。

热门AI辅助写作AI工具讯飞绘文内容运营AI创作个性化文章多平台分发AI助手
材料星

材料星

专业的AI公文写作平台,公文写作神器

AI 材料星,专业的 AI 公文写作辅助平台,为体制内工作人员提供高效的公文写作解决方案。拥有海量公文文库、9 大核心 AI 功能,支持 30 + 文稿类型生成,助力快速完成领导讲话、工作总结、述职报告等材料,提升办公效率,是体制打工人的得力写作神器。

openai-agents-python

openai-agents-python

OpenAI Agents SDK,助力开发者便捷使用 OpenAI 相关功能。

openai-agents-python 是 OpenAI 推出的一款强大 Python SDK,它为开发者提供了与 OpenAI 模型交互的高效工具,支持工具调用、结果处理、追踪等功能,涵盖多种应用场景,如研究助手、财务研究等,能显著提升开发效率,让开发者更轻松地利用 OpenAI 的技术优势。

Hunyuan3D-2

Hunyuan3D-2

高分辨率纹理 3D 资产生成

Hunyuan3D-2 是腾讯开发的用于 3D 资产生成的强大工具,支持从文本描述、单张图片或多视角图片生成 3D 模型,具备快速形状生成能力,可生成带纹理的高质量 3D 模型,适用于多个领域,为 3D 创作提供了高效解决方案。

3FS

3FS

一个具备存储、管理和客户端操作等多种功能的分布式文件系统相关项目。

3FS 是一个功能强大的分布式文件系统项目,涵盖了存储引擎、元数据管理、客户端工具等多个模块。它支持多种文件操作,如创建文件和目录、设置布局等,同时具备高效的事件循环、节点选择和协程池管理等特性。适用于需要大规模数据存储和管理的场景,能够提高系统的性能和可靠性,是分布式存储领域的优质解决方案。

下拉加载更多