@sharedan/cms-sdk
v0.4.1
Published
TypeScript SDK for the ShareDan 簡易CMS public API — posts (by post type), post types, FAQs, static contents, categories, and form delivery/submission (with spam protection).
Maintainers
Readme
@sharedan/cms-sdk
ShareDan CMS の公開API を扱う TypeScript SDK です。 投稿(種別ごと)・FAQ・固定コンテンツ・カテゴリの取得、本文(ProseMirror / Tiptap JSON)の HTML レンダリング、およびフォームの配信・送信(スパム対策込み)を、型付き・依存ゼロで提供します。 ブラウザと Node.js 18+ の両方で動作します。
投稿は「種別(post type)」でスコープします。 投稿系メソッドは第1引数に種別のslug (既定種別は
'news')を取ります(例cms.posts.list('news'))。利用可能な種別はcms.postTypes.list()で取得できます。
特長
- 🧩 型付きクライアント — 各エンドポイントを
cms.posts/cms.postTypes/cms.faqs/cms.contents/cms.forms/cms.maintenanceで呼び出し、レスポンスは完全に型付け - 🖋️ リッチテキスト対応 — 本文/回答の ProseMirror JSON を
renderProseMirror()で HTML 文字列へ変換 - 📨 フォーム送信 — 問い合わせ等フォームの配信(項目定義)と送信を
cms.formsでラップ。Turnstile・レートリミット・ハニーポット・送信速度・重複防止の多層スパム対策に対応 - 🚦 明快なエラー型 —
ShareDanCmsApiError(status/code/isNotFound/isRateLimited等)とShareDanCmsNetworkError - 📦 依存ゼロ・軽量 —
fetchベース。ブラウザ / Node / エッジランタイムで動作 - 🔒 主に閲覧専用 — 公開API(
X-API-KEYでテナント、X-SITE-CODEでサイトを特定)をラップ。書き込みはフォーム送信(cms.forms.submit)のみ
インストール
npm install @sharedan/cms-sdkリポジトリを直接クローンして使う場合は、
npm install後にnpm run buildでdist/(ESM + 型定義)を生成してから読み込んでください。
クイックスタート
import { ShareDanCmsClient, renderProseMirror } from '@sharedan/cms-sdk'
const cms = new ShareDanCmsClient({
baseUrl: 'https://cms.example.com', // CMSのオリジン(/api/v1 は自動付与)
apiKey: 'YOUR_API_KEY', // X-API-KEY
siteCode: 'your-site', // X-SITE-CODE
})
// 種別一覧(discovery。既定種別は slug 'news')
const { items: postTypes } = await cms.postTypes.list()
// 投稿一覧(種別 'news'・カテゴリ絞り込み・ページネーション)
const news = await cms.posts.list('news', { category: 'event', page: 1, perPage: 20 })
console.log(news.meta.total, news.items)
// 投稿詳細(本文あり)→ HTML 化
const detail = await cms.posts.get('news', news.items[0]!.slug)
element.innerHTML = renderProseMirror(detail.body)
// FAQ / 固定コンテンツ / カテゴリ(投稿カテゴリは種別スコープ)
const faqs = await cms.faqs.list({ category: 'product' })
const content = await cms.contents.get('top-message')
const categories = await cms.posts.categories('news')設定
| オプション | 必須 | 説明 |
|-----------|:---:|------|
| baseUrl | ✓ | CMSのオリジン(例 https://cms.example.com)。/api/v1 は自動付与 |
| apiKey | ✓ | テナントのAPIキー(X-API-KEY) |
| siteCode | ✓ | サイト識別子(X-SITE-CODE / m_sites.code) |
| apiVersion | | APIバージョン(既定 'v1') |
| fetch | | 使用する fetch 実装(既定 globalThis.fetch) |
| timeoutMs | | タイムアウト(ミリ秒)。AbortController で中断 |
| headers | | 全リクエストに付与する追加ヘッダ |
API
| メソッド | エンドポイント | 戻り値 |
|---------|---------------|--------|
| cms.postTypes.list() | GET /api/v1/post-types | ListResponse<PostTypeSummary> |
| cms.posts.list(postType, params?) | GET /api/v1/posts/{postType} | Paginated<PostSummary> |
| cms.posts.get(postType, slug) | GET /api/v1/posts/{postType}/{slug} | PostDetail |
| cms.posts.getPreview(postType, slug, token) | GET /api/v1/posts/{postType}/{slug}/preview | PostDetail(下書き版・X-PREVIEW-TOKEN・非キャッシュ) |
| cms.posts.categories(postType) | GET /api/v1/posts/{postType}/categories | ListResponse<Category> |
| cms.faqs.list(params?) | GET /api/v1/faqs | ListResponse<Faq> |
| cms.faqs.categories() | GET /api/v1/faqs/categories | ListResponse<Category> |
| cms.contents.get(key) | GET /api/v1/contents/{key} | Content |
| cms.forms.get(key) | GET /api/v1/forms/{key} | Form(項目定義・非キャッシュ) |
| cms.forms.submit(key, input) | POST /api/v1/forms/{key}/submissions | FormSubmissionResult |
| cms.maintenance.get() | GET /api/v1/maintenance | Maintenance(非キャッシュ) |
postType は種別のslug(既定種別は 'news')。存在しない種別は 404 になります。
PostDetail.body / Faq.answer / Content.body は ProseMirror JSON(ProseMirrorNode)です。
PostDetail.meta(PostMeta)にはメタタイトル・ディスクリプション・OGP画像URL(meta_title / meta_description / og_image_url)が入り、公開ページのSEO/OGP出力に使えます(未設定時は null)。
フォーム(配信・送信)
cms.forms.get(key) でフォームの項目定義(fields)と、Turnstile 設定・送信速度チェック用トークン(speed_check_token)を取得し、cms.forms.submit(key, input) で送信します。
// 1. 配信:項目定義とトークンを取得(都度発行のためキャッシュ不可)
const form = await cms.forms.get('contact')
// 2. 送信:各項目 name をキーに値を渡す
const result = await cms.forms.submit('contact', {
values: {
name: '山田 太郎',
email: '[email protected]',
message: '製品について問い合わせたいことがあります。',
},
// 送信速度チェックが有効なフォームでは、配信で受け取ったトークンをそのまま返す
speedCheckToken: form.speed_check_token,
// Turnstile 有効フォームでは、ウィジェットが発行したトークンを渡す
turnstileToken: form.turnstile.enabled ? widgetToken : undefined,
})
console.log(result.id, result.submitted_at)- スパム対策(Turnstile/レートリミット/ハニーポット/送信速度/重複送信)はサーバ側で適用されます。ハニーポット項目は SDK が空値で自動付与します。
- ファイル項目(
type: 'file') は値にFile/Blobを渡します。File/Blob を含む送信は自動でmultipart/form-dataになり、ファイルはサーバ側でS3に保存されます(送信データにはファイル参照が格納されます)。1ファイル最大10MB/許可形式は画像・PDF・Word・Excel・テキスト。 - Turnstile 有効時は
form.turnstile.site_keyでフロントにウィジェットを描画し、得たトークンをturnstileTokenに渡してください(secret_keyはAPIから返りません)。
メンテナンス状態
cms.maintenance.get() でサイトのメンテナンス状態を取得します。投稿・FAQ 等の利用コンテンツ設定に関わらず常に応答し、状態を即時反映するためキャッシュされません(表示のたびに取得してください)。
const status = await cms.maintenance.get()
if (status.maintenance) {
// メンテナンス中:見出し・案内・再開予定を表示
console.log(status.title, status.message, status.resumes_at)
} else {
// 通常表示
}maintenance を判別子とする直和型(Maintenance)です。maintenance: false のときは他のフィールドを持ちません。
リッチテキストのレンダリング
import { renderProseMirror, extractText } from '@sharedan/cms-sdk'
const html = renderProseMirror(detail.body) // HTML文字列
const html2 = renderProseMirror(faq.answer, { // リンク属性のカスタマイズ
linkTarget: '_self',
linkRel: null,
})
const excerpt = extractText(detail.body).slice(0, 120) // プレーンテキスト抜粋対応ノード: paragraph / heading / bulletList / orderedList / listItem /
taskList / taskItem / blockquote / codeBlock / horizontalRule /
hardBreak / image / fileAttachment
対応マーク: bold / italic / underline / strike / code / textStyle(文字色) /
highlight(ハイライト) / link
React などで使う場合は、出力を dangerouslySetInnerHTML に渡すか、renderProseMirror
を参考に独自レンダラを実装してください。
エラーハンドリング
import { ShareDanCmsApiError, ShareDanCmsNetworkError } from '@sharedan/cms-sdk'
try {
const content = await cms.contents.get('top-message')
} catch (err) {
if (err instanceof ShareDanCmsApiError) {
if (err.isNotFound) { /* 404: 未存在/利用しない設定 */ }
console.error(err.status, err.code, err.details)
} else if (err instanceof ShareDanCmsNetworkError) {
// 接続不可・タイムアウト・CORS など
}
}フォーム送信では、スパム対策・バリデーションの結果を判定できます。
try {
await cms.forms.submit('contact', { values, speedCheckToken: form.speed_check_token })
} catch (err) {
if (err instanceof ShareDanCmsApiError) {
if (err.isBadRequest) { /* 400: Turnstile検証失敗など */ }
if (err.isValidationError) { /* 422: 入力不正・ハニーポット・送信速度・重複送信 */ }
if (err.isRateLimited) {
// 429: レート制限。err.retryAfter 秒後に再試行
console.warn('retry after', err.retryAfter, 'seconds')
}
}
}開発
npm install # typescript を取得
npm run build # dist/ に ESM + 型定義を出力
node examples/node.mjs # ローカルCMS(http://localhost:8091)に対するスモークテストライセンス
MIT © ShareDan CO. ,Ltd.
