@ tcute/ 客户端
aT协议的轻量级和可爱的API客户端.
npm install @atcute/client @atcute/bluesky
先决条件
客户端需要一个定义包,以了解有哪些查询和程序。 安装 与客户一起:
npm install @atcute/client @atcute/bluesky
然后使用其中一种方法登记类型定义:
// tsconfig.json
{
"compilerOptions": {
"types": ["@atcute/bluesky"],
},
}
// env.d.ts
/// <reference types="@atcute/bluesky" />
// or as an import in your entrypoint
import type {} from '@atcute/bluesky';
现在,XRPC方法将掌握注册计划的全部类型信息.
可用软件包 :
| 软件包 | 计划 |
|---|---|
@atcute/atproto | com.atproto.* |
@atcute/bluemoji | blue.moji.* |
@atcute/bluesky | app.bsky.*, (中文(简体) ), chat.bsky.* |
@atcute/frontpage | fyi.unravel.frontpage.* |
@atcute/germ | com.germnetwork.* |
@atcute/leaflet | pub.leaflet.* |
@atcute/lexicon-community | community.lexicon.* |
@atcute/microcosm | blue.microcosm.*, (中文(简体) ), com.bad-example.* |
@atcute/ozone | tools.ozone.* |
@atcute/pckt | blog.pckt.* |
@atcute/standard-site | site.standard.* |
@atcute/tangled | sh.tangled.* |
@atcute/whitewind | com.whtwnd.* |
您可以注册多个软件包来组合其类型.
使用量
客户端使用 XRPC 与 AT 协议服务进行通信,这是HTTP 之上的一个简单的 RPC 框架. 询问是获取请求,程序是POST请求.
提出请求
import { Client, simpleFetchHandler } from '@atcute/client';
import type {} from '@atcute/bluesky';
// create a client pointing to the Bluesky public API
const rpc = new Client({ handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }) });
使用情况 get() 查询和查询 post() 有关程序。 双向返回响应对象 ok, (中文(简体) ),
status, (中文(简体) ), headers,以及 data 字段 :
// queries use get()
const response = await rpc.get('app.bsky.actor.getProfile', {
params: { actor: 'bsky.app' },
});
if (response.ok) {
console.log(response.data.displayName);
// -> "Bluesky"
}
// procedures use post()
const response = await rpc.post('com.atproto.repo.createRecord', {
input: {
repo: 'did:plc:1234...',
collection: 'app.bsky.feed.post',
record: {
$type: 'app.bsky.feed.post',
text: 'hello world!',
createdAt: new Date().toISOString(),
},
},
});
处理出错
答复总是包括 ok 字段表示成功。对于失败的请求, data 包含一个
错误对象 error (错误名称)和可选 message (说明):
const response = await rpc.get('app.bsky.actor.getProfile', {
params: { actor: 'nonexistent.invalid' },
});
if (!response.ok) {
console.log(response.data.error);
// -> "InvalidRequest"
console.log(response.data.message);
// -> "Unable to resolve handle"
}
错误名称在词典中定义。 您可以在键入错误处理时切换 :
if (!response.ok) {
switch (response.data.error) {
case 'InvalidRequest':
// handle or account doesn't exist
break;
case 'AccountTakedown':
// account was taken down
break;
case 'AccountDeactivated':
// account deactivated by user
break;
}
}
乐观的要求
如果你喜欢扔出错误而不是检查 response.ok,使用 ok() 帮助者 :
import { Client, ok, simpleFetchHandler } from '@atcute/client';
const rpc = new Client({
handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }),
});
// throws ClientResponseError if the request fails
const profile = await ok(rpc.get('app.bsky.actor.getProfile', { params: { actor: 'bsky.app' } }));
console.log(profile.displayName);
// -> "Bluesky"
捕获错误 ClientResponseError数字 :
import { ClientResponseError } from '@atcute/client';
try {
const profile = await ok(rpc.get('app.bsky.actor.getProfile', { params: { actor: 'invalid' } }));
} catch (err) {
if (err instanceof ClientResponseError) {
console.log(err.error); // error name from server
console.log(err.description); // error message from server
console.log(err.status); // HTTP status code
}
}
经认证的请求
基于密码的认证, 使用 PasswordSession 从 @atcute/password-session它管理
符号, 自动刷新已过期的访问符号, 并可以坚持会话 :
npm install @atcute/password-session
import { Client, ok } from '@atcute/client';
import { PasswordSession } from '@atcute/password-session';
const auth = await PasswordSession.login({
service: 'https://bsky.social',
identifier: 'you.bsky.social',
password: 'your-app-password',
});
const rpc = new Client({ handler: auth });
// requests are now authenticated
const session = await ok(rpc.get('com.atproto.server.getSession'));
console.log(session.did);
// -> "did:plc:..."
保存 auth.session 可以在应用程序重新启动时持续登录 :
// after login, save the session
localStorage.setItem('session', JSON.stringify(auth.session));
// later, restore the session
const saved = localStorage.getItem('session');
if (saved) {
const auth = await PasswordSession.resume(JSON.parse(saved));
const rpc = new Client({ handler: auth });
}
使用回调来保持持续会话同步 :
const auth = await PasswordSession.login(
{
service: 'https://bsky.social',
identifier: 'you.bsky.social',
password: 'your-app-password',
},
{
onUpdate(session) {
// called on login and token refresh
localStorage.setItem('session', JSON.stringify(session));
},
onDelete(session) {
// called on logout or session invalidation
localStorage.removeItem('session');
},
},
);
响应格式
默认情况下,回复被解析为JSON. 对于返回二进制数据的终点,指定格式
与 as数字 :
// get response as a Blob
const { data: blob } = await ok(
rpc.get('com.atproto.sync.getBlob', {
params: { did: 'did:plc:...', cid: 'bafyrei...' },
as: 'blob',
}),
);
// get response as Uint8Array
const { data: bytes } = await ok(
rpc.get('com.atproto.sync.getBlob', {
params: { did: 'did:plc:...', cid: 'bafyrei...' },
as: 'bytes',
}),
);
// get response as ReadableStream
const { data: stream } = await ok(
rpc.get('com.atproto.sync.getBlob', {
params: { did: 'did:plc:...', cid: 'bafyrei...' },
as: 'stream',
}),
);
// discard response body
await ok(
rpc.post('com.atproto.repo.deleteRecord', {
input: { repo: 'did:plc:...', collection: '...', rkey: '...' },
as: null,
}),
);
运行时间验证
默认情况下,不需验证即可信任响应。 更严格的担保,使用 call() 与
定义包中的方案 :
import { Client, ok, simpleFetchHandler } from '@atcute/client';
import { AppBskyActorGetProfile } from '@atcute/bluesky';
const rpc = new Client({
handler: simpleFetchHandler({ service: 'https://public.api.bsky.app' }),
});
// validates params, input, and output against the schema
const response = await rpc.call(AppBskyActorGetProfile, {
params: { actor: 'bsky.app' },
});
if (response.ok) {
// response.data is validated
console.log(response.data.displayName);
}
抛出验证错误 ClientValidationError数字 :
import { ClientValidationError } from '@atcute/client';
try {
await rpc.call(AppBskyActorGetProfile, { params: { actor: 'invalid!' } });
} catch (err) {
if (err instanceof ClientValidationError) {
console.log(err.target); // 'params', 'input', or 'output'
console.log(err.message); // validation error details
}
}
服务代理
服务代理让您通过您的 PDS 向其它服务发送认证请求 。 政务司 以授权标题转发请求,证明它是代表你行事.
import { Client, ok } from '@atcute/client';
import { PasswordSession } from '@atcute/password-session';
// must be authenticated
const session = await PasswordSession.login({
service: 'https://bsky.social',
identifier: 'you.bsky.social',
password: 'your-app-password',
});
// create a client that proxies requests through your PDS to the chat service
const chatClient = new Client({
handler: session,
proxy: {
did: 'did:web:api.bsky.chat',
serviceId: '#bsky_chat',
},
});
// request goes to your PDS, which forwards it to api.bsky.chat with auth headers
const convos = await ok(chatClient.get('chat.bsky.convo.listConvos'));
共同事务身份证包括:
#atproto_pds- 个人数据服务器#atproto_labeler- 标签服务#bsky_chat- 布鲁斯基聊天服务
自定义抓取处理器
页:1 simpleFetchHandler 大部分情况下都是工作 对于高级情景,请提供自己的处理器:
import type { FetchHandler } from '@atcute/client';
const customHandler: FetchHandler = async (pathname, init) => {
// pathname is like "/xrpc/app.bsky.actor.getProfile?actor=bsky.app"
const url = new URL(pathname, 'https://public.api.bsky.app');
// add custom headers, logging, retry logic, etc.
console.log(`${init.method?.toUpperCase()} ${url}`);
return fetch(url, init);
};
const rpc = new Client({ handler: customHandler });
或执行 FetchHandlerObject 用于状态处理器( 如 PasswordSession ưμ㼯a
import type { FetchHandlerObject } from '@atcute/client';
class MyHandler implements FetchHandlerObject {
async handle(pathname: string, init: RequestInit): Promise<Response> {
// your implementation
return fetch(new URL(pathname, 'https://...'), init);
}
}
const rpc = new Client({ handler: new MyHandler() });
费率限制
retryFetchHandler 将另一个处理器包成重试有速率限制(HTTP 429)的响应. 延迟
从 Retry-After 或者说 RateLimit-Reset 显示时页眉, 否则为指数
备份.
import { Client, retryFetchHandler, simpleFetchHandler } from '@atcute/client';
const rpc = new Client({
handler: retryFetchHandler({
handler: simpleFetchHandler({ service: 'https://bsky.social' }),
maxRetries: 3, // attempts before giving up (default 3)
maxDelay: 60_000, // upper bound on a retry delay, in ms (default 60s)
fallbackDelay: 1_000, // backoff base when no header is present, or null to not retry (default 1s)
shouldRetry: (response) => response.status === 429, // widen to retry other statuses
}),
});
parseRateLimitHeaders 读取 RateLimit-* 头部关闭一个反应 在他们暴露,
返回时 null 当他们缺席或畸形时:
import { parseRateLimitHeaders } from '@atcute/client';
const response = await rpc.post('com.atproto.repo.createRecord', {/* ... */});
const info = parseRateLimitHeaders(response.headers);
if (info) {
console.log(`${info.remaining}/${info.limit} remaining, resets at ${info.reset.toISOString()}`);
}