ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Directus JavaScript SDK 组合式客户端实战:从类型安全的 REST/GraphQL 到实时订阅

Directus JavaScript SDK 组合式客户端实战:从类型安全的 REST/GraphQL 到实时订阅 Directus JavaScript SDK 组合式客户端实战从类型安全的 REST/GraphQL 到实时订阅【免费下载链接】directusThe flexible backend for all your projects Turn your DB into a headless CMS, admin panels, or apps with a custom UI, instant APIs, auth more.项目地址: https://gitcode.com/GitHub_Trending/di/directusDirectus 官方 JavaScript SDKdirectus/sdk是前端应用与 Directus 后端通信的第一方桥梁它将 Directus 的数据库能力封装成一组可插拔、可组合的客户端扩展无需任何第三方依赖即可完成 REST 请求、GraphQL 查询、账号登录与会话刷新、基于 WebSocket 的实时订阅。本文以仓库内 sdk/readme.md 为核心骨架结合源码解析createDirectus、.with()、各 composable 的实现原理帮助你从零搭建一个既类型安全、又按需裁剪体积的 Directus 数据访问层。一、SDK 的三大设计特性directus/sdk位于仓库 sdk 目录对应 npm 包名见 sdk/package.json当前版本 25.0.1核心设计理念可归纳为三点TypeScript First类型优先只要为项目定义了数据集合collection的类型结构SDK 就能在readItems(articles)、client.queryT()、subscribe()等调用中推导出返回数据的类型让查询结果的字段、关系、单例等都在编译期受到约束。模块化架构Modular architectureSDK 按功能拆分为rest、graphql、realtime、authentication、staticToken等多个独立模块你可以“组合”出恰好满足自己需求的客户端其余代码可在构建时被 Tree-shaking 清除。轻量无依赖Lightweight and dependency-freeSDK 不依赖任何外部请求库而是直接基于全局fetch、WebSocket、URL、console等运行时能力工作见 sdk/src/client.ts这让它在浏览器与 Node.js 环境下都能直接运行。包以 ESM/CJS 双格式发布import/require均指向dist见 sdk/package.json从 sdk/src/index.ts 可看到全部公开导出包括 auth、rest、graphql、realtime、schema、types 以及isDirectusError、formatFields、queryToParams等工具函数。二、组合式客户端Composable Client入门SDK 的一切始于createDirectus。它接收 Directus 实例的 URL 和一个可选的options对象返回一个尚未具备任何能力的客户端外壳const client createDirectusSchema(https://directus.example.com);对应源码见 sdk/src/client.tscreateDirectus会保存url包装成URL实例与一组globals默认值fetch、WebSocket、URL、logger这些运行时依赖可通过options.globals按需覆盖例如在测试环境注入自定义的fetchmock或在同构应用里替换WebSocket实现。DirectusClient的核心类型如下sdk/src/types/client.tsexport interface DirectusClientSchema { url: URL; globals: ClientGlobals; with: Extension extends object( createExtension: (client: DirectusClientSchema) Extension, ) this Extension; }返回的with()方法接收一个“扩展工厂函数”把该函数返回的能力浅合并进当前客户端源码中的{ ...this, ...createExtension(this) }。正因为如此功能是按需叠加的——在没有.with(...)之前这个客户端什么都做不了。可用的组合模块一览组合模块为客户端新增的能力源码位置rest().request(...)发起 REST 请求rest/composable.tsgraphql().query(...)发起 GraphQL 请求graphql/composable.tsstaticToken().getToken()/.setToken()静态令牌auth/static.tsauthenticate().login()/.logout()/.refresh().getToken()/.setToken()auth/composable.tsrealtime().subscribe()/.sendMessage()/.onWebSocket(...)等realtime/composable.tsREST 与 GraphQL 混合使用示例原文档给出了一个同时叠加rest与graphql的示例。rest()暴露request(...)它接收一个由命令函数如readItems生成的“命令对象”graphql()则暴露query(...)直接执行字符串形式的 GraphQL 文档const client createDirectusSchema(https://directus.example.com).with(rest()).with(graphql()); // 执行 REST 请求列出 articles 集合 const restResult await client.request(readItems(articles)); // 执行 GraphQL 请求读取文章标题及其作者名 const gqlResult await client.queryOutputType( query { articles { id title author { first_name } } } );从 rest/composable.ts 可以看到request()的真实工作流取出命令中的path/method/params/body补充Content-Type: application/json头若客户端叠加了认证模块且请求头中尚无Authorization则自动调用this.getToken()并写入Bearer token最后经由client.globals.fetch发送它还支持命令级与全局级的onRequest/onResponse拦截钩子可用于统一日志、鉴权注入与响应加工。而 graphql/composable.ts 展示了.query()的签名query(query: string, variables?, scope?: items | system)其中scope决定请求打到/graphql默认面向业务数据还是/graphql/system面向 Directus 系统集合同样会自动携带访问令牌。三、REST 命令体系几乎覆盖全部 REST APIreadItems只是大量“REST 命令”中的一员。从 sdk/src/rest 目录看命令按 HTTP 动词分组覆盖了 Directus 几乎所有端点read读取readItems/readItem/readSingleton、readCollections、readFields、readUsers、readFiles、readActivity、readVersions、aggregate()聚合查询等create / update / delete增删改items、collections、fields、relations、roles、policies、users、files、flows、operations、shares、versions 等资源的对应命令server服务端信息serverPing、serverHealth、serverInfo、serverOpenapi、serverGraphqlschemaSchema 管理schemaSnapshot、schemaDiff、schemaApplyutils杂项文件上传、导入导出、排序、分享链接生成等。以readItems为例其命令工厂rest/commands/read/items.ts会做参数校验空集合名、不允许读取 core 系统集合然后返回{ path: /items/collection, params: query, method: GET }这样的命令对象。命令对象统一实现为“惰性函数”形式() options交由client.request(command)真正执行——这种设计让查询参数如filter、fields、sort、limit、deep可以完整地映射到 URL query string相关实现见 rest/utils/get-request-url.ts 与 utils/query-to-params.ts。需要自定义端点时可以使用customEndpoint()命令构造任意请求或使用withSearch、withToken、withOptions等命令装饰器见 rest/helpers在不写裸fetch的前提下复用令牌注入与请求管线。四、认证两种令牌方案与自动续期原文档展示了两种认证叠加方式方案一邮箱密码登录会话令牌const client createDirectusSchema(https://directus.example.com).with(rest()).with(authentication(json)); await client.login(adminexample.com, d1r3ctu5); // 之后即可发起携带令牌的认证请求方案二静态令牌const client createDirectusSchema(https://directus.example.com) .with(rest()) .with(staticToken(super-secure-token)); // 之后即可发起携带令牌的认证请求两者的差异在源码中一目了然staticTokensdk/src/auth/static.ts只把令牌存在内存闭包里getToken()原样返回、setToken()原地替换适合服务端脚本、CI、BFF 等使用持久令牌Static Token的场景。authentication(mode)sdk/src/auth/composable.ts实现完整的会话生命周期login()依据modejson或cookie默认cookie向/auth/login或/auth/login/provider发送凭证支持在options中携带 OTP 二次验证码与第三方 provider成功后把access_token、refresh_token、expires存入配置的 storage 并计算expires_at。值得深入了解的几个能力点自动续期autoRefresh默认开启配置项为{ msRefreshBeforeExpires: 30000, autoRefresh: true }即令牌到期前 30 秒自动调用/auth/refresh续期由于setTimeout无法处理超过 32 位整数的毫秒值源码用MAX_INT322³¹-1做了保护对有效期超 24 天的令牌不会盲目设置定时器。可插拔存储storage默认使用内存存储memoryStorage()可通过authentication(mode, { storage })注入自定义实现如localStorage持久化从而支持页面刷新后恢复登录态。getToken()的智能刷新每次读取令牌前会先判断是否临近过期若过期则先行refresh()保证后续 REST/GraphQL/WebSocket 请求拿到的总是有效令牌.setToken()也可手动写入外部获取的令牌。登出与停止刷新logout()会调用/auth/logout并清空存储、清理续期定时器stopRefreshing()可在应用退出时显式停止后台续期。令牌在所有模块间是共享的rest()的request()、graphql()的query()都会探测客户端上是否存在getToken存在即自动附上Authorization: Bearer ...头见 rest/composable.ts 与 graphql/composable.ts这正是“先.with(authentication())再.with(rest())”能直接生效的底层原因。五、实时能力Real-Time订阅、收发消息与自动重连realtime()让客户端与 Directus REST WebSocket 端点通信。SDK 会依据主 URL 自动推导 WebSocket 地址若主 URL 本身就是ws:/wss:协议则直接使用否则将https:换成wss:、路径补为/websocket见 realtime/composable.ts也可以在realtime({ url })中显式覆盖。订阅集合变更const client createDirectusSchema(https://directus.example.com).with( realtime({ authMode: public, }), ); const { subscription, unsubscribe } await client.subscribe(test, { query: { fields: [*] }, }); for await (const item of subscription) { console.log(subscription, { item }); } // unsubscribe()subscribe()返回一个包含subscription异步迭代器与unsubscribe退订函数的对象。从源码实现realtime/composable.ts可以确认几个实用细节调用subscribe()时若连接尚未建立会自动触发connect()无需手动await client.connect()每个订阅可携带query内部会被queryToParams转换与event可选create | update | delete不传则接收全部事件加一次init初始快照迭代器会对匹配uid的subscription消息逐条产出事件负载结构定义在 realtime/types.tsinit/create/update为条目数组delete为主键数组连接断开重连成功后会自动重发所有存活订阅。收发原始消息const client createDirectusSchema(https://directus.example.com).with( realtime({ authMode: public, }), ); const stop client.onWebSocket(message, (message) { if (type in message message[type] pong) { console.log(PONG received); stop(); } }); client.sendMessage({ type: ping });onWebSocket(event, callback)支持open、close、error、message四类事件且会返回一个注销函数用于停止监听示例中的stop()。message事件的回调收到的已是解析后的对象若数据是 JSON 字符串会自动JSON.parse。sendMessage()在连接未打开时会抛出“先await client.connect()”的错误提示若发送的对象缺少uidSDK 会自动补齐——这也是服务端把响应关联回调用方的关键。认证模式与连接配置authMode支持三种取值从类型定义可确认realtime/types.tspublic不认证的公开连接适合公开数据的推送handshake默认值连接建立后首条消息发送{ type: auth, access_token }并等待服务端确认源码会在auth/ok之前拒绝连接strict在 WebSocket URL 的查询参数中直接附加access_token。其余可配置项与默认值realtime/composable.ts配置默认值说明authModehandshake认证模式public/handshake/strictheartbeattrue收到服务端ping时自动回pong保活connect.timeout10000毫秒建立连接的超时时间reconnect.delay/reconnect.retries1000/10断线重连的间隔与最大次数可设为false关闭debugfalse是否输出[Directus SDK]前缀的调试日志源码对AUTH_TIMEOUT、AUTH_FAILED、TOKEN_EXPIRED等鉴权错误都有专门处理令牌过期时若存在认证模块会自动用getToken()取新令牌并重新发送auth消息完成静默重认证。服务端对应的消息处理实现位于仓库 api/src/websocket 目录供两侧消息协议对照参考。六、构建你的类型模型Build Your Schema让类型系统为查询兜底SDK 的类型安全依赖一个自定义的Schema类型参数它需要描述项目里每一个集合collection。原文档给出的建模约定非常关键// 主 Schema 类型包含所有可用的集合 interface MySchema { collection_a: CollectionA[]; // 常规集合使用数组类型 collection_b: CollectionB[]; collection_c: CollectionC; // 这是一个单例singleton集合 // 关联表junction本身也是集合 collection_a_b_m2m: CollectionAB_Many[]; collection_a_b_m2a: CollectionAB_Any[]; } // 集合 A interface CollectionA { id: number; status: string; // 各类关系字段 m2o: number | CollectionB; // 多对一可为外键或完整对象 o2m: number[] | CollectionB[]; // 一对多可为外键数组或完整对象数组 m2m: number[] | CollectionAB_Many[]; // 多对多经关联表 m2a: number[] | CollectionAB_Any[]; // 多对任意经多态关联表 } // 多对多关联表 interface CollectionAB_Many { id: number; collection_a_id: CollectionA; collection_b_id: CollectionB; } // 多对任意Many-to-Any关联表 interface CollectionAB_Any { id: number; collection_a_id: CollectionA; collection: collection_b | collection_c; item: string | CollectionB | CollectionC; } // 集合 B interface CollectionB { id: number; value: string; } // 单例集合 interface CollectionC { id: number; app_settings: string; something: string; }建模约定总结常规集合在Schema中是数组类型CollectionA[]单例集合则是单对象类型CollectionC关联表也是集合需要一并建模并在 M2M/M2A 字段中引用关系字段的联合类型number | CollectionB、number[] | CollectionB[]表达“未展开时是外键、fields展开后是对象”的两种查询结果形态多对任意M2A通过collection联合类型收窄指向的集合范围。该Schema会作为泛型贯穿始终createDirectusMySchema(...)之后的client.request(readItems(collection_a))、client.queryT()乃至subscribe()推送的事件负载都会据此推导出精确的返回类型。SDK 提供了ApplyQueryFields、CollectionType、RegularCollections等工具类型参见 sdk/src/types 与ReadItemOutput在 read/items.ts 中的应用可结合fields参数自动推导查询返回的子集类型内置系统集合users、files、roles 等的类型则位于 sdk/src/schema可通过与业务类型交叉合并直接复用。七、安装与后续进阶路径SDK 的 TypeScript 源码即位于仓库根发布产物由 tsdown 构建。在自己的项目中引入时# 使用 npm / pnpm / yarn 安装官方发布包 npm install directus/sdk安装后从directus/sdk入口导入createDirectus、rest、graphql、realtime、authentication、staticToken以及各类 REST 命令函数所有导出见 sdk/src/index.ts。SDK 面向较新的 JS 运行时仓库中package.json声明type: module并要求 Node 22 作为开发/运行环境见 sdk/package.json在浏览器与 Node.js 中均依赖原生fetch/WebSocket。继续深入可以关注以下仓库内资料完整能力导出清单sdk/src/index.ts客户端创建与 globals 设计sdk/src/client.ts、sdk/src/types/client.ts认证生命周期与令牌自动刷新sdk/src/auth/composable.tsREST 命令合集与请求管线sdk/src/restWebSocket 客户端状态机与自动重连sdk/src/realtime/composable.ts类型层约定查询/过滤/字段类型见 sdk/src/types系统集合建模见 sdk/src/schema端到端行为样例sdk/tests含 realtime 与 rest 的测试SDK 规范测试rest/schema 等请求格式基准位于 tests/blackbox 目录把握住“空客户端 .with()按需组合 Schema 泛型 命令对象”这条主线你就能以极小的学习成本把 Directus 变成自己项目里类型可控、体积可控、既可 REST 又可 GraphQL、还能实时推送的数据后端。【免费下载链接】directusThe flexible backend for all your projects Turn your DB into a headless CMS, admin panels, or apps with a custom UI, instant APIs, auth more.项目地址: https://gitcode.com/GitHub_Trending/di/directus创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表