Appearance
Post Exampleを動かす
リポジトリのexample/は、テスト用PostをMemory DBへ登録し、Postのinfo、list、detailを実際のデコレーターで実装します。
大きな一枚のファイルにはせず、実際のサーバーへ機能を追加しやすい最小限の責務で分割しています。
ファイル配置
text
example/
├─ src/
│ ├─ fixtures/posts.ts # DatabasePostItemのテストデータ
│ ├─ routes/
│ │ ├─ server-info.ts # サーバー入口
│ │ └─ posts/
│ │ ├─ info.ts # Post info
│ │ ├─ list.ts # Post list
│ │ ├─ detail.ts # Post detail
│ │ └─ mapper.ts # DB用Postから表示用Postへの変換
│ ├─ database.ts # DB生成とseed
│ ├─ sonolus.ts # Honolus設定
│ ├─ app.ts # route moduleの登録
│ └─ server.ts # Node.js listenerと終了処理
└─ test/posts.test.js # info/list/detailのHTTPテスト起動する
bash
git clone https://github.com/Untitled-Sekai/Honolus.git
cd Honolus/example
npm install
npm run dev既定ではhttp://localhost:3000で待ち受けます。
テスト用Postを作る
fixtures/posts.tsに、DBへ保存するDatabasePostItemを定義します。
ts
export const welcomePost: DatabasePostItem = {
name: 'welcome',
version: 1,
title: {
en: 'Welcome to Honolus',
ja: 'Honolusへようこそ',
},
time: 1_788_022_800_000,
author: {
en: 'Untitled Sekai',
ja: 'Untitled Sekai',
},
tags: [
{ title: { en: 'Example', ja: 'サンプル' } },
],
description: {
en: 'This post is loaded from the example Memory database.',
ja: 'ExampleのMemoryデータベースから取得したポストです。',
},
}database.tsからseedとして渡します。
ts
export const database = createSonolusDatabase({
driver: 'memory',
seed: { post: [welcomePost] },
})独自デコレーターを有効にする
HonolusへDBを渡しつつ、組み込みDB routeは無効化します。これによりDB lifecycleとcontext.databaseを維持したまま、Post routeを自分で登録できます。
ts
export const sonolus = new Honolus({
database,
databaseRoutes: false,
})Post info
ts
@sonolus.route.server.post.info
export class PostInfoHandler {
public handle(_context: SonolusContext): ServerItemInfo {
return {
title: 'Posts',
sections: [],
}
}
}Post list
list handlerはRepositoryからPostを検索し、DB用のlocalized textをクライアント表示用文字列へ変換します。
ts
@sonolus.route.server.post.list
export class PostListHandler {
public async handle(
context: SonolusContext,
): Promise<ServerItemList<PostItem>> {
const result = await context.database!.repository('post').list({
page: { limit: context.pagination.limit },
})
return {
title: 'Posts',
pageCount: Math.ceil(
(result.totalCount ?? result.items.length) /
context.pagination.limit,
),
items: result.items.map((post) =>
toPostItem(post, context.localization),
),
}
}
}Post detail
detail handlerの第2引数にはURLのitemNameが渡されます。存在しない場合はNotFoundErrorを投げることで404になります。
ts
@sonolus.route.server.post.detail
export class PostDetailHandler {
public async handle(
context: SonolusContext,
itemName: string,
): Promise<ServerItemDetails<PostItem>> {
const post = await context.database!.repository('post').get(itemName)
if (!post) throw new PostNotFoundError(itemName)
return {
item: toPostItem(post, context.localization),
description: localized(post.description!, context.localization),
actions: [],
hasCommunity: false,
leaderboards: [],
sections: [],
}
}
}完全な実装はroutes/posts/にあります。
HTTPで取得する
bash
curl http://localhost:3000/sonolus/posts/info
curl 'http://localhost:3000/sonolus/posts/list?localization=ja'
curl 'http://localhost:3000/sonolus/posts/welcome?localization=ja'detailでは次のようにPostを取得できます。
json
{
"item": {
"name": "welcome",
"version": 1,
"title": "Honolusへようこそ",
"time": 1788022800000,
"author": "Untitled Sekai",
"tags": [{ "title": "サンプル" }]
},
"description": "ExampleのMemoryデータベースから取得したポストです。",
"actions": [],
"hasCommunity": false,
"leaderboards": [],
"sections": []
}自動テスト
bash
npm testtest/posts.test.jsはinfo、list、detailをHono appへHTTP requestし、Post名、日本語タイトル、説明、404を確認します。
OpenAPIを生成する
ExampleはCLI用scriptも定義しています。
bash
npm run build
npm run openapi内部では次のコマンドを実行し、example/openapi.jsonへ登録済みルートを出力します。
bash
honolus openapi --module ./dist/app.js --output openapi.json本番化するときはMemory DBをPostgreSQLへ交換し、同じRepository呼び出しを維持できます。