0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのモジュールをREST APIとして公開するexpressミドルウェア

Last updated at Posted at 2024-05-19

apiフォルダにjsファイルを配置するだけで、REST APIとして呼び出し可能にするミドルウェアです

RailsやCakePHPのルーティングのように、URLから呼び出すファイル名と関数名を決定します

  • /api/func?val1=str&val2=cat ⇒ api.js の func('str', 'cat')を呼び出す

機能

  • /api/にあるファイル(ES Module)を動的に読み込み、下記ルールでルーティングする
    • http(s):///<ファイル名(拡張子抜き)>/<関数名>
  • (get:クエリストリング|post:リクエストbody)から関数の引数と同じ名前の値を取り出して、関数を呼び出す

利用イメージ

1.ミドルウェアに登録する

// api用moduleのロード(非同期)
apiModuleLoader(app).then((handler) => app.use(handler));

2.RESTとして公開する関数(util.js)を/api/に保存する

util.js
export function strcat(val1, val2) { return val1 + val2; }

3.expressを実行する

npm run express

4.ブラウザからURLを開く(/<ファイル名>/<関数名>?<クエリストリング>)

image.png

APIが呼び出せたことを確認できました

5.POSTで呼び出し(curl)

POSTで呼び出し

$ curl -X POST -d "val1=str&val2=cat2" "http://localhost:3000/util/strcat"
"strcat2"

jsonで呼び出し

$ curl -X POST -H "Content-Type: application/json" -d '{"val1":"str", "val2": "cat3"}' "http://localhost:3000/util/strcat"
"strcat3"

その他の機能

  • 関数内でreqestオブジェクトを参照することができます(GET,POSTで処理を切り分ける等)
/**
 * reqオブジェクトを参照するサンプル(GET,POSTで処理を切り分ける)
 */
export const test1 = () => {
  // 関数自体にreqが入っているため、必要があれば参照可能
  if (test1['Request']['method'] == 'GET') {
    return 'test1 get';
  } else {
    return { message: 'test1 post' };
  }
};

ミドルウェア

  • apiフォルダにあるファイルをdynamic importで読み込み、関数の一覧をexpressに登録する
  • 登録するパスは/<ファイル名(拡張子抜き)>/<関数名>
  • fn-Argsで関数の引数名の一覧を取得し、クエリストリング(リクエストBody)からその名前で値を取り出して、関数を呼び出す
  • 関数呼び出しの直前、関数オブジェクトにreqをセットする(関数内でreqを利用できる)
express-api-loader.js
/**
 * JavaScriptのモジュールをREST APIとして公開するexpressミドルウェア
 * 機能
 * ・/api/にあるファイル(module)を動的に読み込み、下記ルールでURLとマッピングする
 *  http(s)://<host_name>/<ファイル名(拡張子抜き)>/<関数名>
 * ・(get:クエリストリング|post:リクエストbody)から関数の引数と同じ名前で取り出して、関数を呼び出す
 * ex.
 *  api/api.ts に `export const strcat = (val1, val2) => val1 + val2;` という関数が宣言すると
 *  localhost/api/strcat?val1=1&val2=2 で呼び出すことができる
 */

import fs from 'fs';
import path from 'path';
import fnArgs from 'fn-args';

const handler = (func, arg_mapper) => {
  return (req, res) => {
    func['Request'] = req; // requestを呼び出し元で参照できるようにする
    const result = func(...fnArgs(func).map(arg_mapper(req)));
    res.json(result);
  };
};

const apiModuleLoader = async (express) => {
  const api_files = fs.readdirSync('./api');
  for (let file_name of api_files) {
    const module = await import(`./api/${file_name}`);
    file_name = path.parse(file_name).name;
    for (const key of Object.keys(module)) {
      const func = module[key];
      const route = `/${file_name}/${key}`;
      if (typeof func === 'function') {
        express.get(
          route,
          handler(func, (req) => (arg) => req.query[arg])
        );
        express.post(
          route,
          handler(func, (req) => (arg) => req.body[arg])
        );
      }
    }
  }
  const requestHandler = async (req, res, next) => {
    next();
  };
  return requestHandler;
};

export default apiModuleLoader;

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?