Restify による Web API Server の作成
はじめに
restify は JaveScript(node.js) にて Web API(REST API) を作成するフレームワークです。
ちょっと趣味で使っているレベルでの話にはなりますが、特に restify の話をしている記事がなさそうだったので、折角なので簡単にお試しいただけるような内容だけでもまとめてみました。
この記事の目的
位置づけとしては「node.js を使ってサクッと Web API を作ってみよう」ということを想定しています。そのため、本番環境における運用を想定だとか、すごい Tips とかそういうのはありません。
そういう部分も気になる方は、ぜひ公式の Quick Start や各種 API Reference を読み込んでいただけたらと。(一応、最後にいくつか公式 Docs の内容にも触れてます)
また、例えば express との違い等についても書いてませんので、そういった点をご了承いただけたらと存じます。
準備
-
node.js(npm)
ザックリいうと JavaScript という言語でサーバーアプリを作成する際によく使用します。
Long Term Support(LTS) の方で自分の PC にあったものを入れてください。
https://nodejs.org/ja/download/ -
VSCode
コードの編集等で使用します。拡張機能で様々な機能が追加できます。
https://code.visualstudio.com/Download
また、以下の拡張機能も追加してください。
REST Client
サーバーアプリの機能を試すのに便利な拡張機能です。個人的には普段からよく使ってます。
動かしてみる
以下の Github にサンプルコードを用意しました。
git で clone する方法もありますが、そういった方法に慣れない方は zip でダウンロードすることもできます。
プロジェクトファイル一式がある場所で Powershell(適宜好みのコマンド ツール)を開きましょう。
Windows の方は、以下の画像のようにエクスプローラーから開くこともできます。
serverアプリの実行
以下のコマンドで必要な module を install します。
(package.json にそういう感じのことを書いてます。気になる方は npm というパッケージ管理ツールについて調べてみてください。)
npm install
続けて以下のコマンドでサーバープログラムを実行します。
npm start
まずはブラウザで以下の URL にアクセスしてみましょう。
以下のように私が雑に作ったページが出力されます。
次に、以下の URL にアクセスしてみましょう。
なんか違うページが表示されているかと思います。
では、ちょっとだけ実行されているコードを見てみましょう。
const restify = require('restify');
const fs = require('fs');
const jsonItems = require('./testItems.json');
// Create HTTP server
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, () => {
// plugin for use request body
server.use(restify.plugins.bodyParser({ mapParams: true }));
});
// Listen for incoming requests.
server.get('/', (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write("Welcome to the start test page");
res.end();
});
server.get('/get-html', (req, res) => {
fs.readFile("./index.html", 'UTF-8',
function(err, data) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write(data);
res.end();
});
});
server.get('/get-html', (req, res) => {
res.contentType = 'json';
res.send(jsonItems);
res.end();
});
server.post('/test-post', (req, res) => {
console.dir(req.body);
console.log(JSON.stringify(req.body))
res.send({ TestBody: "Test Post Response Body", req: req.body });
res.end()
});
server.get() という風に書いている部分がありますが、server.get('/',...) と書かれているところと、server.get('/get-html',...) と書かれているところがありますね。
'/' のエンドポイント方ではプログラム内で用意した文字列を返していて、'/test-get' のエンドポイント方では index.html というファイルを readFile していることが何となくわかるかなと思います。
もう一つエンドポイントがありますね。/get-json にアクセスしてみると、以下のようにテキトーに作った json ファイル(testitems.json)の中身を出力させています。
POST 実行
先ほどブラウザ―でページを開いたのが GET リクエストです。POST リクエストに関しても試してみましょう。
落としてきたファイル群の中にある test.http というファイルを VSCode で開きます。
その後、ファイルの一番上にある "Send Request" のボタンをクリックしてみましょう。
すると以下の画像のようになります。
画面の右側は POST リクエストを送信した結果になります。
こんな感じで、ひとまずは GET/POST やいくつかのファイル形式を用いたパターンについて動作を試せました。
もう少し公式ドキュメントとか見たい人向け
とりあえず動かしてみたい人はここまでで終わっても良いかと思います。
折角なので、もう少しだけ Docs を読んでみます。(長いのと、割と重複する内容もあるので一部のみ抜粋していきます。)
なんか「ここはむしろこうですね」みたいなアドバイスとかあれば後々の方々のためにコメントなり、別途記事をお纏めいただけたらと。
API について
公式 Docs では API のページが4種類あります。それぞれ割と名前のままな気がしますが、念のために気になるものがあればご確認いただけたらと。
Server API
主にサーバーの設定だとかに関係する API みたい。
ポートの指定やメソッドの指定とかもある。
const server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, () => {});
// Listen for incoming requests.
server.get('/', (req, res) => {});
Request API
Request の Header とかの情報を取得するような API みたい。
Response API
Response で返すときに、Header やら含めて色々と情報を付与できる。
res.header('content-type', 'json');
Plugin API
プラグインを追加する。これは結構落とし穴になることあるんじゃないかなと思う。
例えば、以下のコードはサンプルでも使用してますが、POST の時に Request の Body を
見ようとした、Body のデータを使おうとしたときに、以下の感じで plugin を追加することで実行できました。この記事を読んでいる時にどうかはわかりませんが、最初はかなりシンプルな機能だけ備えているみたいで、必要に応じて plugin を追加するような運用になるのかなと思いました。
server.use(restify.plugins.bodyParser({ mapParams: true }));
恐らくは以下のような流れになるのかなと思います。ものによっては、「この機能を使うには、plugin を追加してください」みたいなことが書かれてたりします。(Log とかそうだった気がする。)
- 使いたい機能をいずれかの API で探す。
- Server
- Request
- Response
- その中でそれっぽいものがなかったら Plugin から探す。
Quick Start より一部抜粋
next() について
「Sinatra style handler chains」の項目にて以下の記載があります。
In a restify server, there are three distinct handler chains:
- pre - a handler chain executed prior to routing
- use - a handler chain executed post routing
- {httpVerb} - a handler chain executed specific to a route
All three handler chains accept either a single function, multiple functions, or an array of function>
最後の文章のように複数の function をリクエストに対して実行というか、ハンドラーにて処理できるみたいです。実際の同じ項目内のコードが以下。
server.post('/foo',
function (req, res, next) {
req.someData = 'foo';
return next();
},
function (req, res, next) {
res.send(req.someData);
return next();
}
);
そして、「Using next()」 の項目には next(false) という使われ方が。
それより先の function は実行されないとのこと。
なので、試してみました。以下のコードを使ってます。(require 等は割愛)
next() と next(false) を切り替えて実行してみたら、確かに false にすると最後の function が実行されずに、console.log が出力されませんでした。next() ではきちんと hogehoge してました。
server.get('/foo',
function (req, res, next) {
req.someData = 'foo';
return next();
},
function (req, res, next) {
res.send(req.someData);
// switch "next()" or "next(false)"
return next(false);
},
function (req, res, next) {
console.log("hogehoge")
return next();
}
);
I/O について
QuickStart の Socket.IO の項目に以下のように記載されてます。
To use socket.io with restify, just treat your restify server as if it were a “raw” node server:
公式の sample でも fs.readFile() が使われているみたいなので、restify とは別の module を使って読み込むのが一般的なのかもしれないです。
restify-errors について
エラー関連に特化した module があるみたいです。「restify-errors」の項目ご確認ください。
const errs = require('restify-errors');
server.get('/error', function (req, res, next) {
// resource not found error
return next(new errs.ConflictError("I just don't like you"));
});
errs.ConflictError という関数を使ってますが、これは Conflict が発生してる際にはこの関数でエラーをハンドリングしなさいということみたいですね。該当のエンドポイントに GET リクエストを送ったら、以下の感じで応答が返されました。
{"code":"Conflict","message":"I just don't like you"}
また、「RestError」の項目にあるように、色々と種類は準備されてるみたいです。
一部をスクショで抜粋します。
そして、もし自分の好みのがなかったら自作できるみたいです。公式では ZombieApocalypseError(ゾンビ アポカリプス エラー) を作成してます。なので、
ゾンビ アポカリプス エラー が必要な際には追加できるということですね。助かる!
一旦結び
本当は Logging も試してみたいところですが、今一つやり方が掴めていないため、
一旦は結びとしておきます。(plugin を追加して Request API で何かできそうなものの、どこのディレクトリにログが残るのかとかもまだ掴めていない)
こんな感じで、お手軽にしたい方はお手軽に、色々と設定等拘りたい人は詳細に設定ができそうなフレームワークに感じました。僕は特に express とかも expert ではないのですが、少なくともそういう自分でも手軽に色々と試せる良いフレームワークだなと思ってます。
(これまでの自分の zenn の記事でも多々使用してます。)
(順番的にこっちを先に書いてたらこれまでの記事ももう少し書きやすかったのかなと思ったり)
まだ zenn にはrestify を使った記事は少なそうに感じたので、今後増えるといいなとお思いました。
Discussion