DenoのOakでミドルウェアを作成する方法

概要

Oakは、ミドルウェア(middleware)を使用して、リクエストとレスポンスの間で処理を実行する能力を持つフレームワークです。ミドルウェアは、リクエストオブジェクト、レスポンスオブジェクト、および次のミドルウェア関数へのアクセスを提供します。ミドルウェアは、Oakのルートハンドラ内で使用されます。

ここでは、以下のバージョンを使用して、Oakでミドルウェアを作成する方法を紹介します。

  • Oak v9.0.0
  • Deno v1.15.3

また、今回作成するコードは全て、以下のGitHubリポジトリに掲載しています。

https://github.com/wiblok/Oak.js

ミドルウェアの定義方法

Oak.jsでは、ミドルウェアを使用する主な方法は次のようになります。

  1. アプリケーションレベルミドルウェア
    全体のアプリケーションに適用されるミドルウェアです。app.use()を用いて定義します。
import { Application } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

app.use((context, next) => {
  console.log('This is an application-level middleware');
  next();
});
  1. ルーターレベルミドルウェア
    特定のルート、もしくはルートグループに対して適用されるミドルウェアです。router.use()を用いて定義します。
import { Application, Router } from "https://deno.land/x/oak/mod.ts";

const app = new Application();
const router = new Router();

router.use((context, next) => {
  console.log('This is a router-level middleware');
  next();
});

app.use(router.routes());
app.use(router.allowedMethods());

これらのミドルウェアは、個々の要求がアプリケーションを通過する際に特定のポイントで機能を実行するのに役立ちます。それぞれのミドルウェア関数は、要求-応答サイクルを終了させるか、スタック内の次のミドルウェア関数に要求と応答オブジェクトを渡すかします。

アプリケーションレベルミドルウェアを作成する方法

以下の例では、myMiddlewareという名前のアプリケーションレベルのミドルウェアを作成します。

application.ts

import { Application } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

const myMiddleware = async (context, next) => {
  // ミドルウェアの処理
  console.log('ミドルウェアの実行');
  await next();
}

app.use(myMiddleware);

app.use((context) => {
  context.response.body = { message: 'Hello, World!' };
});

await app.listen({ port: 8000 });

このコードは、Oakフレームワークを使用してDenoでWebサーバーを作成するためのものです。以下にコードの解説を箇条書きで示します。

  • import { Application } from "https://deno.land/x/oak/mod.ts";
    • OakフレームワークをApplicationという名前でインポートします。
  • const app = new Application();
    • new Application()を呼び出して新しいOakアプリケーションを作成し、app変数に代入します。
  • const myMiddleware = async (context, next) => { ... }
    • myMiddlewareという名前の関数を定義します。
    • この関数はミドルウェアと呼ばれ、リクエストを処理するためにOakによって使用されます。
  • console.log('ミドルウェアの実行');
    • ミドルウェア関数内の処理の一部として、コンソールに「ミドルウェアの実行」というメッセージを出力します。
  • app.use(myMiddleware);
    • app.useメソッドを使用して、作成したミドルウェア関数(myMiddleware)をOakアプリケーションに適用します。
    • これにより、全てのリクエストがこのミドルウェア関数を通過することになります。
  • app.use((context) => { ... });
    • app.useメソッドを使用して、GETリクエストに対するルートハンドラを定義します。
    • アロー関数内の処理では、

context.response.bodyプロパティを使用してJSON形式のレスポンスを送信しています。

  • await app.listen({ port: 8000 });
    • app.listenメソッドを使用して、ポート8000でアプリケーションを起動します。

以下のコマンドを使用してリクエストを送信できます。

curl -X GET http://localhost:8000

ルーターレベルミドルウェアを作成する方法

以下の例では、myMiddlewareという名前のルーターレベルのミドルウェアを作成します。

routeMiddleware.ts

const myMiddleware = async (context, next) => {
  // ミドルウェアの処理
  console.log('ミドルウェアの実行');
  await next();
}

export default myMiddleware;

routeMain.ts

import { Application } from "https://deno.land/x/oak/mod.ts";
import myMiddleware from './routeMiddleware.ts';

const app = new Application();

app.use(myMiddleware);

app.use((context) => {
  context.response.body = { message: 'Hello, World!' };
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

このコードを実行すると、すべてのリクエストに対してミドルウェアが実行され、コンソールに ‘ミドルウェアの実行’と出力されます。

curl -X GET http://localhost:3000

組み込みミドルウェアを利用する方法

Oakフレームワークには組み込みのミドルウェアがあり、これらは特定の機能を提供します。例えば、以下のようなコードで静的ファイルを提供することができます。

builtin.ts

import { Application, send } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

// sendミドルウェアを使用して静的ファイルを提供する
app.use(async (context) => {
  await send(context, context.request.url.pathname, {
    root: `${Deno.cwd()}/public`,
  });
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

このコードは、publicディレクトリ内の静的ファイルを提供します。例えば、ブラウザでhttp://localhost:3000/sample.txtにアクセスすると、public/sample.txtが表示されます。

curl -X GET http://localhost:3000/sample.txt

サードパーティミドルウェアを利用する方法

Denoでは、deno.land/xから利用可能なサードパーティのミドルウェアを利用することができます。例えば、以下のようなコードでoak_middleware_loggerというログ出力用のミドルウェアを利用することができます。

deno install oak_middleware_logger

次に、このパッケージをアプリケーションに組み込むためのコードを記述します。

thirdparty.ts

import { Application } from "https://deno.land/x/oak/mod.ts";
import logger from "https://deno.land/x/o

ak_middleware_logger/mod.ts";

const app = new Application();

// loggerミドルウェアを使用してログを出力する
app.use(logger.logger);
app.use(logger.responseTime);

app.use((context) => {
  context.response.body = { message: 'Hello, World!' };
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

このコードは、すべてのリクエストに対してoak_middleware_loggerミドルウェアが実行され、それによってHTTPリクエストの詳細がコンソールに出力されます。

curl -X GET http://localhost:3000

エラーハンドリングミドルウェアの作成方法

エラーハンドリングミドルウェアは、他のミドルウェアから渡されたエラーを処理します。以下はエラーハンドリングミドルウェアの作成例です。

errorhandling.ts

import { Application } from "https://deno.land/x/oak/mod.ts";

const app = new Application();

app.use(async (context, next) => {
  try {
    await next();
  } catch (err) {
    console.error(err.message);
    context.response.status = 500;
    context.response.body = 'Something went wrong';
  }
});

app.use((context) => {
  throw new Error('Something went wrong');
});

await app.listen({ port: 3000 });
console.log('Server is running on port 3000');

このコードでは、エラーハンドリングミドルウェアが実行された後にエラーをスローします。このエラーはエラーハンドリングミドルウェアによってキャッチされ、エラーメッセージがコンソールに出力されます。また、クライアントに対しては500エラー(Internal Server Error)とともにエラーメッセージが返されます。

curl -X GET http://localhost:3000