Briswell Tech Blog

ブリスウェルのテックブログです

Middyを使用することで、Lambda向けのMiddlewareの作成を簡易化する。

I. はじめに

こんにちは。
ブリスウェルのSonです。

Lambdaを使ってNode.jsアプリを開発する際、いくつかの困っていることに直面しています。

・複雑なエラー処理の実装
・認証や権限の実装
・安全な環境変数の管理
・パフォーマンス最適化

その改善策としてフレームワークを探していたところ、Middyが良さそうだと感じて、実際に使ってみた。

II. Middyとは

① 概要

AWS LambdaのためのスタイリッシュなNode.jsミドルウェアエンジンです。
Lambdaコードを整理し、重複を外し、ビジネスロジックに集中する。

② メリット

  • すぐに使用できる豊富な公式のミドルウェアとユーティリティと一緒に提供されています。
  • 最小のコアを保持することで、関数のサイズを小さくし、Cold Startをコントロールします。
  • 簡単に拡張できます。

③ 機能紹介

1. warmup

Lambdaのコールドスタートの問題を軽減するために使用されます。

使用例

const middy = require('@middy/core')
const warmup = require('@middy/warmup')

const isWarmingUp = (event) => event.isWarmingUp === true

const lambdaHandler = (event, context, cb) => {
  /* ... */
}

export const handler = middy()
  .use(warmup({ isWarmingUp }))
  .handler(lambdaHandler)

2. do-not-wait-for-empty-event-loop

タイムアウトが発生しないようにするために使用されます。例えば、データベースへの接続する時。

使用例

import middy from '@middy/core'
import doNotWaitForEmptyEventLoop from '@middy/do-not-wait-for-empty-event-loop'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(doNotWaitForEmptyEventLoop({ runOnError: true }))
  .handler(lambdaHandler)

3. http-json-body-parser

HTTPリクエストを自動的に解析し、JSON形式の本文をオブジェクトに変換する。

使用例

import middy from '@middy/core'
import httpJsonBodyParser from '@middy/http-json-body-parser'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(httpJsonBodyParser())
  .handler(lambdaHandler)

4. validator

自動的に着信イベントと送信レスポンスをカスタムスキーマに対して検証します。

注意のこと:
eventSchemaまたはresponseSchemaの少なくとも1つが必要です。

使用例

import middy from '@middy/core'
import validator from '@middy/validator'
import httpJsonBodyParser from '@middy/http-json-body-parser'
import { transpileSchema } from '@middy/validator/transpile'

const lambdaHandler = (event, context) => {
  /* ... */
}

const eventSchema = {
  type: 'object',
  required: ['body'],
  properties: {
    body: {
      type: 'object',
      required: ['name', 'email'],
      properties: {
        name: { type: 'string' },
        email: { type: 'string', format: 'email' }
      }
    }
  }
}

export const handler = middy()
  .use(httpJsonBodyParser())
  .use(
    validator({
      eventSchema: transpileSchema(eventSchema)
    })
  )
  .handler(lambdaHandler)

5. http-content-encoding

レスポンスのHTTP Content-Encodingヘッダーを設定し、レスポンス本文を圧縮します。

使用例

import middy from '@middy/core'
import httpContentNegotiation from '@middy/http-content-negotiation'
import httpContentEncoding from '@middy/http-content-encoding'
import { constants } from 'node:zlib'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(httpContentNegotiation())
  .use(httpContentEncoding({
    br: {
      params: {
        [constants.BROTLI_PARAM_MODE]: constants.BROTLI_MODE_TEXT, // adjusted for UTF-8 text
        [constants.BROTLI_PARAM_QUALITY]: 7
      }
    },
    overridePreferredEncoding: ['br', 'gzip', 'deflate']
  })
  .handler(lambdaHandler)

6. http-cors

Cross-Originリクエストを実行するために必要なAccess-Control-Allow-OriginAccess-Control-Allow-Headers、および Access-Control-Allow-Credentialsを含むHTTP CORSヘッダーをレスポンスオブジェクトに設定するために使用されます。

使用例

import middy from '@middy/core'
import cors from '@middy/http-cors'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(cors())
  .handler(lambdaHandler)

7. secrets-manager

AWS Secrets Managerからパラメーターを取得して、関数ハンドラのcontextのオブジェクトにアサインされる。

注意のこと
・Lambdaは、secretsmanager:GetSecretValueのIAM権限を持っている必要があります。

使用例

import middy from '@middy/core'
import secretsManager from '@middy/secrets-manager'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(
    secretsManager({
      fetchData: {
        apiToken: 'dev/api_token'
      },
      awsClientOptions: {
        region: 'us-east-1'
      },
      setToContext: true
    })
  )
  .handler(lambdaHandler)

8. ssm

AWS Systems Manager Parameter Storeからパラメータを取得して、関数ハンドラのcontextのオブジェクトにアサインされる。

注意のこと
・Lambdaは、ssm:GetParametersのIAM権限を持っている必要があります。

使用例

import middy from '@middy/core'
import { getInternal } from '@middy/util'
import ssm from '@middy/ssm'

const lambdaHandler = (event, context) => {
  /* ... */
}

let globalDefaults = {}
export const handler = middy()
  .use(
    ssm({
      fetchData: {
        accessToken: '/dev/service_name/access_token',
        dbParams: '/dev/service_name/database/'
      },
      cacheExpiry: 15 * 60 * 1000,
      cacheKey: 'ssm-secrets'
    })
  )
  .before(async (request) => {
    const data = await getInternal(
      ['accessToken', 'dbParams', 'defaults'],
      request
    )
    Object.assign(request.context, data)
  })
  .handler(lambdaHandler)

9. sts

他のAWSサービスに接続する際に使用するSTSAWS Security Token Service)資格情報を取得することのようなシーンで使うことがあります。

注意のこと
sts:AssumeRoleのIAM権限が必要です。

使用例

import middy from '@middy/core'
import sts from '@middy/sts'

const lambdaHandler = (event, context) => {
  /* ... */
}

export const handler = middy()
  .use(
    sts({
      fetchData: {
        assumeRole: {
          RoleArn: '...',
          RoleSessionName: ''
        }
      }
    })
  )
  .handler(lambdaHandler)

III. 終わりに

AWS Lambda開発者が開発プロセスを効率化したいと考えている場合には最適なツールです。

Middyは軽量でモジュール化されたアプローチを採用しており、問題を分離し、重複を減らし、Lambda関数の主要なビジネスロジックに焦点を当てることができます。

AWS Lambdaの開発に強力で柔軟なミドルウェアフレームワークをお探しの場合は、ぜひMiddyをご検討ください。