JSON ServerをCLIコマンドを使わずTypescript&node.jsからサーバーを立てるやり方


2022/05/01

JSON ServerはExpress.jsを利用したモックREST APIサーバーをCLIツールとしてコマンドラインから簡単に提供してくれjavascript製のプログラムです。

JSON Serverの利用者はあまりExpress.jsの利用方法に慣れていなくとも、色々な内部処理の実装をすることなくインストール一発で簡単にREST APIのテスト環境が整うことが最大の魅力です。

他方、より応用的なREST APIからのレスポンスを得ようとすると、JSON Serverをより深くカスタマイズする必要が出てきます。

場合に寄っては、Express.jsのミドルウェアを実装して利用することも検討しないといけません。

今回はそんなJSON Serverのカスタマイズのときに役に経つかもしれないTypescriptでの実装に焦点を当てます。


CLI有りのJSON Serverの利用手順をおさらい

通常のJSON Serverの使い方としては、node.jsのインストールされている環境で、CLIコマンドとしてグローバルにインストールし、

            
            $ yarn global add json-server
        
さらにプロジェクトのルートフォルダにdb.jsonというルーティングの定義ファイルを作成し、

            
            {
    "posts": [
        { "id": 1, "title": "json-server", "author": "typicode" }
    ],
    "comments": [
        { "id": 1, "body": "some comment", "postId": 1 }
    ],
    "profile": { "name": "typicode" }
}
        
このルートを定義した状態で、JSON Serverを以下のコマンドで起動します。

            
            $ json-server --watch db.json
        

この間JSON Serverの利用者はほぼ何もソースコードを弄ることなく、REST APIのモック環境が出来上がります。

あとはクライアント側からこのサーバーがリスニングの状態のまま定義済みのルートにGETを投げてみると、

            
            $ curl -XGET http://localhost:3000/posts/1
{ "id": 1, "title": "json-server", "author": "typicode" }
        
とJSON形式のレスポンスが得られます。

JSON Serverによって、ユーザーは殆ど何もすることなくフェイクのREST APIサーバーがローカルに構築することが可能です。


TypescriptでJSON Serverをカスタマイズしてみる

とりあえず先程のCLIからの使い方でも十分な開発水準で使えるのであれば、内部のソースコードを弄る作業はする必要ありません。

もっと柔軟かつ拡張的な機能をモックサーバーに持たせるのではあれば、Typescriptからサーバーをカスタマイズを検討してみるのも良いでしょう。

ここでいう拡張機能としては、リクエスト・レスポンスでの認証操作/バリデーションなどのHTTPインターセプトのようなものをミドルウェアとして介在させることを指します。

以下で、適当なtypescriptプロジェクトにJSON Serverと定義ファイルを導入します。

            
            $ yarn add json-server -S
$ yarn add @types/json-server -D
        
まずはサーバーの本体になるserver.tsというファイルを以下のように作成します。

            
            import jsonServer from 'json-server';
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();

const endpoint = 'http://localhost:3000';

server.use(middlewares);

server.use(router);

server.listen(3000, () => {
    console.log(`JSON Server is now running at ${endpoint}!`);
});
        
編集したら、早速tsビルドでトランスパイルして、出力されたjsコードがdist/server.jsに出来ているとします。

            
            $ tsc
$ ls dist/
server.d.ts    server.js      server.js.map
        
このserver.jsはnodeコマンドで直接JSON Serverを起動できるようになります。

            
            $ node dist/server.js
JSON Server is now running at http://localhost:3000!
        
あとは別の端末からCurlなどでリクエストをすることでモックRest APIサーバーとして立ち上がります。


自作JSON Serverにミドルウェアを仕込む

よりJSON Serverを応用的にカスタマイズしていく場合、「ミドルウェア」の作成してserver.tsを拡張していくことになります。

ここでのミドルウェアとは、Express.jsのミドルウェアそのものです。

必須ではないのですが、typescriptでより型の厳密に扱うために、プロジェクト環境に
express.jsも導入しておくと便利です。

            
            $ yarn add express @types/express -D
        

なおExpress.jsのミドルウェアに関しては、このブログだけでは簡単に説明できるものではないので、より詳しく知りたい方は別の技術記事を探してみてください。

以下では実装のエッセンスだけを取り上げます。

まずは、typescriptプロジェクトのルートに
middlewareという名前でミドルウェア用のリソースフォルダを作成します。

その
middlewareフォルダの中に、timeStamp.tsというコードを新規に作成し、以下のような内容にしておきます。

            
            import { Request, Response, NextFunction, RequestHandler } from 'express';

export function timeStamp(): RequestHandler {
    return (req: Request, res: Response, next: NextFunction) => {
        console.log('Now the time is ', Date.now());
        //👇最終的にJSON Serverルーターに到達させるのに必要なnext
        next();
    }
};
        
Exress.jsの経験がある方ならば、慣れ親しんだ普通のExpressミドルウェアになります。

後はこのミドルウェアを
server.ts側で呼び出して使えばOKです。

            
            import jsonServer from 'json-server';
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();

//👇ミドルウェアの呼び出し
import { timeStamp } from './middleware/timeStamp';
const myTimeStamp = timeStamp();

const endpoint = 'http://localhost:3000';

server.use(middlewares);

//👇ミドルウェアを利用する位置に注意
//  (必ずrouterよりも前の位置で呼び出し)
server.use(myTimeStamp);

server.use(router);

server.listen(3000, () => {
    console.log(`JSON Server is now running at ${endpoint}!`);
});
        
でtsトランスパイル後に、nodeでサーバーを立ち上げて、GETリクエストして挙動を確かめてみると、

            
            $ node dist/server.js
JSON Server is now running at http://localhost:3000!
Now the time is  2022-04-29T03:16:21.828Z
GET /posts/1 200 11.158 ms - 85
        
ミドルウェアがきっちりと中間で仕事をしていることがわかります。


Typescriptでのルーターレスポンス出力の変形

JSON ServerでJSON形式のHTTPレスポンス部分を最終的に吐き出してくれる役目を担っているのが、router(ルーター)です。

公式の利用説明の通りでJavascriptでの利用ならば、出力レスポンスは
router.renderメソッドから、

            
            router.render = (req, res) => {
    res.jsonp({
        body: res.locals.data
    })
}
        
であったり、HTTPステータスコードも一緒に、

            
            router.render = (req, res) => {
    res.status(500).jsonp({
        error: "error message here"
    })
}
        
などとすると自由にレスポンス変形することができます。

これをTypescriptでやろうと思うと、執筆時現在で
@types/json-serverにバグがあるらしく、router.renderが存在していないというようなコンパイルエラーが発生することがあります。

参考|ISSUE IN router

現状でこの不具合を回避するには以下のようにtypescriptコードをすると良いようです。

            
            import jsonServer from 'json-server';
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();
const endpoint = 'http://localhost:3000';

server.use(middlewares);
server.use(router);

//👇routerを強制的にanyへアップキャストする
(router as any).render = (req: any, res: any) => {
    res.jsonp({ data: res.locals.data })
}

server.listen(3000, () => {
    console.log(`JSON Server is now running at ${endpoint}!`);
});
        

以上、TypescriptでJSON-Serverを拡張するテクニックを書き出してみましたが...ここまでREST APIサーバーを自作する必要があるのなら、
Express.jsでアプリケーションを一から作るのと大差ないのかもと感じます。

あくまでもJSON-Serverの売りはインストール後即時使えるREST APIフェイクサーバーですので、その内部のExpressコアの部分まで修正したいなら、いっそExpressから書き直した方が良いでしょう。

ここらへんの線引きは難しいので、どちらを使って開発を切り替えるかは己が判断で行ってください。

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。