Serverless frameworkで`prisma migrate`を実行する
この記事を書こうと思った理由
serverless frameworkとPrismaで開発をすることになったのですが、
スキーマを反映させるためのコマンドprisma migrate
を
lambdaで実行するのにとても苦労したので、自分のした処理をここにまとめます。
本当はもっと良い方法があると思うので、知ってましたら教えてください。
実行手順
1.Prismaインストール
PrismaとPrisma CLIをインストールします。
yarn add prisma @prisma/client
Prisma CLIをインストールしたら、Prismaの設定を初期化します。
yarn prisma init
このコマンドにより、prismaディレクトリが作成され、schema.prismaファイルが配置されます。
2. データベース接続の設定
.envファイルを開き、使用するデータベースに合わせて接続URLを設定します。
例えば、MySQLを使用する場合は以下のようになります。
DATABASE_URL="mysql://<USER>:<PASSWORD>@<HOST>:3306/<DATABASE>"
prismaはDATABASEの中身は操作できますが、直接DATABASEを作成はできないので、
ない場合は以下のようにコマンドで作成しましょう(今回はmysqlです。)
ssh -i <キーペアのpath> ec2-user@<踏み台のIP>
mysql -h <HOST> -u <USER> -p
CREATE DATABASE `sample`;
SHOW DATABASES;
3. Prismaスキーマの定義
prisma/schema.prisma
ファイルが自動生成されているはずなので、
以下のように設定する
datasource db {
provider = "mysql" // お好きなものを設定しましょう
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
binaryTargets = ["rhel-openssl-3.0.x", "native"] // lambda環境向けの設定
output = "../src/generated/client"
}
// テーブル設定はお好きな内容にしてください
model prefectures {
prefecture_cd Int @id
prefecture_name String
}
output = "../src/generated/client"
について
outputをsrcディレクトリの配下にしているのは、デプロイ時に他のコードと一緒にビルドプロセスに含ませるのが簡単になるというメリットがあります。
4. serverless.yml設定
service: sample
# versionが4以降は有料になるので、3を指定してます。
frameworkVersion: '3'
# 環境変数を.envファイルから読み込む設定
useDotenv: true
plugins:
- serverless-esbuild #ビルド時にファイルの軽量化、パフォーマンス向上が期待できるのでesbuildを使用
custom:
stackName: 'sample'
tableName: 'sample'
defaultStage: dev
esbuild:
bundle: true
minify: true
exclude: ['aws-sdk', 'src/__test__/*']
packager: yarn
packagePath: './package.json'
target: 'node20'
external:
- prisma
logRetentionInDays: ${env:LOG_RETENTION_IN_DAYS, 14}
provider:
name: aws
region: ${opt:region, "ap-northeast-1"}
runtime: nodejs20.x
tags:
sample_tag: sample
memorySize: 512 #(MB)
timeout: 300 #(秒)
iam:
role:
name: 'sample-lambdaRole'
statements:
- Effect: Allow
Action:
- lambda:InvokeFunction
Resource: "*"
vpc:
securityGroupIds:
- ${env:SECURITY_GROUP_ID}
subnetIds:
- ${env:SUBNET_IDS_AZ}
environment:
DATABASE_URL: ${env:DATABASE_URL}
# 以下の設定をすることで複数lambdaがある場合、個別にパッケージされます。
# それによりデプロイ時間短縮、メモリ使用量の削減などのメリットを得られます。
package:
individually: true
functions:
prismaMigrate:
handler: src/prismaMigrate/index.handler
name: central-link-api-${env:STAGE}-prismaMigrate
package:
patterns:
- 'prisma/**'
- 'node_modules/prisma/**/*'
- 'node_modules/@prisma/**/*'
environment:
PRISMA_QUERY_ENGINE_LIBRARY_PATH: ${env:PRISMA_QUERY_ENGINE_LIBRARY_PATH}
PRISMA_CLI_PATH: ${env:PRISMA_CLI_PATH}
PRISMA_SCHEMA_PATH: ${env:PRISMA_SCHEMA_PATH}
serverless frameworkでデプロイした場合にはlambdaにはhandler:
で指定したコードしか反映されません。
ですが、prismaのコマンドを使用するには以下でまとめている要素がlambda内にないと実行できないので
package:patterns:
で含めるべきprismaのコードを指定しています。
prismaコマンドで必要そうな要素
- prisma cliコマンドファイル群
- prisma/migrations配下のmigration.sqlファイル
(npx prisma migrate dev
コマンドで自動生成されます) - schema.prismaファイル
- prisma クエリエンジン(基本はクエリ実行のためのものだが、一部DBとの対話に使用されるとのこと。自分は実際にmigrationコマンドを実行するとクエリエンジンがないというエラーが発生しました)
※あくまで個人で試行錯誤して思ったことなので、正確性は保証できません。
5. lambda処理の設定
import { PrismaClient } from '../generated/client';
import { exec } from 'child_process';
import util from 'util';
process.env.PRISMA_QUERY_ENGINE_LIBRARY = process.env.PRISMA_QUERY_ENGINE_LIBRARY_PATH; // PRISMA_QUERY_ENGINE_LIBRARYのパスを明示するために追加
const PRISMA_CLI_PATH = process.env.PRISMA_CLI_PATH;
const PRISMA_SCHEMA_PATH = process.env.PRISMA_SCHEMA_PATH;
const prisma = new PrismaClient();
const execPromise = util.promisify(exec);
export const handler = async () => {
try {
await prisma.$connect();
const { stdout, stderr } = await execPromise(`node ${PRISMA_CLI_PATH} migrate deploy --schema=${PRISMA_SCHEMA_PATH}`);
// 標準出力と標準エラー出力をログに出力
console.log('Migration stdout:', stdout);
console.log('Migration stderr:', stderr);
return {
statusCode: 200,
body: JSON.stringify({
message: 'Migration completed successfully',
}),
};
} catch (error) {
console.error('Error during migration:', error as Error);
return {
statusCode: 500,
body: JSON.stringify({
message: 'Migration failed',
error: (error as Error).message,
}),
};
} finally {
await prisma.$disconnect();
}
};
参考までにそれぞれの環境変数の値は以下の通り
PRISMA_QUERY_ENGINE_LIBRARY_PATH=/var/task/node_modules/@prisma/engines/libquery_engine-rhel-openssl-3.0.x.so.node
PRISMA_CLI_PATH=/var/task/node_modules/prisma/build/index.js
PRISMA_SCHEMA_PATH=/var/task/prisma/schema.prisma
6. デプロイしてみる
以下のコマンドを実行
npx prisma migrate dev
するとprisma/migrationsのディレクトリ配下にmigration.sqlファイルが生成されます。
lambdaをデプロイしてみる
sls deploy
7. 最後にlambdaを実行してみる
実行して"libquery_engine-rhel-openssl-3.0.x.so.node"が存在しないなどのエラーが出た時には以下のコマンドを実行して再度、デプロイいましょう
PRISMA_CLI_BINARY_TARGETS="rhel-openssl-3.0.x" npx prisma generate
番外編:Github Actionsに自動デプロイ
自分がGithubActionsで自動デプロイを実装した時のコードです。
name: 🚀Deploy to STG (Auto)
on:
push:
branches:
- develop
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22.3.0'
cache: 'yarn'
- run: node --version
- name: Install dependencies
run: yarn install
- name: Prisma Generate
run: npx prisma generate --schema=./prisma/schema.prisma
- name: Download and Replace Prisma Engine
run: PRISMA_CLI_BINARY_TARGETS="rhel-openssl-3.0.x" npx prisma generate
- name: Deploy to AWS
run: npx serverless@3 deploy --stage stg --config serverless.yml --verbose
env:
AWS_ACCESS_KEY_ID: ${{ secrets.STG_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.STG_AWS_SECRET_ACCESS_KEY }}
DATABASE_URL: ${{ secrets.STG_DATABASE_URL }}
STAGE: ${{ vars.STG_STAGE }}
SECURITY_GROUP_ID: ${{ secrets.STG_SECURITY_GROUP_ID }}
SUBNET_IDS_AZ1: ${{ secrets.STG_SUBNET_IDS_AZ }}
PRISMA_QUERY_ENGINE_LIBRARY_PATH: ${{ vars.STG_PRISMA_QUERY_ENGINE_LIBRARY_PATH }}
PRISMA_CLI_PATH: ${{ vars.STG_PRISMA_CLI_PATH }}
PRISMA_SCHEMA_PATH: ${{ vars.STG_PRISMA_SCHEMA_PATH }}
お疲れ様でした!!!
Discussion