agreedを利用してモックAPIを実装する備忘録として
使用ライブラリ
agreed
フロントエンド側モックサーバ 兼 バックエンド側テストクライアントとしても使える
agreed-typed
agreedにtypescriptによる静的型付けを提供できる
API定義からjson形式のswaggerファイルを生成できる
実装
インストール
フロント側プロジェクトにローカルインストール
$ npm i -D agreed agreed-typed
agreed
contract
と呼ばれるagreedにおけるクライアント要求ファイルを作成する
agreed.js
module.exports = [
{
request: {
path: '/user/:id',
method: 'GET',
query: {
q: '{:someQueryStrings}'
},
values: {
id: 'yosuke',
someQueryStrings: 'foo'
}
},
response: {
body: {
message: '{:greeting} {:id} {:someQueryStrings}',
images: '{:images}',
themes: '{:themes}'
},
values: {
greeting: 'hello',
images: ['http://example.com/foo.jpg', 'http://example.com/bar.jpg'],
themes: {
name: 'green'
}
}
}
}
]
req/resにおけるqueryやbodyに記述する{:foo}
と valuesのfoo
が紐づく
package.jsonにモックAPIサーバ起動タスクを用意するのが一般的かも
package.json
{
(snip)
"scripts": {
"agreed": "agreed-server --path ./agreed.js --port 3010"
}
}
このように手軽にフロント側でモックAPIサーバを用意できるが、この上API定義に静的型付けや必須/非必須などの情報を追加定義したい
agreed-typed
- typescriptによるAPI定義の静的型付け
- swaggerジェネレータとしての機能も提供
agreed.ts
import {
APIDef,
GET,
Capture,
ResponseDef,
Success200,
Error404,
convert,
} from 'agreed-typed';
/**
* @summary User Get API
*/
export type UserApi = APIDef<
GET, // HTTP Method
['user', Capture<':id'>], // API Path
{}, // request header
{ q: string }, // request query
undefined, // request body
{}, // response header
| ResponseDef<Success200, CreateResponseBody>
| ResponseDef<Error404, { error: 'error test' }> // response body
>;
type CreateResponseBody = {
message: string;
images?: string[];
themes?: object;
};
const User: UserApi[] = [
{
request: {
path: ['user', ':id'],
method: 'GET',
query: {
q: '{:someQueryStrings}',
},
body: undefined,
},
response: {
status: 200,
body: {
message: '{:greeting} {:id} {:someQueryStrings}',
images: '{:images}',
themes: '{:themes}',
},
values: {
greeting: 'hello',
images: ['http://example.com/foo.jpg', 'http://example.com/bar.jpg'],
themes: {
name: 'green',
},
},
},
},
{
request: {
path: ['user', '9999'],
method: 'GET',
query: {
q: '{:someQueryStrings}',
},
body: undefined,
},
response: {
status: 404,
body: {
error: 'error test',
},
},
},
];
const agrees = [User].map((a: any) => convert(...a));
module.exports = agrees.reduce((acc, val) => acc.concat(val), []);
contractのモジュール化
contractをモジュール管理することもできるのでメンテナンス性を担保できる
api/sendMessage.ts
import {
APIDef,
POST,
ResponseDef,
Success200,
Error404,
} from 'agreed-typed';
/**
* @summary Send Message API
*/
export type SendMessageApi = APIDef<
POST, // HTTP Method
['send'], // API Path
{}, // request header
{}, // request query
CreateRequestBody, // request body
{}, // response header
| ResponseDef<Success200, CreateResponseBody>
| ResponseDef<Error404, { error: 'error post test' }> // response body
>;
type CreateRequestBody = {
message: string;
};
type CreateResponseBody = {
result: string;
};
const SendMessage: SendMessageApi[] = [
{
request: {
path: ['send'],
method: 'POST',
body: {
message: 'foo bar',
},
},
response: {
status: 200,
body: {
result: 'SEND SUCCESS',
},
},
},
{
request: {
path: ['send'],
method: 'POST',
body: {
message: 'error',
},
},
response: {
status: 404,
body: {
error: 'error post test',
},
},
},
];
module.exports = SendMessage;
先に作ったuser情報取得APIも同様にモジュール化して、agreed.tsでまとめるように書き換える
agreed.ts
import { convert } from 'agreed-typed';
import * as GetUser from './api/getUser';
import * as SendMessage from './api/sendMessage';
const agrees = [GetUser, SendMessage].map((a: any) => convert(...a));
module.exports = agrees.reduce((acc, val) => acc.concat(val), []);
備忘録のため一旦ここまで