ranuts/node — 小さな HTTP フレームワーク
Node.js 向けの、依存なしの小さな HTTP ツール一式です。HTTP サーバー、ルーター、WebSocket サーバー、ボディと静的ファイルのミドルウェア、それに CLI とファイルシステムのヘルパーがいくつか入っています。
⚠️ Node 専用です。 このエントリーポイントは
node:http、node:fs、node:child_processなどを読み込みます。ranuts/nodeから import してください。ブラウザー側のコードから読み込んではいけません。
読み込み
import { Server, Router, staticMiddleware, body } from 'ranuts/node';ボディを解析するミドルウェアは
bodyという名前で export されています(内部の名前はbodyMiddlewareです)。bodyMiddlewareという export はありません。
はじめの一歩
import { Server, Router, staticMiddleware, body } from 'ranuts/node';
const app = new Server();
const router = new Router();
// ルートはパスの完全一致で照合されます。ハンドラーはリクエストの Context を受け取ります。
router.get('/hello', (ctx) => {
ctx.res.setHeader('Content-Type', 'application/json');
ctx.res.end(JSON.stringify({ message: 'hello world' }));
});
// JSON ボディの POST。body() がそれを解析して ctx.request.body に載せます
router.post('/echo', (ctx) => {
ctx.res.end(JSON.stringify({ youSent: ctx.request.body }));
});
// body() はリクエストボディを解析すると同時に、ルーターが読む ctx.request
// (method / path / url / query)も埋めます。だから router.routes() より前に登録します。
app.use(body());
app.use(router.routes());
app.use(router.allowedMethods());
// 静的ファイルの配信(`/` は ./public/index.html に振り分けられます)
app.use(staticMiddleware({ pathname: './public' }));
const server = app.listen(3000, () => {
console.log('Server running at http://localhost:3000');
});
// `server` は土台になっている node:http の Server インスタンスです。ミドルウェアの順番が効いてきます。Router は ctx.request.path と ctx.request.method を読みますが、それを埋めるのは body() だからです。まず body() を登録してください。付属の body() が今のところ解析できるリクエストボディは application/json と multipart/form-data です。
API
Server
デフォルト export。node:http の上に載せた、最小限の Koa 風サーバーです。
| メンバー | 説明 | 型 |
|---|---|---|
new Server() |
サーバーを作ります。引数は取りません。 | () => Server |
use(middleware) |
ミドルウェアを連なりの末尾に足します。返り値は void で、チェーンはできません。 |
(fn: MiddlewareFunction) => void |
listen(...args) |
待ち受けを始めます。引数はそのまま http.Server.listen へ渡され、土台の http.Server が返ります。 |
(...args) => http.Server |
middleware |
登録済みのミドルウェアの配列。 | MiddlewareFunction[] |
ctx |
共有されるリクエストの Context(req/res はリクエストごとに差し替わります)。 |
Context |
ミドルウェアのシグネチャ
type Next = () => Promise<void> | Promise<never>;
type MiddlewareFunction = (ctx: Context, next: Next) => void | Promise<void>;next() を呼ぶと次のミドルウェアへ制御が渡ります。ミドルウェアは登録した順に呼ばれ、next() を二度呼ぶと例外になります。
Context の中身
| フィールド | 説明 | 型 |
|---|---|---|
req |
届いたリクエスト。 | http.IncomingMessage |
res |
サーバーの応答。res.setHeader / res.writeHead / res.end で書き込みます。 |
http.ServerResponse |
ipv4() |
このマシンの、内部向けでない最初の IPv4 アドレスを返します(なければ undefined)。 |
() => string | undefined |
request |
body() が足すもの。{ method, path, url, query, body } で、query は URLSearchParams です。 |
object (dynamic) |
[key] |
Context は開いた入れ物です。ミドルウェアは好きなフィールドを足せます。 |
any |
Router
デフォルト export。HTTP メソッドとパスの完全一致ごとにハンドラーを登録し、routes() でそれらをミドルウェアとして差し出します。
| メソッド | 説明 | 型 |
|---|---|---|
new Router() |
ルーターを作ります。 | () => Router |
get(url, handler) |
GET のルートを登録します。 |
(url: string, h: Handler) => void |
post(url, handler) |
POST のルートを登録します。 |
(url: string, h: Handler) => void |
put(url, handler) |
PUT のルートを登録します。 |
(url: string, h: Handler) => void |
patch(url, handler) |
PATCH のルートを登録します。 |
(url: string, h: Handler) => void |
del(url, handler) |
DELETE のルートを登録します。 |
(url: string, h: Handler) => void |
head(url, handler) |
HEAD のルートを登録します。 |
(url: string, h: Handler) => void |
options(url, handler) |
OPTIONS のルートを登録します。 |
(url: string, h: Handler) => void |
routes() |
一致したハンドラーへ振り分けるミドルウェアを返します。 | () => MiddlewareFunction |
allowedMethods() |
パスやメソッドが一致しなかったときに 404・405・501 を返すミドルウェアを返します。 |
() => MiddlewareFunction |
ハンドラーのシグネチャ
type Handler = (ctx: Context, next: Next) => void;リクエストのデータは ctx.request(method、path、url、query、body)から読み、応答は ctx.res を通して返します。パスは完全一致で照合されます。:param のようなセグメントには対応していないので、クエリパラメーターには ctx.request.query を使ってください。
ミドルウェア
| シンボル | 説明 | 型 |
|---|---|---|
body(options?) |
ボディを解析するミドルウェア。ctx.request を埋め、application/json と multipart/form-data を解析します。返り値はミドルウェアです。 |
(o?: Partial<ServerBody>) => MiddlewareFunction |
staticMiddleware(opt?) |
opt.pathname(既定は process.cwd())から静的ファイルを配信し、/ には index.html を返します。 |
(o?: Partial<Option>) => MiddlewareFunction |
connect(fn) |
Connect や Express 流の (req, res, next) ミドルウェアを、このフレームワークのミドルウェアに合わせます。 |
(fn) => MiddlewareFunction |
body(options) のオプション
| オプション | 説明 | 型 | 既定値 |
|---|---|---|---|
uploadDir |
multipart/form-data でアップロードされたファイルの置き場。 |
string |
'.' |
encoding |
届くリクエストストリームのエンコーディング。 | BufferEncoding |
'utf-8'/'binary' |
json |
JSON のボディを解析します(false なら生の文字列のまま)。 |
boolean |
true |
urlencoded |
urlencoded のボディ用に予約されています。 | boolean |
true |
staticMiddleware(option) のオプション
| オプション | 説明 | 型 | 既定値 |
|---|---|---|---|
pathname |
ファイルを配信する起点のディレクトリ。 | string |
process.cwd() |
fileTypes |
追加で登録する 拡張子 → MIME タイプ の対応。 |
Record<string, string> |
{} |
WebSocket
| シンボル | 説明 | 型 |
|---|---|---|
new WSS(httpServer) |
WebSocket サーバーを node:http のサーバーに取り付けます(upgrade のハンドシェイクとフレーム処理を引き受けます)。 |
(server: http.Server) => WSS |
import { Server, WSS } from 'ranuts/node';
const app = new Server();
const server = app.listen(3000);
const wss = new WSS(server);
wss.on('connect', (client) => {
client.on('message', (data) => client.send('echo: ' + data));
});
// wss.broadcast(data) は接続中の全クライアントに送ります。wss.clients がその一覧です。個々の client は send(data, options?)、ping()、pong()、close()、socket を備え、message・close・error のイベントを発行します。
ユーティリティ
| シンボル | 説明 | シグネチャ |
|---|---|---|
connect(fn) |
Connect や Express の (req, res, next) ミドルウェアを、このフレームワークのミドルウェアに合わせます。 |
(fn) => MiddlewareFunction |
get({ url }) |
JSON のエンドポイントを HTTPS で GET し、{ success, data, message } で解決します。 |
({ url: string }) => Promise<Response> |
getIPAdress() |
このマシンの、内部向けでない最初の IPv4 アドレス。なければ undefined。 |
() => string | undefined |
paresUrl(req) |
req.url を { search, query, pathname, path, href } に分解します(綴りにご注意を)。 |
(req: IncomingMessage) => ParseUrl | undefined |
prompt({ message }) |
ターミナルで yes/no を尋ね、y か yes なら true で解決します。 |
({ message, stream?, defaultResponse? }) => Promise<boolean> |
runCommand(cmd, args) |
子プロセスを起動し(stdio は引き継ぎます)、終了コード 0 で解決します。 |
(cmd: string, args: string[]) => Promise<void> |
readStream({ path }) |
path の fs.ReadStream を作ります。 |
(o: { path: string, ... }) => ReadStream |
writeStream({ path }) |
path の fs.WriteStream を作ります。 |
(o: { path: string, ... }) => WriteStream |
startTask() |
高分解能のタイマーを始め、中身の見えない symbol を返します。 |
() => symbol |
taskEnd(symbol) |
対応する startTask() からの経過時間(Node ではナノ秒の bigint)。 |
(s: symbol) => number | bigint |
traverse(dir, cb, pre?) |
dir を再帰的にたどり、ファイルごとに cb(relPath, absPath, stats) を呼びます(非同期)。 |
(dir, cb, pre?) => Promise<any> |
traverseSync(dir, cb, pre?) |
traverse の同期版。 |
(dir, cb, pre?) => void |
isColorSupported |
真偽値。今のターミナルが ANSI カラーに対応しているかどうか。 | boolean |
colors |
ANSI カラーのヘルパー。colors.red('text') のように使え、reset / bold / dim も揃っています。 |
Record<string, (s: string) => string> |
関連ページ
同じ ranuts/node エントリーポイントには、別ページで説明しているファイルシステムのヘルパーも入っています。