2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS Lambda(Node.js)においてmiddyを使ってAmazon RDSに接続する方法

Last updated at Posted at 2020-08-03

AWS Lambda(Node.js)において、middyを使ってAmazon RDSに接続する方法を紹介します。

はじめに

データベースエンジン

この記事ではデータベースエンジンとしてAurora MySQLを使用している場合の例を示しますが、他のデータベースエンジンの場合も基本的な実装は変わりません。

プログラミング言語

この記事ではソースコードをTypeScriptで書いているので型定義をインストールしたり型アノテーションを記述したりしていますが、JavaScriptで書く場合は不要です。

ミドルウェア

middyでRDSに接続するために、@middy/db-managerを使います。
@middy/db-managerでは、データベースクライアントとしてKnex.jsが使われます。

接続情報をAWS Secrets Managerで管理していない場合

接続情報をAWS Secrets Managerで管理していない場合、ミドルウェアとして@middy/db-managerのみを使います。

パッケージをインストール

middyを使うために、@middy/coreパッケージをインストールします。

$ npm i -d @middy/core

middyでRDSに接続するために、@middy/db-managerパッケージをインストールします。

$ npm i -d @middy/db-manager

また、データベースエンジンに応じたパッケージをインストールします。
今回はAurora MySQLなので、mysqlパッケージをインストールします。

$ npm i -d mysql

Lambdaの型定義として、@types/aws-lambdaパッケージをインストールします。

$ npm i -D @types/aws-lambda

ハンドラーを作成

middyを用いて、ハンドラーをつくります。

src/index.ts
import lambda from 'aws-lambda'
import Knex from 'knex'
import middy from '@middy/core'
import dbManager from '@middy/db-manager'

type Event = any
interface Context extends lambda.Context {
  db: Knex
}

const handler = middy(async (
  event: Event,
  context: Context
): Promise<void> => {
  const users = await context.db.select('*').from('users')
  console.log(users)
})

handler.use(dbManager({
  config: {
    client: 'mysql',
    connection: {
      host: '127.0.0.1',
      port: 3306,
      user: 'your_database_user',
      password: 'your_database_password',
      database: 'myapp_test'
    }
  }
}))

export { handler }

詳しく解説していきます。

interface Context extends lambda.Context {
  db: Knex
}

@middy/db-managerを使うとコンテキストのdbプロパティにKnexインスタンスが割り当てられるので、dbプロパティを持つContextを定義しています。

const handler = middy(async (
  event: Event,
  context: Context
): Promise<void> => {
  const users = await context.db.select('*').from('users')
  console.log(users)
})

ハンドラーをmiddy関数でラップすることで、middyfy(middy化)しています。
ハンドラー内では、context.dbからKnex.jsのAPIを用いてデータベース操作が行なえます。

handler.use(dbManager({
  config: {
    client: 'mysql',
    connection: {
      host: '127.0.0.1',
      port: 3306,
      user: 'your_database_user',
      password: 'your_database_password',
      database: 'myapp_test'
    }
  }
}))

ハンドラーで、ミドルウェアとしてdbManagerを使っています。
configには、Knex.jsの設定オブジェクトを渡します。
ここでは例示のために接続情報をべた書きしていますが、実際には環境変数やAWS Secrets Managerで管理すべきでしょう。

接続情報をAWS Secrets Managerで管理している場合

接続情報をAWS Secrets Managerで管理している場合、ミドルウェアとして@middy/db-manager@middy/secrets-managerを使います。

パッケージをインストール

middyを使うために、@middy/coreパッケージをインストールします。

$ npm i -d @middy/core

middyでRDSに接続するために、@middy/db-managerパッケージをインストールします。

$ npm i -d @middy/db-manager

また、データベースエンジンに応じたパッケージをインストールします。
今回はAurora MySQLなので、mysqlパッケージをインストールします。

$ npm i -d mysql

middyでSecrets Managerに接続するために、@middy/secrets-managerパッケージをインストールします。

$ npm i -d @middy/secrets-manager

Lambdaの型定義として、@types/aws-lambdaパッケージをインストールします。

$ npm i -D @types/aws-lambda

ハンドラーを作成

middyを用いて、ハンドラーをつくります。

src/index.ts
import lambda from 'aws-lambda'
import Knex from 'knex'
import middy from '@middy/core'
import dbManager from '@middy/db-manager'
import secretsManager from '@middy/secrets-manager'

type Event = any
interface Context extends lambda.Context {
  db: Knex
}

const MIDDY_RDS_SECRET_KEY = 'MIDDY_RDS_SECRET'

const handler = middy(async (
  event: Event,
  context: Context
): Promise<void> => {
  const users = await context.db.select('*').from('users')
  console.log(users)
})

handler.use(secretsManager({
  secrets: {
    [MIDDY_RDS_SECRET_KEY]: 'secret_name'
  },
  cache: true,
  throwOnFailedCall: true
}))

handler.use({
  before: (handler, next) => {
    interface Secret {
      host: string
      port: number
      username: string
      password: string
      dbname: string
    }
    interface Connection {
      host: string
      port: number
      user: string
      password: string
      database: string
    }
    interface Context extends lambda.Context {
      [MIDDY_RDS_SECRET_KEY]: Secret | Connection
    }
    const context = handler.context as Context
    const secret = context[MIDDY_RDS_SECRET_KEY] as Secret
    context[MIDDY_RDS_SECRET_KEY] = {
      host: secret.host,
      port: secret.port,
      user: secret.username,
      password: secret.password,
      database: secret.dbname
    }
    return next()
  }
})

handler.use(dbManager({
  config: {
    client: 'mysql'
  },
  secretsPath: MIDDY_RDS_SECRET_KEY,
  removeSecrets: true
}))

export { handler }

詳しく解説していきます。

interface Context extends lambda.Context {
  db: Knex
}

@middy/db-managerを使うとコンテキストのdbプロパティにKnexインスタンスが割り当てられるので、dbプロパティを持つContextを定義しています。

const handler = middy(async (
  event: Event,
  context: Context
): Promise<void> => {
  const users = await context.db.select('*').from('users')
  console.log(users)
})

ハンドラーをmiddy関数でラップすることで、middyfy(middy化)しています。
ハンドラー内では、context.dbからKnex.jsのAPIを用いてデータベース操作が行なえます。

handler.use(secretsManager({
  secrets: {
    [MIDDY_RDS_SECRET_KEY]: 'secret_name'
  },
  cache: true,
  throwOnFailedCall: true
}))

ハンドラーで、ミドルウェアとしてsecretsManagerを使っています。
secretsでは、コンテキストのどのプロパティに対してSecrets Managerのどのシークレットを割り当てるかを指定します。
cachetrueにすることで、キャッシュを有効化しています。
throwOnFailedCalltrueにすることで、シークレットの取得に失敗した場合にエラーをスローするようにしています。

handler.use({
  before: (handler, next) => {
    interface Secret {
      host: string
      port: number
      username: string
      password: string
      dbname: string
    }
    interface Connection {
      host: string
      port: number
      user: string
      password: string
      database: string
    }
    interface Context extends lambda.Context {
      [MIDDY_RDS_SECRET_KEY]: Secret | Connection
    }
    const context = handler.context as Context
    const secret = context[MIDDY_RDS_SECRET_KEY] as Secret
    context[MIDDY_RDS_SECRET_KEY] = {
      host: secret.host,
      port: secret.port,
      user: secret.username,
      password: secret.password,
      database: secret.dbname
    }
    return next()
  }
})

Secrets Managerに保存されているシークレットを、Knex.jsに渡す形式に変換しています。
シークレットの形式は、データベースエンジンによって異なります。1

handler.use(dbManager({
  config: {
    client: 'mysql'
  },
  secretsPath: MIDDY_RDS_SECRET_KEY,
  removeSecrets: true
}))

ハンドラーで、ミドルウェアとしてdbManagerを使っています。
secretsPathには、Secrets Managerのシークレットが割り当てられているコンテキストのプロパティを指定します。
removeSecretstrueにすることで、データベースに接続後にコンテキストからシークレットを削除しています。

  1. Lambda ローテーション関数の作成に使用できる AWS テンプレート - AWS Secrets Manager

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?