@alxia/graphql (英语)
图QL,用于 阿尔克夏,服务于 图QL 瑜伽。终点是 与其它路线一样: 在应用程序的中间器件后方, 由它的守卫,它的 解析器读取所建的中间软件的上下文 —— 键入并检查. 瑜伽的插件系统是你的: Envelop的插件和瑜伽自己的.
bun add @alxia/graphql graphql-yoga graphql @alxia/core
bun add -d typescript
graphql-yoga 财务报告和审定财务报表 graphql 是对等方: 软件包宣布不依赖.
graphql-ws 是一个可选的,因为 ws 一个人.
graphql 16和17都工作; 与17, graphql-yoga 必须是5.22或
后来,第一个接受. graphql-yoga 5(从5.16起)和6个工作;
6号线的规格 瑜伽6号 @graphql-hive/logger Logger 作为习惯
logging 价值,及其 @graphql-yoga/plugin-* 4.x需要瑜伽6号.
瑜伽本身的要求 ID 是关闭(requestId: false: 客户同意 x-request-id
从来没有回音或记录, 所以应用程序自己的ID是唯一的。 通过
requestId: true 或对象保存 trustHeader: false 除非你设定它.
瑜伽5号被忽略了 requestId。 。 。 . logging 默认为 'warn' 两个主,所以
瑜伽6的每条请求的INFO线路关闭 和从未设置的应用程序 logging 没有
更长的时间可以看到瑜伽的信息行;通过 logging: true 或者说 'info' 为了得到他们.
使用量
import { alxia } from '@alxia/core';
import { graphql, type GraphQLContext } from '@alxia/graphql';
import { bearer } from '@alxia/jwt';
import { createSchema } from 'graphql-yoga';
const base = alxia()
.decorate({ db })
.use(bearer({ jwt, schema: Claims })); // every route after it needs a token
const schema = createSchema<GraphQLContext<typeof base>>({
typeDefs: /* GraphQL */ `type Query { me: String! }`,
resolvers: {
Query: { me: (_, __, { user, db }) => db.name(user.sub) }, // user and db typed
},
});
const app = base.plugin((app) => graphql(app, { schema })); // POST and GET /graphql
app.listen(3000);
graphql(app, options) 添加 GET 财务报告和审定财务报表 POST 路线 path —— 说
/graphql 默认情况下,在应用的前缀下——并返回应用. 鉴于
改为 app.plugin 作为函数,它停留在链中,并看到应用程序的类型.
二. 背景情况
解脱者上下文为瑜伽request, (中文(简体) ), params),应用程序的每个
decorate,每个 derive每一个中间器件的: user, (中文(简体) ), db, (中文(简体) ), log, (中文(简体) ),
requestId - 和 set,通过它设置一个标题或饼干:
login: async (_, { name }, { set }) => {
set.cookies.set('session', await sign(name), { httpOnly: true });
return true;
},
GraphQLContext<typeof app> 是那种类型。 解脱者读出
应用程序未构建的是一个编译错误 :
the schema's resolvers read a context the app does not build: missing user
夹克 (N+1)
一个每个家长都装入记录的字段—— Note.author 超过50个音符,即
51个查询。 构建 数据记录器 内
页:1 context 选项并输入 GraphQLContext第二届
参数。 (约加的) batching 选项,下面是另一回事:
操作在一个 HTTP 请求中 。 )
bun add dataloader
import DataLoader from 'dataloader';
const createLoaders = () => ({
user: new DataLoader(async (ids: readonly string[]) => findUsers(ids)), // one query for all ids
});
type Loaders = ReturnType<typeof createLoaders>;
const schema = createSchema<GraphQLContext<typeof base, { loaders: Loaders }>>({
typeDefs,
resolvers: { Note: { author: (note: { authorId: string }, _, { loaders }) => loaders.user.load(note.authorId) } },
});
const app = base.plugin((app) => graphql(app, { schema, context: () => ({ loaders: createLoaders() }) }));
每个请求, 从未为进程做过一次: 数据记录器缓存它的内容
装入,所以一个共享的 将服务 steal records 并交出一个用户的数据
到下一个。 在韦伯索克特, context 运行每个操作。 见
用 DataLoader 连接。 。 。 .
每个请求一个值
解决许多父母相同价值的字段(类别:
20件)应装入一次. oncePerRequest(context, key, load)
运行 load 第一次请求 key 返回该值
对同一请求的每个后来的呼叫。 缓存随
上下文对象,和 a load 投出或拒绝的,是不被保存的.
import DataLoader from 'dataloader';
import { oncePerRequest } from '@alxia/graphql';
const categories = (context: object) =>
oncePerRequest(context, 'categories', () => new DataLoader(loadCategories)); // one DataLoader per request and key
const category = (item: { categoryId: string }, _args: unknown, context: object) =>
categories(context).load(item.categoryId);
变异会一个接一个地运行它的根子, 一个会改变什么
后一读出. 以 freshPerMutation 所以每个根
变异字段从空缓存开始( 它的子字段共享); 查询
保留一个缓存 :
import { freshPerMutation } from '@alxia/graphql';
const resolvers = freshPerMutation({ Query, Mutation, Ward });
无需依赖: dataloader 留在你身边。 见
每个请求一个值。 。 。 .
瑜伽的插件
每个瑜伽选择都通过,但是 graphqlEndpoint,哪个 path
套装和 graphiql,哪个 ide 决定:
import { useDepthLimit } from '@envelop/depth-limit';
import { useResponseCache } from '@graphql-yoga/plugin-response-cache';
graphql(app, {
schema,
plugins: [useDepthLimit({ maxDepth: 8 }), useResponseCache({ session: ({ request }) => ... })],
maskedErrors: true, // Yoga's default: an error's message never leaks
ide: Bun.env.NODE_ENV === 'development' && 'apollo-sandbox', // at runtime, never process.env: bun build inlines it
batching: true,
});
-
订阅 服务于服务器发送事件, 瑜伽默认值 :
async *subscribe在一个决心中,Accept: text/event-stream编辑 请求; 也在 WebSocket 上ws: true。 。 。 .@alxia/compress默认情况下只留下一个事件流. -
一个IDE 回复 a
GET从端点的浏览器中取出,带有Content-Security-Policy它可以装入 unpkg 上的被绑起的文件 仅此而已,没有受到任何人的指责@alxia/secure-headers继续 以 默认, 服务应用程序中的 GraphiQL 单独 dev(NODE_ENV=development, (中文(简体) ), 或者说alxia({ dev: true })),无;ide在任何地方选择一个:graphql(app, { schema }); // GraphiQL in dev, nothing deployed graphql(app, { schema, ide: 'apollo-sandbox', sandbox: { initialDocument: '{ me }' } });ide无( 默认) 瑜伽的GraphiQL in dev (英语: isDev),没有其他'graphiql'瑜伽的GraphiQL,到处都是; graphiql选择'apollo-sandbox'阿波罗沙盒,嵌入,指向页面打开的地址,前缀包括—— https在终止 TLS 的代理服务器后面( )IDE 指南) (中文(简体) ).sandbox带title, (中文(简体) ),initialDocument, (中文(简体) ),initialHeaders, (中文(简体) ),pollForSchemaUpdates, (中文(简体) ),includeCookiesfalse无 -
(简体中文) 实值
@alxia/cors整个应用程序的,瑜伽本身的关闭,除非cors给出.
运行的操作
每个电话都是 POST /graphql· ; graphql() 告诉周围的观察者
执行的操作。 后面,我们走 use(logger()) 请求的行
携带 operationName 财务报告和审定财务报表 operationType后边
use(telemetry({ … })) 其跨度被命名为 query GetNotes,与
打开遥测 graphql.operation.name 财务报告和审定财务报表 graphql.operation.typeA级
分批体是一行一横, batch和每个名字.
const app = alxia()
.use(logger())
.use(telemetry({ service: 'notes' }))
.plugin((app) => graphql(app, { schema }));
请求
在未执行姓名之前被拒绝。 完毕 ws: true,每个操作
套接字是一条线和一条长度,从它的 subscribe 消息
以错误回答时标出.
见 终点指南
财务报告和审定财务报表 网页标签指南。 。 。 .
网络安全套
ws: true 服务于 WebSocket 上的终点
graphql-transport-ws 议定书 graphql-ws, (中文(简体) ),
阿波罗客户端的默认值 GraphQLWsLink 还有urql的
subscriptionExchange。 。 。 . graphql-ws 是一个可选的对等点; 服务器发送
事件始终如一。 安装它 bun add graphql-ws。 。 。 .
const app = base.plugin((app) => graphql(app, { schema, ws: true })); // ws://…/graphql too
import { createClient } from 'graphql-ws';
const client = createClient({ url: 'wss://api.example.com/graphql', connectionParams: { device: 'web' } });
升级后运行了应用程序的中间软件——一名警卫 401 拒绝
套接字,他们添加的是上下文,除了 connectionParams
发送的客户端——每次操作都经过
瑜伽的插件,然后关闭套接字 1001,并填写
他们的订阅. ws: { path, keepAlive } 移动套接字或空格
它的 p. 见 网页标签指南, (中文(简体) ),
阿波罗客户端和urql的设置包含在内.
订户
import { boundedStream } from '@alxia/graphql';
// Your pub/sub reader and your session check.
declare function subscribeToTopic(topicId: string): AsyncIterable<unknown>;
const subscribe = (
_: unknown,
args: { topicId: string },
ctx: { assertSession(): Promise<void> },
) =>
boundedStream(subscribeToTopic(args.topicId), {
idleMs: 90_000,
totalMs: 10 * 60_000,
checkEveryMs: 30_000,
check: () => ctx.assertSession(),
});
当闲去或老去时 溪流会清净地结束 check 抛出,和
客户端离开后立即释放源. 见
瑜伽的插件和选项。 。 。 .
限制
4个限制 约束一个请求的成本 在任何解析器运行之前。 文档
最多可持有 15,000个纪念品 (单位:千美元)maxTokens, (中文(简体) ), false 无上限: 更长
一个被解析器拒绝为 GRAPHQL_PARSE_FAILED,400下
application/graphql-response+json永远也达不到计划 也许
最多一个巢 1 000级 联 合 国 {, (中文(简体) ), ( 财务报告和审定财务报表 [ (单位:千美元)maxNesting, (中文(简体) ), false 页:1
无限制 : 图ql- js 通过重现来解析并溢出数个堆栈
1000个水平下降,所以更深层的文件 被拒绝的方式相同 之前
帕塞尔再现,从来就不是500。 这算数 {, (中文(简体) ), ( 财务报告和审定财务报表 [ 一起来,
参数和所包括的列表,因此这不是一项深入选择的规则
验证规则 这个
将瑜伽解析器保存在以重量为界的缓存中, 64个小米B 百分比
终点, 1 024 (英语) 文档, 一小时 (单位:千美元)parseCache),即瑜伽的自有之处
没有大小绑定。 A 类 POST 尸体可以持有 1个小米B (单位:千美元)bodyLimit除非
app 声明自己的 bodyLimit() 在此之前 graphql(),保存; 尸体
过去是核心的413 而且从来没有到达瑜伽. bodyLimit 范围 a POST
单独: 一个 WebSocket subscribe 信件以 Bun 为界 maxPayloadLength
(默认为16 MiB) :
graphql(app, {
schema,
maxTokens: 20_000, // false: no cap
maxNesting: 2_000, // 1 000 by default; false: no cap
parseCache: { maxBytes: 32 * 1024 * 1024, max: 512, ttl: 30 * 60_000 }, // false: no cache
bodyLimit: 5 * 1024 * 1024, // 1 MiB by default; a multipart upload needs more
});
拒绝的文件是200 errors 下级 application/json (400名以下)
application/graphql-response+json) (中文(简体) ). 一个错误的选项值丢出一个 TypeError
地点 graphql() 已经宣布.
客户端查询为上千个符;入门查询为163个. 见 要求的代价。 。 。 .
错误、健康和关闭
GraphQL 错误将保留 GraphQL —— 条目 errors[]在200块之内..
时 @alxia/core是因为 alxia({ errors: 'problem' }) 使 HTTP 层
rFC 9457问题:一个警卫的401,一个413,a
五百来. health() 旁边挂起,然后继续 SIGTERM 飞行中的询问
在进程退出前, 订阅结束 :
import { alxia, health } from '@alxia/core';
import { graphql } from '@alxia/graphql';
const app = alxia({ errors: 'problem' })
.plugin(health())
.plugin((app) => graphql(app, { schema }));
app.listen({ port: 4000 });
见 终点指南。 。 。 .
正在进行
graphqlExecutor(options) 运行此过程中的查询或突变,其中
相同的插件、限制、解析缓存和错误掩码为 graphql(),则a
您通过服务器上下文:用于 MCP 工具或任务,而不是浏览器.
import { graphqlExecutor } from '@alxia/graphql';
const execute = graphqlExecutor({ schema, plugins: [useAuth()] });
const { data, errors } = await execute({ query: '{ me { id } }' }, { viewer });
见 指南。 。 。 .
测试
graphqlClient 从 @alxia/graphql/testing 向应用程序发送操作
进行中,通过 app.fetch: 无端口. 这是一个子路径,所以主条目
生产包从不携带.
import { graphqlClient } from '@alxia/graphql/testing';
const client = graphqlClient(app, { headers: { authorization: `Bearer ${token}` } });
const { status, data, errors } = await client.query(
'query ($name: String!) { hello(name: $name) }',
{ variables: { name: 'Ada' } },
);
A 类 TypedDocumentNode 类型 data 和变量. method: 'GET' 发送
uRL中的操作; persisted 使一尘不染地,
当应用程序不允许其他任何内容时( 您命名 ) TData);客户端添加了否
证监会头条,把你的 headers数字 :
const result = await client.query<{ hello: string }>({ persisted: hash, method: 'GET' });
见 测试指南。 。 。 .
API 密码
| 导出 | |
|---|---|
graphql(app, options) | 终点 : schema, (中文(简体) ), path, (中文(简体) ), ide, (中文(简体) ), sandbox, (中文(简体) ), ws, (中文(简体) ), maxTokens, (中文(简体) ), maxNesting, (中文(简体) ), parseCache, (中文(简体) ), bodyLimit,每个瑜伽选择 |
graphqlExecutor(options) | (request: { query, operationName?, variables? }, context, { signal? }?) => Promise<{ data?, errors? }>: 正在通过下列选项进行查询或突变: graphql() (没有路线)path, (中文(简体) ), ide, (中文(简体) ), ws, (中文(简体) ), bodyLimit, (中文(简体) ), cors(中文(简体) ) |
ExecutorRequest, (中文(简体) ), ExecutorResult, (中文(简体) ), ExecutorError, (中文(简体) ), ExecutorCall, (中文(简体) ), GraphQLExecutor, (中文(简体) ), GraphQLExecutorOptions | 类型 |
GraphQLWsOptions | ws选项 : path, (中文(简体) ), keepAlive |
GraphQLWsContext | 在WebSocket上的操作增加了上下文的内容: connectionParams |
renderSandbox(endpoint, options?), (中文(简体) ), SANDBOX_POLICY | sandbox 页面和它装入的策略 |
SandboxOptions | 其选项: title, (中文(简体) ), initialDocument, (中文(简体) ), initialHeaders, (中文(简体) ), pollForSchemaUpdates, (中文(简体) ), includeCookies |
GraphQLContext<App, UserContext?> | 解脱者读出 |
oncePerRequest(request, key, load) | load()'值,每个请求对象最多一次计算 key 联合国 string; 返回 T一言为定, 一言不发 |
clearRequestCache(request) | 虚空什么 oncePerRequest 为请求对象保留; 下一次调用负载; 返回 void |
freshPerMutation(resolvers) | 与下列函数相同的解析器(相同的类型) Mutation 包起来打电话 clearRequestCache(context) 首先:每个根突变字段开始为空,它的子字段共享,一个查询未受影响 |
boundedStream(source, options) | source (单位:千美元) AsyncIterable 或者说 AsyncIterator作为 AsyncIterableIterator<T> 订阅费 subscribe: 结束于 idleMs 没有事件 totalMs 总之,运行 check 每个 checkEveryMs ,在最后一个检查是 checkDebounceMs 老了,扔东西 mapError(error) 何时 check 失败或源本身抛出,并调用源的 return() 连接或任何端口 |
BoundedStreamOptions | 其选项: idleMs, (中文(简体) ), totalMs, (中文(简体) ), checkEveryMs, (中文(简体) ), checkDebounceMs?, (中文(简体) ), check, (中文(简体) ), mapError?, (中文(简体) ), signal? |
ServerContext<Ctx>, (中文(简体) ), GraphQLOptions | 类型 |
graphqlClient(app, { path?, headers?, method? }) (单位:千美元)@alxia/graphql/testing(中文(简体) ) | query(document, { variables?, operationName?, headers?, method?, extensions?, persisted? }),或 query({ persisted }) 没有文档,正在处理中: { status, data?, errors?, response } |
GraphQLClient, (中文(简体) ), GraphQLResult, (中文(简体) ), QueryDocument, (中文(简体) ), QueryOptions, (中文(简体) ), PersistedQueryOptions, (中文(简体) ), HttpMethod, (中文(简体) ), GraphQLClientOptions, (中文(简体) ), ResponseError (单位:千美元)@alxia/graphql/testing(中文(简体) ) | 类型 |
文档
- 指南: 每一区域一个页面——挂起终点,打出上下文,瑜伽的插件和选项,GraphQL over WebSocket,运行中的操作,GraphiQL和Apollo Sandbox,以及 为生产加固API (速率极限,深度极限,内向,出错,体积,CSRF,持续操作).
- 解决问题:一个错误消息,以及该如何应对.
- 路线图:即将到来的,以及没有计划的.
- 食谱数字 : 图形QL API, (中文(简体) ), 认证请求, (中文(简体) ), 测试 alxia 应用程序, (中文(简体) ), 健康检查和优雅的关闭还有更多.