我們在 Next.js SDK 中加入 Edge Runtime 的經驗
Logto 的 Next.js SDK 現在支援 Edge Runtime。本文章將分享我們的冒險旅程,看看我們面對的障礙、如何克服它們,以及在這過程中學到的酷炫知識。
序論
Edge Runtime は技術ランドスケープの中でバズワードとなり、AWS Lambda@Edge や Cloudflare Workers、Vercel Edge などのプラットフォームで動的で低遅延な関数を駆動しています。その重要性を強調するように、Vercel は最近 "experimental-edge" を "edge" に変更し、人気のある Next.js フレームワークでの公式サポートをシグナルとしています。
我々の Next.js SDK が真剣にトラクションを得ていることを受け、我々 Logto は "なぜ Edge Runtime サポートを追加しないのか" と考えました。そこで、我々はそのチャレンジに挑みました。この記事では、我々が直面した課題、それをどのように克服したのか、そしてその過程で学んだ面白いことを共有します。
Edge Runtime サポートのためのモジュールと依存関係の移行
Edge Runtime との作業は一部のユニークな課題を提示します。これは主に、Edge Runtime が Node.js で一般的に使用されるすべてのモジュールと依存関係をサポートしていないからです。我々は crypto、lodash、iron-session モジュールという問題に遭遇し、何らかの革新的なワークアラウンドが必要となりました。
Crypto
Node.js 環境では、crypto モジュールは OpenSSL の暗号化関数のラッパーとして機能します。残念ながら、Edge Runtime はそれをサポートしていません。しかし、大丈夫 - ほとんどの Edge Runtime は Web Crypto API をサポートすることで救済策を提供します。一部の微細な違いはありますが、crypto モジュールの確固たる代役となります。たとえば、ランダムなバイトを生成するには:
そしてハッシングは以下のようになります:
Lodash
Lodash はそのユーティリティのために多くの開発者の間でお気に入りとなっていますが、Edge Runtime ほどではありません。我々のワークアラウンドは何だったのでしょうか? Lodash 関数をネイティブの JavaScript メソッドと交換し、コードの効率性と可読性を保ちました。
ほとんどの Lodash 関数を置き換えることはヘラクレスの仕事ではありませんでしたが、いくつかの技巧を必要としました。次に、我々が "once" のユーティリティをどのように独自の方法で再現したのかを見 てみましょう:
Iron Session
iron-session モジュールの最新バージョンは Edge Runtime フレンドリーなので、我々がしなければならなかったのはバージョンをアップデートすることだけでした。それが全てです!
Edge Runtime における "Response" の微妙さをナビゲートする
Edge Runtime で我々の SDK を適応させるときに直面したもう一つの課題は、"Response" オブジェクトの違いを扱うことでした。以下は、我々がこれらの違いをどのように克服したのかを説明します:
手動でレスポンスを作成する
Node.js とは異なり、Edge Runtime のリクエストはレスポンスには伴わない。つまり、new Response()
を呼び出すことでそれを作成する必要がある。以下は、データを返す例:
"withIronSessionApiRoute" の解放
Edge Runtime では、Response.body
は読み取り専用の機能です。つまり、データが準備される前にレスポンスを初期化することができない。その結果、我々の信頼性が高い "withIronSessionApiRoute"(他の ミドルウェアと同様)をベンチに座らせる必要がありました。
我々が何を代替したのかを理解するために、まず withIronSessionApiRoute
が実際に何を行うのかを解説しましょう:
- それはクッキーを覗き、セッションオブジェクトを構築し、それを
res
に紐付けます。 - セッションに変更があった場合、自動的に "set-cookie" ヘッダーを
res
に追加します。
それでは、我々が新しい Edge Runtime 環境でこの機能をどのようにエミュレートしたのかを見てみましょう。
- 読み込み:既存の
getIronSession
関数を利用しました。それに空で偽のresponse
を与えることで、必要に応じてセッションを取得する。これがreq.session
の "get" メソッドを置き換えました。 - 書き込み:我々は upfront に data 付きの
response
を準備し、このresponse
インスタンス上でgetIronSession
を使用してセッションオブジェクトを取得しました。このオブジェクトを手に入れたら、必要なときにセッションを改変することができました。
リダイレクト
Edge Runtime ではリダイレクトを行うために、我々は手動で Location
ヘッダーをレスポンスに追加
要しました。
一つのパッケージ、2つのランタイム
我々のこの旅において、我々はEdge と Node.js のランタイムをサポートするために単一のパッケージに留まることに決めました。
ここが理由です
我々は Edge 用の別のパッケージを作成することを考えましたが、すぐにそれが不必要であることに気付きました。我々のコードのほとんどは2つのランタイム間で共有されており、一握りの行だけが微調整を必要としていました。さらに、SDK の使用はどちらのランタイムでもほぼ同じままなので、統一されたパッケージを維持することが最も意味があると感じました。
我々がやったこと
労力を二重にするのではなく、既存のパッケージを拡張することに決めました。パッケージのルートに "edge" フォルダを追加し、旧 "src" フォルダの隣に置きました。次に、package.json ファイルを更新し、"exports" に新しいパスを追加しました。この方法で、Edge と Node.js のランタイムは同じパッケージ内で、ほとんど手間をかけずに共存することができました。
締めくくり
我々の Next.js SDK のエッジ部分の完全なソースコードはここでチェックすることができます。
Edge Runtime を受け入れることによる我々の旅を共有することで、我々は他の人々が同様の道を探求することに触発し、ガイドすることを望んでいます。我々の Next.js SDK の更なる更新をお楽しみに。