くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

Nitro Serverに入門してみたら、Nuxt3の理解が深まった

この記事をみて、Nitro サーバに興味が出たので、
ドキュメントみながら、少し触ってみたときの備忘録(*´ω`*)

全てをdevDependenciesにするよ。具体的には、ビルド後の資材が必要最低限のもので.outputディレクトリにまとめられるから、 「巨大な node_modules を AWS Lambda にあげて、コールドスタート時のあまりの遅さに悩まされる」 なんて事態を回避できるよ。

ビルドも早く軽量で、よさそうな感じ(*´ω`*)
Nuxt3がなんであの書き方になったのかも少し理解できた気がする。

install

package.jsonの設定

$ pnpm dlx giget@latest nitro nitro-app

初期設定で作成されるけど、少し変更したのがこれ。

{
  "scripts": {
    "dev": "nitropack dev",
    "build": "nitropack build",
    "preview": "node .output/server/index.mjs",
    "postinstall": "nitropack prepare"
  },
  "packageManager": "pnpm@8.3.1",
  "engines": {
    "pnpm": ">=8",
    "node": ">=18"
  },
  "devDependencies": {
    "nitropack": "^2.5.2"
  }
}

auto importsを使っているので、preparedevを実行しないと、
型が生成されないので、postinstallに設定しておく。

npmrcの設定

そのまま、pnpm installすると、いくつか依存先の型定義が見つからないので、
shamefully-hoist=trueを設定しておく。

# .npmrc
shamefully-hoist=true

install

あとは、インストールなどすればOK

$ pnpm install
$ pnpm dev
$ pnpm build && pnpm preview

Routing

ファイルパスがそのままルートになるよう。

# デフォルト
api/
  test.ts      <-- /api/test
routes/
  hello.ts     <-- /hello
nitro.config.ts

ただ、api/ディレクトリの仕様が、
Vercelの仕様と競合するので、Nuxt3のように

# Nuxt3
routes/
  api/
    test.ts    <-- /api/test
  hello.ts     <-- /hello
nitro.config.ts

という形がよいぽい。

特定のメソッドのみの場合は、こんな感じのファイル名にすればOK

Request/Response

シンプルなのはこの形。
パスパラメタはevent.context.paramsから取得。

// routes/hello/[name].ts
export default eventHandler(event => {nitro : `Hello ${event.context.params.name}!` } );

内部では、h3というhttp frameworkを使っているので、
queryやheaderの取得、response関係の設定はこっちを見るといい。

// routes/[...params].ts
export default eventHandler((event) => {
  const pathParams = event.context.params;
  const query = getQuery(event);

  const header = getHeaders(event);

  const sessions = event.context.sessions;
  const cookie = getCookie(event, "cookie_key");
  const method = getMethod(event);

  setResponseStatus(event, 404);
  return {
    nitro: "Is Awesome!",
    pathParams: pathParams,
    query,
    header,
    method: method || "(none)",
    cookie: cookie || "(none)",
    sessions: sessions || "(none)",
  };
});
// "http://localhost:3000/a/b/?p=1&a=1&a=2"の場合
{
  "nitro": "Is Awesome!",
  "pathParams": {
    "params": "a/b"
  },
  "query": {
    "p": "1",
    "a": [ "1", "2" ]
  },
  "header": {
    "cookie": "cookie_key=something; cookie_key2=anything",
    // ...
  },
  "method": "GET",
  "cookie": "local",
  "sessions": "(none)"
}

全部のパスを取得したい場合はこれ。

// routes/[...].ts
export default eventHandler(event => `Default page`)

Cache/Storage

Cache-Controlヘッダーなど、Cacheを使いたい場合、
cachedEventHandlerに変えると、オプションで設定できる。

// routes/[...params].ts
export default cachedEventHandler(
  (event) => ({ seconds: new Date().getSeconds() }),
  { swr: false, maxAge: 5 }
);

ルートを正規表現などで指定することもできるけど、
現段階で、experimental(実験的)な機能。

// nitro.config.ts
import { defineNitroConfig } from "nitropack/config";

export default defineNitroConfig({
  routeRules: {
    "/blog/**": {
      swr: 60 * 60,
      // or
      cache: {
        maxAge: 60 * 60
      }
    },
  },
});

を利用する。が、Radisなどにも変更ができる。

import { defineNitroConfig } from "nitropack/config";

export default defineNitroConfig({
  storage: {
    cache: {
      driver: "redis",
      url: "redis://localhost:6379",
    },
  }
});

いずれもUnstorageのdriverを利用。

DBアクセスなどもそのあたりの設定を使えるよう。

Resources

Resources系は2種類あり

  • public/ ... 配置されるファイル
  • aseets/ ... ビルド時にバンドルされるファイル

という形。

public/
  image.png     <-- /image.png
  video.mp4     <-- /video.mp4
  robots.txt    <-- /robots.txt
assets/
  my_file       <-- useStorage('assets:server').getItem("my_file")
package.json
nitro.config.ts

assets/ディレクトリにあるファイルは、Unstorage経由で扱える。

Middleware

Middlewareについてドキュメントには詳しく書いてないけど、
middleware/配下に配置するよう。

ファイル名順に実行されるので、順番をファイル名の先頭に付けておくとよい。

middleware/
├── 1.common.ts
└── 2.auth.ts

Middlewareの中身は、routingと同じ。
認証チェックのサンプルより。

// middleware/2.auth.ts
export default defineEventHandler((event) => {
  event.context.auth = { name: "User " + Math.round(Math.random() * 100) };
});

event.contextは、h3のH3EventContextなので、
そちらの型を拡張すればよいっぽい。

declare module "h3" {
  interface H3EventContext {
    auth: { name: string }
  }
}
export {};

Plugins

plugins/配下に配置すると、auto importしてくれる。

plugins/
  1.first.ts
  2.second.ts
// plugins/test.ts
export default defineNitroPlugin((nitroApp) => {
  console.log('Nitro plugin', nitroApp)
})

Deploy

デプロイ先の設定をまとめたプレセットが用意されているので、
それらを利用すればOK。

# Node.js Server(default)
$ nitro build

# Firebase Hosting
$ NITRO_PRESET=firebase nitro build

# AWS Lambda
$ NITRO_PRESET=aws-lambda nitro build

# Heroku
$ NITRO_PRESET=heroku nitro build

# Vercel
$ NITRO_PRESET=vercel nitro build

# GitHub Pages
$ NITRO_PRESET=github-pages nitro build

ほか、Netlify、Azure、Denoなども用意。
各設定値のソースはこのあたり。

AppConfig/RuntimeConfig

Nuxt3と同様、useAppConfig(event?)useRuntimeConfig(event?)が使える。

Environment Variables

NITRO_PRESET以外にも、NITRO_PORTNITRO_HOSTなどもある。

このあたりは、unenvを利用しているよう。

実験的な機能

nitro.config.tsでフラグを立てると、
実験的な機能を使うことができる。

// nitro.config.ts
import { defineNitroConfig } from "nitropack/config";

export default defineNitroConfig({
  experimental: {
    openAPI: true,
    wasm: true,
  },
});
  • openAPI
    • /_nitro/swagger/_nitro/openapi.jsonエンドポイントを自動生成
    • Swagger UIを自動生成してくれるので、APIを叩きやすい
    • ただ、パスパラメタの解析までなので、今後を期待
  • wasm
    • Enable WASM support
    • らしい。。

以上!! 触った感じ、ビルドも実行も早くていい。
Nuxt3の理解も深まった気がする(*´ω`*)

参考にしたサイトさま