LambdaでEJSを使ってみる

2019.08.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

今回はNode.jsでejsというテンプレートエンジンのパッケージを使ってEJSテンプレートからHTMLファイルを生成する方法をご紹介します。

環境

  • Lambda Node.js10
  • aws-cli/1.16.158

ejsの用途

ただ単に変数を置換するだけであれば、Javascriptの replace で可能ですが、テンプレート内で条件分岐やループを行いたい場合、ejsを使えば簡単にできます。

試してみる

ejsをLambdaから呼び出すためにLayer化します。(ZIPアップロードでも大丈夫です)

S3の構成

今回はお試しということでデプロイ時のソース置き場とテンプレート置き場とHTMlファイルの出力先を一つのバケットを使います。 それぞれ以下のキーを使用します。

  • デプロイ時のソース置き場
  • source
  • テンプレート置き場
  • template
  • HTMlファイルの出力先
  • output

プロジェクト構成

最終的な構成は以下となります。

.
├── cfn
│ └── template.yml
├── functions
│ └── generate_html
│ └── index.js
├── layer
│ └── nodejs
│ ├── node_modules
│ │ └── ejs
│ ├── package-lock.json
│ └── package.json
├── output
│ ├── ejs-layer.zip
│ └── packaged.yml
└── template
└── template.ejs

HTMLテンプレートの作成

簡易的ですが、以下のテンプレートをS3に置いておきます。 template.ejs

<h2>Classmethodの領収書</h2>
<% if (user) { %>
<h3><%=user.username%>様</h3>
<% } %>

¥<%=amount%>

S3へアップロード

$ aws s3 cp template/template.ejs s3://{バケット名}/template/

Layerの作成

zipファイルの作成

ejsのパッケージを含んだzipファイルを作成します。

$ mkdir -p layer/nodejs
$ cd layer/nodejs
$ npm init -y
$ npm install --save ejs
$ cd ../
$ zip -r ../output/ejs-layer.zip .

layer/nodejsの階層でディレクトリを作成して、/nodejs配下にejsをインストールします。 インストールできたらnodejs/配下をzip化します。

S3の source/layer/ 配下にアップロードします。

$ aws s3 cp /output/ejs-layer.zip s3://{バケット名}/source/layer/

デプロイはLambdaと同じSAMテンプレートで行うので先にLambdaを実装します。

Lambdaの作成

Lambdaのコード

generate_html/index.js

const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const ejs = require('ejs');
const bucket = process.env['BUCKET_NAME']

const getTemplate = async (template_name) => {
const params = {
Bucket: bucket,
Key: `template/${template_name}`,
};
const data = await s3.getObject(params).promise();
return new String(data.Body, 'utf8');
};

const putHtml = async (html) => {
const file_name = Math.floor(new Date().getTime()/1000);
const file_path = `output/${file_name}`
const params = {
Bucket: bucket,
Key: file_path,
Body: html,
ContentType: 'text/html'
};
await s3.putObject(params).promise();
return file_path;
};

exports.handler = async (event) => {
console.log(event);
const template = await getTemplate('template.ejs');
let params = {
amount: event.amount
};
params.user = event.user ? event.user : null;
const html = ejs.render(template, params);
const file_path = await putHtml(html);

const response = {
statusCode: 200,
body: JSON.stringify(file_path),
};
return response;
};
  • まず getTemplate で先程準備したEJSテンプレートをS3からダウンロードします。
  • eventからパラメータの値を受け取り、 ejs.render() で置換します。
  • パラメータの置換した後に putHtml でS3のoutput/配下に出力したHTMLをアップロードします。

S3にアップロードするときの注意点ですが、 ContentType: 'text/html' と明示しておかないと、ブラウザから実際に出力したHTMLのページを開く時に閲覧でなくダウンロードになってしまいます。

SAMテンプレート

template.yml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
BucketName:
Description: Load template and output destination of generated html file
Type: String
Default: {バケット名}
Resources:
GenerateHtml:
Type: AWS::Serverless::Function
Properties:
FunctionName: generate_html
CodeUri: './functions/generate_html/index.js'
Handler: index.handler
MemorySize: 128
Runtime: nodejs10.x
Timeout: 300
Policies:
- AWSLambdaBasicExecutionRole
- AmazonS3FullAccess
Environment:
Variables:
BUCKET_NAME: !Ref BucketName
Layers:
- !Ref EjsLayer
GenerateHtmlFuncLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${GenerateHtml}
RetentionInDays: 7
EjsLayer:
Type: AWS::Serverless::LayerVersion
Properties:
LayerName: ejsLayer
ContentUri:
Bucket: !Ref BucketName
Key: 'source/layer/ejs-layer.zip'
CompatibleRuntimes:
- nodejs10.x
RetentionPolicy: Retain

Lambdaパッケージ化

$ sam package \
--template-file cfn/template.yml \
--s3-bucket {バケット名} \
--s3-prefix source \
--output-template-file ./output/packaged.yml

Lambdaデプロイ

$ sam deploy \
--stack-name generate-html \
--template-file ./output/packaged.yml \
--capabilities CAPABILITY_NAMED_IAM

テスト

Lambdaコンソールから実際に実行してみます。 テスト を選択してテストイベントを作成します。

イベントのパラメータに以下を設定します。

ステータスが200で返ってきました。bodyにはS3出力先のキーが入っています。

S3コンソールからレスポンスに含まれたキーを開きます。

S3からダウンロードして開いてみると想定通りにHTMLが生成できています。

参考

ejs / npm