SDK
Node.js SDK
安装 @sofia-post/node,在 Node.js 中调用 Sofia API:发布、定时、上传媒体、读取数据洞察,响应和错误都带类型。
官方 Node.js SDK 把 Sofia API v1 封装在一个类 Sofia 中:每个路由对应一个方法,请求和响应都有类型,所有失败都使用同一种错误类型。它没有任何依赖,可在 Node.js 20 或更高版本上运行。
安装
SDK 以 @sofia-post/node 包的名称发布在 npm 上。import 和 require 都可以使用。
npm install @sofia-post/node快速开始
用你的密钥创建客户端,找到要发布内容的店铺,然后在 Instagram 上定时发布一篇帖子:
import { Sofia } from '@sofia-post/node';
const sofia = new Sofia(process.env.SOFIA_API_KEY);
const [establishment] = await sofia.establishments();
const post = await sofia.post({
establishmentId: establishment.id,
targets: [{ platform: 'instagram', text: 'Tarte tatin du jour 🍎' }],
scheduledAt: new Date('2026-12-01T09:00:00Z'),
});
console.log(post.id, post.status);身份验证
构造函数接收一个个人密钥(在设置 › API 的个人访问标签页中创建),如果你的应用代表其他 Sofia 用户操作,则接收 OAuth 访问令牌(OAuth2)。SDK 会在每次请求中以 Authorization: Bearer … 发送它。
客户端默认连接 https://api.sofia-post.com。第二个参数可以更改基础 URL,或发送请求所用的 fetch:
const sofia = new Sofia(apiKey, { baseUrl: 'http://localhost:3008' });方法
| 方式 | 作用 |
|---|---|
me() | 密钥代表谁操作、它的 scope,以及是否为测试密钥 |
platforms() | 各网络的规则:文案长度、媒体、格式 |
establishments() | 密钥可访问的店铺 |
accounts(establishmentId) | 已连接到店铺的社交账号 |
connectionLink(establishmentId, input) | 由 Sofia 托管的页面,用户可在其中连接网络 |
insights(establishmentId, period?) | 按网络查看触达、互动和粉丝数据 |
post(input, options?) | 立即发布、用 scheduledAt 定时发布,或保存为草稿 |
postList(filters?) | 一页帖子,可按店铺、状态或日期筛选 |
getPost(id) · updatePost(id, input) · deletePost(id) | 读取、修改或撤回一篇帖子 |
upload(input) · uploadFromUrl(input) | 把文件或 URL 添加到店铺的媒体库 |
mediaList(filters) · getMedia(id) · deleteMedia(id) | 读取并清理媒体库 |
createWebhook(input) · webhooks() · deleteWebhook(id) · testWebhook(id) | 在帖子发布或失败时收到通知 |
帖子
一篇帖子在每个网络上都有一个目标,各自带有文案、媒体和格式。请先用 dryRun 检查:不会写入任何内容,被拒绝时会列出它违反的每条规则。传入 idempotencyKey,重试的请求就不会重复发布。scheduledAt 接受 Date 或 ISO 8601 字符串。
import type { PublishPostInput } from '@sofia-post/node';
const input: PublishPostInput = {
establishmentId,
targets: [
{ platform: 'instagram', text: 'Menu du jour', media: [{ id: media.id }] },
{ platform: 'facebook', text: 'Menu du jour : tarte tatin et café' },
],
};
await sofia.post(input, { dryRun: true });
await sofia.post(input, { idempotencyKey: crypto.randomUUID() });媒体
文件只需上传一次,然后把它的 id 放进帖子的 media。upload 接受 Buffer、Uint8Array 或 Blob,除非你指定 contentType,否则会根据文件名判断内容类型。uploadFromUrl 则由 Sofia 自己下载文件。
import { readFile } from 'node:fs/promises';
const media = await sofia.upload({
establishmentId,
file: await readFile('tarte-tatin.jpg'),
fileName: 'tarte-tatin.jpg',
});
const imported = await sofia.uploadFromUrl({ establishmentId, url: 'https://example.com/menu.png' });分页
postList 和 mediaList 每次返回一页。把一页的 nextCursor 作为下一次调用的 cursor 传入;最后一页没有它。
let cursor: string | undefined;
do {
const page = await sofia.postList({ establishmentId, status: 'published', limit: 100, cursor });
page.data.forEach((post) => console.log(post.id));
cursor = page.nextCursor;
} while (cursor);错误
当 API 拒绝调用时,每个方法都会抛出 SofiaApiError。它包含 HTTP status、错误信封中稳定的 code、details(对被拒绝的帖子,即它违反的每条网络规则)、需要提供给支持团队的 requestId,以及在 429 时的 retryAfterSeconds。
import { SofiaApiError } from '@sofia-post/node';
try {
await sofia.post(input);
} catch (error) {
if (!(error instanceof SofiaApiError)) throw error;
if (error.code === 'post_refused') console.log(error.details);
if (error.status === 429) await wait(error.retryAfterSeconds);
}当 API 完全无法访问时,错误的 code 为 network_error,状态为 0。SDK 从不自动重试:遇到 429 请等待 retryAfterSeconds,遇到 5xx 请放慢重试间隔。
TypeScript
该包自带类型:PublishPostInput、Post、Media、SocialAccount、EstablishmentInsights 等都与 Sofia 一起导出,因此你的编辑器会补全每个字段和每个枚举值。
接下来:API 参考介绍了 SDK 调用的每个路由,CLI 则在终端中操控同一套 API。