🎄

CDKのNodejsFunctionでHTMLをimportできるようにしたい

2023/12/24に公開

はじめに

この記事はAWS(Amazon Web Services) Advent Calendar 2023 24 日目の記事です。いよいよ明日で終わりですね。

概要

CDKのNodejsFunctionを利用して、Lambdaへのデプロイを行っています。
https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.NodejsFunction.html

該当のLambdaでHTMLの静的なページを作成する必要があったのですが、javascriptの中で記述するのが大変だったので、htmlファイルとして保存したファイルをimportして利用する方法を検討しました。

具体的には下のようなイメージです。

import htmlPage from './html/base.html';

あまりメジャーなやり方ではないせいか、調べてもパットは出てこなかったのでやり方をまとめておきます。

結論

NodejsFunctionのbundlingオプションを設定するだけです。

bundling: {
  loader: {
    '.html': 'text',
  },
}

があればOKです。
後続ではもうちょっと細かく説明をしていきます。

背景

Lambdaを使って静的なページを作成することで、他のシステムとは分離して動作するシステムを作成しようとしていました。
必要なのはボタンが2つのみある簡単なページだったので、Lambda上でHTMLを動的に生成して返すような仕様にしました。

まずは普通に作ってみる

最初は以下のような形で作成していました。

export const handler = () => {
  return `<html><body>Hello world</body></html>`
}

このままでもいいのですが、コード上は文字列として判定されるため、IDEの補完やエラーの検知といった恩恵を受けられず、修正しにくい状態になっていました。
また、HTMLのみで500行近い記述になっており、文字列として管理するのは難しい状態になっていたため、別のファイルに切り出すことを検討しました。

切り出す

まずはいったんエラーを気にせずにHTMLのみを切り出しました。

index.ts
import { page } from './page.html'

export const handler = () => {
  return page;
}
page.html
<html>
  <body>
    <p>Hello World</p>
  </body>
</html>

htmlの形式は読み込めないというエラーが出るので修正していきます。
IDEなどで型を正しく認識できるように、d.tsのファイルを作成します。

html.d.ts
declare module '*.html' {
  const value: string;
  export default value;
}

IDE上のエラーはなくなりましたが、ビルドの時にエラーが発生するので、修正します。

esbuildの設定

NodejsFunctionではesbuildを利用してビルドを行っています。
esbuildの設定はNodejsFunctionの中で行います。具体的には bundling のオプションになります。

https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda_nodejs.BundlingOptions.html

設定できる内容は基本的にはesbuildのオプションと同じだと思います。
今回は loader の内容を設定します。

bundling: {
  loader: {
    '.html': 'text',
  },
},

このように設定すると、htmlの内容はtextとして認識してくれるようになります。

https://esbuild.github.io/api/#loader
ちなみに、esbuildのドキュメントではtextではなく、dataurlという項目が紹介されており、これはpngをbase64に変換してくれるもののようです。

余談ではありますが、graphqlを利用するときにloaderのオプションを利用していたことがあります。

全体像

紹介していない部分もありますが、NodejsFunctionの全体像としては以下のようになりました。

new NodejsFunction(this, name, {
  functionName: `${name}-${stage}`,
  timeout: cdk.Duration.seconds(30),
  memorySize: 1024,
  runtime: Runtime.NODEJS_18_X,
  role: lambdaRole,
  bundling: {
    minify: true,
    sourceMap: true,
    loader: {
      '.html': 'text',
    },
    metafile: true,
    externalModules: props.stage === 'local' ? [] : ['@aws-sdk/*'],
},
environment: props.environment,
}),

まとめ

CDKのNodejsFunctionでHTMLをimportする方法を整理しました。
割と簡単な内容ですが、esbuildのOption周りは色々できることがあるので、設定を色々見てみると新しい発見があるかもしれません。

We're Hiring!

私が所属している株式会社DELTAでは特定の技術にこだわらず、ユーザのニーズに合った提案を行っています。
https://speakerdeck.com/delta_tech/zhu-shi-hui-she-delta-hui-she-shuo-ming-zi-liao

一緒に働いてくださる仲間を大募集中です!
ご興味をお持ちいただけましたら、私に直接連絡か、お気軽にフォームからご連絡ください。

https://docs.google.com/forms/u/1/d/e/1FAIpQLSfQuWNU1il5lq2rVdICM0tSK_jTsjqwc52LYEwUxBq7_ImtrQ/viewform

DELTAテックブログ

Discussion