🐶

Next.js × Prisma × PostgreSQLの開発環境をdockerを使って構築する

2023/10/12に公開

初めに

Next.jsでのアプリケーション開発を行うにあたって、環境構築をした際の備忘録を記していきます。

この記事でやらないこと

  • Next.js、Prisma、Dockerについての詳しい解説

手順

  1. Next.jsアプリケーションを作成
  2. PostgreSQLのコンテナ作成
  3. Prismaのインストール
  4. dbコンテナ立ち上げ時にcreate文を実行するようにする
  5. makeコマンドでdocker compose 〇〇が立ち上がるようにする

動作環境

  • Mac Apple M1 Pro
  • Docker version 24.0.5
  • Docker Compose version v2.20.2-desktop.1

いざ、環境構築

ここからは実際にコンテナを作成して環境構築していきます。
※あえて順を追って書いているため、結論だけ知りたいねん、という方はページ下部までジャンプしていただけるといいかと思います。

Next.jsのプロジェクトを作成

npx create-next-app next-prisma-postgresql
// 以下の設定はお好みで
✔ Would you like to use TypeScript with this project? … No / Yes
✔ Would you like to use ESLint with this project? … No / Yes
✔ Would you like to use `src/` directory with this project? … No / Yes
✔ Would you like to use experimental `app/` directory with this project? … No / Yes
✔ What import alias would you like configured? … @/*

以下のコマンドでNext.jsがlocalhost:3000で立ち上がることを確認します(ローカルPCにNode環境がインストールされている場合)

npm run dev

PostgreSQLのコンテナを定義

docker-compose.ymlをプロジェクトのルートに作成し、以下のように記述します

services:
 db:
   image: postgres:15
   hostname: db
   env_file:
     - ./.env
   networks:
     - app_network
   environment:
     - TZ=Asia/Tokyo
     - POSTGRES_DB=$DB_NAME
     - POSTGRES_USER=$DB_USER
     - POSTGRES_PASSWORD=$DB_PASS
     - PGDATA=/var/lib/postgresql/data/pgdata
   ports:
     - 5432:5432
   volumes:
     - db_data:/var/lib/postgresql/data
     - ./sql:/docker-entrypoint-initdb.d

networks:
 app_network:
   driver: bridge

.envをルートディレクトリに作成
ここに設定している値は各環境によって自由に設定してください。

DB_HOST=db
DB_NAME=mydb
DB_USER=admin
DB_PASS=password

試しにプロジェクト直下でdocker compose upと打ってみると、以下のようにdbのコンテナが立ち上がります。

xxx@yyyzzz next-prisma-postgresql % docker compose up
[+] Running 14/14
 ✔ db 13 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                                                                                                 9.6s 
   ✔ e886f0f47ef5 Already exists                                                                                                                                                       0.0s 
   ✔ bf49dd051338 Pull complete                                                                                                                                                        0.6s 
   ✔ 2c6efaa7c024 Pull complete                                                                                                                                                        0.9s 
   ✔ aaa6d3dac3d5 Pull complete                                                                                                                                                        0.9s 
   ✔ 0302c98903c9 Pull complete                                                                                                                                                        1.8s 
   ✔ 82c9ca236580 Pull complete                                                                                                                                                        1.8s 
   ✔ 15a5ae6b0cba Pull complete                                                                                                                                                        1.8s 
   ✔ a868fa6ad308 Pull complete                                                                                                                                                        2.1s 
   ✔ 1a3bad4a399a Pull complete                                                                                                                                                        6.3s 
   ✔ 3c605283dfa7 Pull complete                                                                                                                                                        6.3s 
   ✔ 5dab6cff0193 Pull complete                                                                                                                                                        6.3s 
   ✔ 47d8471cbd01 Pull complete                                                                                                                                                        6.3s 
   ✔ 030e872d86fa Pull complete                                                                                                                                                        6.3s 
[+] Running 2/2
 ✔ Network next-prisma-postgresql_app_network  Created                                                                                                                                 0.1s 
 ✔ Container next-prisma-postgresql-db-1       Created  

コンテナにNode環境を作成

ルートディレクトリにDockerfileを作成します。

touch Dockerfile

Dockerfileに、以下のように記述します。

FROM node:18-alpine

RUN apk add g++ make py3-pip

WORKDIR /app/

COPY ./ /app/
RUN apk add --no-cache git
RUN npm install -g npm@9.7.2
RUN npm install -g node-gyp
RUN npm upgrade --save --legacy-peer-deps
RUN npm install

先ほどdbコンテナを定義したdocker-compose.ymlに、以下のようにappを追記します。

services:
  app:
    tty: true
    networks:
      - app_network
    build:
      context: .
    ports:
      - "3000:3000"
    volumes:
      - ./:/app
      - node_modules:/app/node_modules
    depends_on:
      - db

  db:
    image: postgres:15
    hostname: db
    env_file:
      - ./.env
    networks:
      - app_network
    environment:
      - TZ=Asia/Tokyo
      - POSTGRES_DB=$DB_NAME
      - POSTGRES_USER=$DB_USER
      - POSTGRES_PASSWORD=$DB_PASS
      - PGDATA=/var/lib/postgresql/data/pgdata
    ports:
      - 5432:5432
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./sql:/docker-entrypoint-initdb.d

networks:
  app_network:
    driver: bridge
volumes:
  node_modules:

コンテナをビルドして、起動します

docker compose up --build

appコンテナの中に入り、npm run devを実行すると、アプリケーションが起動します。

docker container exec -it next-prisma-postgresql-app-1 sh
npm run dev

起動を確認したら、一旦npm run devは停止しておきます。

Prismaをインストール

ここからはappコンテナ内で作業します。
より詳しい内容ついては公式Docをご参照ください。
https://www.prisma.io/docs/getting-started

prismaとts-nodeをインストール

npm install prisma ts-node --save-dev

Prismaのセットアップを行います

npx prisma init

以下のようにPrismaディレクトリが作成されます

先ほど作成した.envにも、コメントとDATABASE_URLの記述が追加されています。

DB_HOST=db
DB_NAME=mydb
DB_USER=admin
DB_PASS=password

# This was inserted by `prisma init`:
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"

DATABASE_URLの値を、以下のように書き換えます。

DATABASE_URL="postgresql://admin:password@db:5432/mydb?schema=public"

ポイント

  • docker-compose.ymlでdbのhostnameをdbとしているため、localhostdbに書き換えます
  • admin, password, mydb などと記述しているところは.envの上部に記載しているDB_NAMEやDB_USER, DB_PASSに設定している値です
  • docker-compose.ymlで、dbのportを5432に指定する場合は、db:1234 といった感じで任意のportに書き換えてください
  • .envは忘れずに.gitignoreに加えておきましょう

dbコンテナ立ち上げ時にcreate文を実行するようにする

今のままでは、DBにはなんのテーブルも定義されていません。
そのため、DBコンテナのビルド時に自動的にcreate文を実行し、テーブルを定義するようにします。

create_table.sql

drop table if exists users cascade;
-- ----------------------------------------------------------------------------------------------------

create table users (
    id serial not null,             -- ID
    name text not null,             -- ユーザー名
    created_at timestamp not null,  -- 作成日時
    primary key (id)
);

一旦コンテナをdownし、再度起動します。

docker compose down
docker compose up --build

ログに以下のようにCREATE TABLEと出力されるとOK。

next-prisma-postgresql-db-1   | server started
next-prisma-postgresql-db-1   | CREATE DATABASE
next-prisma-postgresql-db-1   | 
next-prisma-postgresql-db-1   | 
next-prisma-postgresql-db-1   | 
next-prisma-postgresql-db-1   | /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/create_table.sql
next-prisma-postgresql-db-1   | 
next-prisma-postgresql-db-1   | DROP TABLE
next-prisma-postgresql-db-1   | psql:/docker-entrypoint-initdb.d/create_table.sql:1: NOTICE:  table "users" does not exist, skipping
next-prisma-postgresql-db-1   | CREATE TABLE
next-prisma-postgresql-db-1   | 
next-prisma-postgresql-db-1   | 

Prisma Studioを開く

再びappコンテナに以下のコマンドで入り、テーブルの構成をPrismaに反映します。

docker container exec -it next-prisma-postgresql-app-1 sh
npx prisma db pull

実際に反映されているかはschema.prismaを確認するか、npx prisma studioで確認できます
……が、今のdocker-compose.ymlの記述ではnpx prisma studioを起動してもlocalhost:5555では何も見ることができません。

docker-compose.ymlに以下を追記します

services:
  app:
    tty: true
    networks:
      - app_network
    build:
      context: .
    ports:
      - "3000:3000"
      - "5555:5555" # ここを追加
    volumes:
      - ./:/app
      - node_modules:/app/node_modules
    depends_on:
      - db

  db:
    image: postgres:15
    hostname: db
    env_file:
      - ./.env
    networks:
      - app_network
    environment:
      - TZ=Asia/Tokyo
      - POSTGRES_DB=$DB_NAME
      - POSTGRES_USER=$DB_USER
      - POSTGRES_PASSWORD=$DB_PASS
      - PGDATA=/var/lib/postgresql/data/pgdata
    ports:
      - 5432:5432
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./sql:/docker-entrypoint-initdb.d

networks:
  app_network:
    driver: bridge
volumes:
  node_modules:

再度コンテナを立ち上げ直すと、確認できるようになりました。

makeコマンドを使ってコンテナが立ち上がるようにする

ここからは少しオマケ感。
以下のように、サーバーデプロイ用のdocker-composeとローカル開発用のdokcer-composeを分けたい、ということがありました。

docker-compose.deploy.yml
docker-compose.local.yml

この場合、それぞれコマンドを打つのは面倒なので、Makefileとして名前をつけて定義します。
ルートディレクトリで以下のコマンドを実行します。

touch Makefile

作成したファイルに、コマンド名と実行内容を記述

.PHONY: up
up-local:
	docker compose -f docker-compose.local.yml up --build
up-deploy:
	docker compose -f docker-compose.deploy.yml up --build

.PHONY: up-d
up-d-local:
	docker compose -f docker-compose.local.yml up -d --build

.PHONY: down
down-local:
	docker compose -f docker-compose.local.yml down
down-deploy:
	docker compose -f docker-compose.deploy.yml down

.PHONY: build
build-local:
	docker compose -f docker-compose.local.yml build
build-deploy:
	docker compose -f docker-compose.deploy.yml build	

こうすることで、以下のようなコマンドでコンテナが立ち上がるようになります。

make up-local

種類が増えたり、一つのコマンドで複数コンテナを立ち上げたい、などといった場合に便利です。
makeコマンドはMacには標準で入っているみたいですが、windowsには入っていないようなので適宜インストーるしてください。(WSL2上ですがUbuntu20.04では入ってるみたいでした。なければインストールしていただければと思います)

結果

最終的なdocker-compose.ymlは以下です

services:
  app:
    tty: true
    networks:
      - app_network
    build:
      context: .
    ports:
      - "3000:3000"
      - "5555:5555"
    volumes:
      - ./:/app
      - node_modules:/app/node_modules
    depends_on:
      - db

  db:
    image: postgres:15
    hostname: db
    env_file:
      - ./.env
    networks:
      - app_network
    environment:
      - TZ=Asia/Tokyo
      - POSTGRES_DB=$DB_NAME
      - POSTGRES_USER=$DB_USER
      - POSTGRES_PASSWORD=$DB_PASS
      - PGDATA=/var/lib/postgresql/data/pgdata
    ports:
      - 5432:5432
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./sql:/docker-entrypoint-initdb.d

networks:
  app_network:
    driver: bridge
volumes:
  node_modules:

sql/create_table.sql

drop table if exists users cascade;
-- ----------------------------------------------------------------------------------------------------

create table users (
    id serial not null,             -- ID
    name text not null,             -- ユーザー名
    created_at timestamp not null,  -- 作成日時
    primary key (id)
);

.env

DB_HOST=db
DB_NAME=mydb
DB_USER=admin
DB_PASS=password

# This was inserted by `prisma init`:
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="postgresql://admin:password@db:5432/mydb?schema=public"

テーブルの作成方法に関しては、コンテナ立ち上げ時以外にもschemaに定義してdb pushする方法でも良いかと。
一旦ここまでで、次回はVPSにデプロイする用のコンテナ構成なども最近いろいろ試行錯誤していたので記事にしていければと思います!

記事で作成したリポジトリ
https://github.com/KeitaUenishi/next-prisma-postgresql-docker-sample

Discussion