BLOG

ブログ

【Nuxt3】NitroでHTMLファイルを返してみた

はじめに

Nuxt3の記事を読むたびに登場するNitro。 エンジニア初心者の私はNitroのことをよくわからないままNuxt3を使用していました。 正直、Nitroについてよくわかっていないとしても、Nuxt3自体は動かすことができますし、超便利に使用できます。でもやっぱりどんなものか知りたい!と思ったので調べてみました。

今回はそんな私のNitro調査報告です。これを読めばあなたもNitroと仲良しになれる! Webサーバーなどについても軽く触れておりますので、 私と同じ初心者の方も安心して読んでください〜!! 記事後半でNitroだけを使用してHTMLを返すWebサーバーもどきを作成しますのでお楽しみに!

なお、わかりやすい文章になるように表現を簡略化してる部分が多々あります。厳密な定義とはズレてる部分があるかもしれませんのでご了承ください。

でははじまり〜!

Nitroとは

Nitroは超高速なWebサーバーを構築するためのオープンソースのTypeScriptフレームワークである、と公式さんはおっしゃています。 また、いろいろな方のブログ等を拝見していると、Nitroはサーバーエンジンである!という結論に辿り着くことができます。

ここまで読んで、「ふむ、OK!」となった方は中盤くらいまで飛ばしてもらって大丈夫です!わからないよって方、大丈夫です、私もさっぱりでした。一緒に勉強していきましょう!

どうやらNitroはWebサーバーに関係しているようです。 ではWebサーバーとは何者なのでしょうか?

簡潔にいうと、Webページの情報を渡してくれるコンピュータ、です。

少し詳しく説明します。
我々が普段何気なくみているWebページは、必ずどこかのコンピュータに保存されています。私たちは自分のパソコンやスマホを使って、Webページにアクセスします。このとき、アクセスを検知して欲しい情報を私たちのパソコンやスマホに渡してくれるのがWebサーバーです。 (厳密にはブラウザ等さまざまな要素が絡み合いますが今回は割愛します。)

わかりやすく例えるなら、レンタルサーバーが想像しやすいと思います。レンタルサーバーにもいろいろ種類があって、メール機能を司るサーバーやデータベースとして働くサーバーなどがありますが、今回の例ではHTML等のファイルが置いてあってブラウザからリクエストが届いたらリクエストに応じたファイルを返してくれるWebサーバーのことを指します。

ちなみにサーバーというと何か特別な気がしてしまう(個人の感想です)のですが、端的にいうとただのコンピュータです。さまざまな機能を提供(serve)するからサーバーと呼ばれているだけです。もしあなたが今使ってるパソコンが誰かに何かを提供しているなら、それは立派なサーバーです。ちょっとズレてしまいましたがこの感覚があるのとないのではこの先の話の理解度が違うなと感じたのでご紹介でした。

話を戻して、Webサーバーを図にするとこんな感じです。

これでWebサーバーの正体はなんとなく掴めました。

ではいよいよ本題。Nitroはサーバーエンジンだ!を理解していきます。
サーバーエンジンは使われる文脈によって意味が変わるようで、Googleで「サーバーエンジン」を検索しても具体的な解説記事を見つけることはできませんでした。(Nginxの記事がたくさん出てきます。)

ここでいうサーバーエンジンは、Webサーバーの機能の中核となる部品、です。部品といっても実体があるわけではなく、ソフトウェアとしてインストールされています。 サーバーエンジンが入っていないWebサーバーはWebサーバーとして十分に機能できません。

自動車で例えるとわかりやすいかもしれません。自動車は車体があってもエンジンが搭載されていなければ動きませんよね。Webサーバーも同じで、サーバーエンジンがなければ、Webページの情報を私たちに届けることができません。 ちなみに図にするとこんな感じです。

そして!そのサーバーエンジンこそがNitroというわけです。 自動車もエンジンが良ければ速度も速くなるし燃費も良くなります。(たぶん) Nitroも超高性能エンジン、だから速いしいろんな機能が使えるぜ!というわけです。実際Nuxt3はNitroにサーバーエンジンが変更になって画面表示が非常に高速になったと話題を呼びました。

まとめるとブログでいろんな方がおっしゃっている通り、Nitroは高性能サーバーエンジン、ということになりますね。

開発環境でのNitro

さて、皆さんはNuxt3を使うとき、yarn dev やnpm run devという魔法のコマンドを使うと思います。このときに何が起きているのかおさらいして、どこでNitroが登場するのかを見ていきましょう!

yarn dev やnpm run devのコマンドを実行すると、 自分のパソコンの中でNode.jsが動き、コマンドを実行したプロジェクト内のnode_modulesを参照して仮想のWebサーバーを構築してくれます。 自分のパソコンの中でもう一つのコンピュータが動きだすイメージですね。 Nitroはこの仮想のWebサーバーの中で働いてくれます。 実際にNuxt3の初期設定を終わらせたディレクトリの中にあるnode_modulesの中身を見てみると、nitropackというモジュールを見つけることができます。

Nuxt2まではNode.jsの標準的なWebサーバーの機能を使用して皆さんが書いたコードをブラウザに表示していました。Nuxt3からはこの標準的な仮想WebサーバーにNitroという新たなサーバーエンジンが加わり、より速く表示する処理ができるようになったというわけです。 簡潔にまとめるとNuxt3の開発環境ではNuxt3に特化したサーバーエンジンが動いている!だから動作が速い!ということになります。

Nitroになってよかったこと

もちろん、Nitroはただ開発環境で画面表示を速くするだけではありません。

Nitroの恩恵は他にもたくさんあります。 調べてみるといろいろな記事が出てきますが公式が掲げているメジャーなものは下記の通りです。

  • クロスプラットフォームサポート
  • サーバーレスサポート
  • API Routesをサポート
  • 自動コード分割
  • ホットリロード
  • ハイブリッド・モード

クロスプラットフォームとサーバレスのサポート

1つ目のクロスプラットフォームサポートと2つ目のサーバレスについてですが、Nuxt3はNitroのおかげでNode.js以外の環境でも動かすことができるように設計されているようです。昨今話題のサーバレスな開発(Amazon Lambda等)にも対応しているナウいフレームワーク!というのがNitroの売りのようです。ちなみに私はVercelでデプロイしたことがあるのですが、とっても簡単に設定することができました。

サーバレスをすごく噛み砕いて説明すると、ある程度出来上がってるサーバーを使ってアプリ開発に注力しちゃおう!という考え方を指します。サーバー自体がないわけではありませんので注意です。

API Routesのサポート

3つ目のAPI Routesは、Nuxtのプロジェクト内でAPIのエンドポイントを作ることができる機能です。APIを作成するメリットはプロジェクトによってことなりますが、中間APIとして使用したり、簡易的なAPIサーバーとして重たい処理や何度も使用する処理なんかをAPIにして、どこからでも呼び出せるようにする!みたいな使われ方をされるようです。

自動コード分割のサポート

4つ目の自動コード分割について、これはビルドする時に関わる機能です。Nuxtは皆さんが書いたコードをyarn buildやnpm run buildという魔法のコマンドでhtmlやJavaScriptに変換してくます。そうやって生成されたHTMLやJavaScriptを使って画面を表示しているのですが、この生成されたコードたちをきれいにまとめてコンピュータが理解しやすい形にまとめてくれるのが自動コード分割機能です。

ホットリロードのサポート

5つ目のホットリロードは馴染み深いと思います。開発環境起動中にコードを変更すると、変更に合わせて画面が変わる機能です。この機能が遅いのと速いのでは開発効率は雲泥の差だと個人的に思います。

ハイブリッドレンダリングのサポート

6つ目は昨年私がブログ記事にもしましたハイブリッドレンダリングを実現する機能です。Nuxt2まではSSGかSSRどちらか一方しか選べませんでしたが、Nuxt3は状況に合わせてレンダリングの方式を変えることができます。これもNitroのおかげなんですね。

Nitro単体で使ってみる

さあ、Nitroについてわかってきたところで、いよいよ手を動かす時間です! NitroはNuxt3に特化してはいるものの、Nuxt3に依存しているわけではないようです。

ですので、Nitro単体で使ってみることにします! まずは公式に基づいて、Nitroのプロジェクトを作成し、Nitroを使える状態にしてあげます。

npx giget@latest nitro nitro-app
cd nitro-app
npm install

npm install 実行後はこのようなディレクトリ構成になっているはずです。

では早速Nitroサーバーを起動してみましょう。みんな大好き開発用コマンドを宣言します。

npm run dev

ブラウザでlocalhost:3000にアクセスすると、JSON形式で情報が返ってきていることが確認できます。(Nitroは素晴らしい!って言ってる笑)

このJSONはroutes/index.tsで設定されています。記述を見てみましょう。

export default eventHandler(() => {
  return { nitro: 'Is Awesome!' }
})

この部分について解説していきます。
Nitroはroutesディレクトリ配下にTypeScriptファイルを配置することで、簡単にAPIを作成することができます。チュートリアルでは、routesディレクトリ配下のindex.tsがGETリクエストに対して、JSONを返しています。 APIのpathについてはroutesディレクトリがpathをファイル名から読み取ってくれます。

具体的にはsample.tsならlocalhost:3000/sampleのような形でAPIのpathを作成できます。この辺はNuxtのpageディレクトリに仕様が似ていますね。これは便利!

また、NitroはroutesディレクトリのTypeScriptファイル名で受け取るHTTPリクエストの種類を決めることができます。基本的なリクエスト種別は以下の通りです。

  • GET(sample.get.ts)
  • POST(sample.post.ts)
  • PUT(sample.put.ts)
  • DELETE(sample.delete.ts)

リクエストの種類をファイル名で設定しない場合は、デフォルトとしてGETリクエストに対応するよう設定されるようです。

TypeScriptファイルの中身の動きとしては、defineEventHandler関数の中にリクエストに対して返したい値や具体的な処理を書くだけで、HTTPリクエストに対するNitroサーバーの挙動を設定することができます。ありがとうdefineEventHandler関数!

上記を簡単にまとめます。

  • APIのpathはroutesディレクトリ内のファイル名で設定できる
  • HTTPリクエスト種別はTypeScript名で設定できる
  • defineEventHandler関数で簡単にリクエストに対する処理を書くことができる

なるほど、わかりやすいです。初心者でもここまで理解できるということは、コンピュータさんたちはもっと速くこの処理を作ることができるというわけです。だから動きが速いのか、と納得しました。

では試しにPOSTリクエストに対する応答をNitroサーバーに追加してあげましょう。 routesディレクトリの配下にindex.post.tsファイルを追加し下記の記述を追記します。

export default defineEventHandler(async event => {
    const body = await readBody(event)
    return { requestBody:body}
  })

API testerでhttp://localhost:3000に対してPOSTリクエストを送信します。リクエストbodyにはJSON形式で好きなものを設定します。今回は私の名前をJSONで入れてみました。

ちなみにreadBody関数は読んで字の如く、リクエストのbody内容を読み込んでくれます。関数名が分かりやすくてとても嬉しい設計です。

ちゃんと受け取れれていますね。

NitroでHTMLファイルを返す処理

それではお待ちかね!
次はHTMLファイルを返す処理についてみていきましょう。

まずはHTMLファイルをどこにおくかです。 NitroにHTMLファイルや画像ファイル名などを置いておくためには、assetsディレクトリを作成してあげる必要があります。プロジェクト直下(routesディレクトリと同じ階層)にassetsディレクトリを作成しましょう。

この中に読み取りたいHTMLなどのファイルを追加します。 今回はassetsディレクトリ配下にindex.htmlを作成し、以下のようにNitroへの挨拶と感謝を記述します。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <H1>こんにちは、Nitro</H1>
    <p>ありがとう、Nitro</p>

</body>

</html>

ではこのHTMLファイルをlocalhost:3000(ルートパス)にアクセスした時に返すようNitroサーバーの動きを設定してあげます。 最初に画面表示したroutes/index.tsファイルに戻って、以下のように内容を書き換えていきます。

export default defineEventHandler(async (event) => {
  return await useStorage().getItem(`assets/server/index.html`)
})

useStrage関数はassetsの中身を参照しますよというイメージ、getItem関数はassetsディレクトリ内のこのファイルを選びますよというイメージです。

ここで注意です!!

getItem()のパス指定を見てみましょう。assetsとintex.htmlの間にserverが挟まってます。 ですが実際のディレクトリ構成を確認するとserverディレクトリは存在しません。 このserverは何者なのでしょうか…?

これはNitroの仕様で、assets内のファイルにサーバー側の処理としてアクセスする場合は、assetsのうしろに/serverをつけてからファイルを指定する、という決まりがあります。 一応公式にもサンプルとともに解説はあるのですが、この書き方が少しややこしくissueにもなってました。ちなみに私はここでガッツリハマり、激しく時間を溶かしました。(serverとassetsの位置が逆になっていました。)

ともあれ、上記の記述でhtmlが返ってくるはずです!
localhost:3000にアクセスしてみましょう!

Nitroへの挨拶と感謝が画面に表示されていれば成功です。

ルートパスにアクセスするとHTMLを返すWebサーバーもどきをNitroで作成しました。 実際のWebサーバーとは程遠いですが、なんとなくわたしたちが何気なくアクセスしてる裏側でサーバーがどんな動きをするのか、Nitroとはどのような役割なのかをご紹介できたと思います!assetsの部分は手こずりましたが、それ以外は公式ドキュメントも読みやすかったです。

もう怖くないよ、Nitro!

まとめ

というわけで、今回はNitroに触れてみました。 正直、Nuxtを使う上でこの知識が実用的かというと答えは否かもしれませんが、Nuxtの知見が深まった気がします。 また、Webサーバーというものの理解を深めるためにも大変勉強になりました。

やっぱり自分で作ったもの(ほぼチュートリアルに沿ってですが)が動くところを確認できると楽しいですね。

余談ですが「Nitro」と調べても爆薬の記事しか出てきませんので、「unjs/nitro」で調べることをお勧めします。

これであなたもNitroマスター!マスターまでいかなくとも仲良しにはなれましたね。
以上遠藤でした〜!
それでは、またの機会に〜!

RELATED ARTICLE