飞布以开发者体验优先,为提升开发者procode的效率,根据模板实时生成各语言的SDK,且支持用户自行扩展。
飞布的SDK包含两种类型:服务端钩子SDK和客户端SDK。
服务端钩子SDK:生成的代码运行在服务端上,主要用途是钩子开发,详情见 钩子概览
客户端SDK:生成调用REST API的客户端SDK,主要运行在浏览器中
安装客户端SDK
安装客户端SDK和安装钩子类似。
在状态栏中有一项叫做“客户端模板 ”,默认为0。点击后,可打开模板页,默认为空。点击右上角“浏览模板市场”,打开模板下载页,选择客户端模板。
我们以ts-client
客户端模板为例。
1,在模板下载页,选择 ts-client
,点击下载
1.1 Fireboom将从下述仓库中下载模板:
不同Fireboom版本,下载模板的分支不同!
1.2 下载后,将在 template
目录下增加ts-client
目录
其生成规则和 钩子SDK 一致,详情参考 安装钩子
2,在模板页修改“生成路径”,并开启开关
3,后续每次触发“编译”,都会重新生成文件(非 .hbs
文件,只生成1次 )
复制 sdk
└─ ts-client
├─ claims.ts
├─ client.ts
├─ index.ts # 入口文件,暴露 client
└─ models.ts # 结构体定义
4,在客户端引入该SDK即可使用
复制 import { client } from "./ts-client"
使用SDK
ts-client
是Fireboom HTTP协议 在TypeScript中的基本实现,可以在浏览器和服务器环境中使用。它被用作Web客户端实现的基本接口。
引用SDK
配置基本请求头
在index.ts文件中增加baseURL配置,自定义访问域名。
复制 import { createClient } from './client' ;
export const client = createClient ({
// 如果需要修改访问域名,可以在这里配置
baseURL : 'https://my-custom-base-url.com' ,
});
Nodejs支持
使用customFetch配置选项,在没有内置fetch实现的服务器环境中使用SDK。
安装node-fetch:
并将其添加到SDK配置中。
复制 import fetch from 'node-fetch' ;
const client = createClient ({
customFetch : fetch ,
});
自定义请求头
复制 const client = createClient ({
extraHeaders : {
customHeader : 'value' ,
} ,
});
// or
client .setExtraHeaders ({
customHeader : 'value' ,
});
查询和变更
查询
复制 const response = await client .query ({
operationName : 'Hello' ,
input : {
hello : 'World' ,
} ,
});
变更
复制 const response = await client .mutate ({
operationName : 'SetName' ,
input : {
name : 'fireboom' ,
} ,
});
实时
实时查询
复制 client .subscribe (
{
operationName : 'Hello' ,
input : {
name : 'World' ,
} ,
liveQuery : true , // 开启实时查询
} ,
(response) => {}
);
订阅
复制 client .subscribe (
{
operationName : 'Countdown' ,
input : {
from : 100 ,
} ,
} ,
(response) => {}
);
订阅一次
使用subscribeOnce
运行订阅,这将直接返回订阅响应,而不会推流。适用于SSR场景。
复制 const response = await client .subscribe (
{
operationName : 'Countdown' ,
input : {
from : 100 ,
} ,
subscribeOnce : true ,
} ,
(response) => {}
);
文件上传
复制 async function handleFiles (e : Event ) {
const files = ( e .target as HTMLInputElement )?.files;
if (files == null ) return
// 调用上传函数
const res = await client .uploadFiles ({
provider : "tengxunyun" , // 供应商名称
files : files ,
profile : "avatar" , // (可选)高级配置项
directory : "sss" , // (可选)上传目录
meta : { // (可选)meta信息
postId : "sss"
}
})
console .log ( "https://test-1314985928.cos.ap-nanjing.myqcloud.com/" + res .fileKeys[ 0 ])
}
身份验证
身份认证包含两种模式:授权码模式(基于cookie)和隐式模式(基于token)。
授权码模式
登录
获取用户
复制 const user = await client .fetchUser ();
退出登录
复制 client .logout ({
logoutOpenidConnectProvider : true ,
});
隐式模式
获取Token
隐式模式登录的核心是获取access_token
,有两种模式:使用OIDC登录页 和不使用登录页 。
一般不使用OIDC的登录页,而是根据各供应商的实现,调用其登录接口获取access_token,例如:
使用Token
复制 export const client = createClient ({
extraHeaders : {
// 这里注入请求头
Authorization : 'Bearer <access_token>' ,
} ,
})
CSRF保护
// todo
中断请求
如果要中断请求,请使用AbortController实例。
复制 const controller = new AbortController ();
const { fileKeys } = await client .uploadFiles ({
abortSignal : abortController .signal ,
provider : S3Provider .minio ,
files ,
});
// cancel the request
controller .abort ();
错误处理
OPERATION API错误分为3类:InputValidationError
、GraphQLResponseError
、ResponseError
。
InputValidationError
状态码为400
时,表示入参校验错误,入参不符合规范,或不满足@jsonSchema
指令校验规则。
复制 # statusCode= 400
{
"code" : "InputValidationError" ,
"message" : "Bad Request: Invalid input" ,
"input" : {
"id" : 11
} ,
"errors" : [
{
"propertyPath" : "/id" ,
"invalidValue" : 11 ,
"message" : "must be less than or equal to 10"
}
]
}
GraphQLResponseError
状态码为200
,但执行OPERATION失败,常见错误场景如下:
1,数据源中断
复制 # statusCode= 200
{
"errors" : [
{
"message": "Error in connector: Error creating a database connection. (Error creating a database connection.)",
"locations" : null ,
"path" : null
}
] ,
"data" : {
"rb_findUniqueT" : null
}
}
2,主键冲突
复制 # statusCode= 200
{
"errors" : [
{
"message": "Error occurred during query execution:\nConnectorError(ConnectorError { user_facing_error: Some(KnownError { message: \"Unique constraint failed on the constraint: `PRIMARY`\", meta: Object {\"target\": String(\"PRIMARY\")}, error_code: \"P2002\" }), kind: UniqueConstraintViolation { constraint: Index(\"PRIMARY\") } })",
"locations" : null ,
"path" : null
}
] ,
"data" : {
"rb_createOneT" : null
}
}
ResponseError
状态码非200时的其他错误场景,例如:
用例
复制 import {GraphQLResponseError , ResponseError , InputValidationError} from "fireboom-wundersdk/client" ;
const { data , error } = await client .query ({
operationName : 'Hello' ,
input : {
hello : 'World' ,
} ,
});
console .log (data)
if (error != undefined ) {
if (error instanceof InputValidationError ) {
// 1,处理入参校验错误
console .log ( "meet InputValidationError:" , error .message)
} else if (error instanceof GraphQLResponseError ) {
// 2,处理执行OPERATION错误
console .log ( "meet GraphQLResponseError:" , error .errors[ 0 ].location)
} else if (error instanceof ResponseError ) {
// 3,处理其他错误
console .log ( "meet ResponseError:" , error .statusCode)
} else {
console .log ( "meet UnknownError:" , JSON .stringify (error))
}
}
参考