Search code examples
postgresqldockerdocker-composedockerfileprisma

How do I run Prisma migrations in a Dockerized GraphQL + Postgres setup?


I'm new to using Prisma as well as Dockerizing my setup. I would like to specify my data model using Prisma, have Postgres as my database and use that in a GraphQL API (my current API uses apollo-server-express) that also deals with authentication and roles etc.

What I have now is a simple docker-compose.yml and a Dockerfile of my GraphQL API:

docker-compose.yml

services:
  api:
    build: ./api
    env_file:
      - .env
    volumes:
      - ./api:/usr/src/app
    ports:
      - ${API_PORT}:${API_PORT}
    command: npm start

Dockerfile

# Latest LTS version
FROM node:14

# Set default values for environment variables
ENV API_PORT=3001

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
COPY package*.json ./
RUN npm install

# Bundle app source
COPY . .

# Bind port
EXPOSE ${API_PORT}

# Start server
CMD ["npm", "start"]

How would I go about using Prisma and Postgres in this setup, where the migrations happen in some containerized way, instead of me executing a Prisma command manually in the CLI?

Pointing out my misconceptions, hints or feedback is appreciated! Thank you


Solution

  • Late answer to my own question: Like @Athir suggested I now separated the two processes and created two docker-compose.yml files: one named docker-compose.migrate.yml that's responsible for running the migration and one named docker-compose.yml which is the main application.

    My docker-compose.migrate.yml:

    version: '3'
    services:
      prisma-migrate:
        container_name: prisma-migrate
        build: ./api/prisma
        env_file:
          - .env
        environment:
          DB_HOST: <secret>
        depends_on:
          - db
    
      db:
        image: postgres:13
        container_name: db
        restart: always
        env_file:
          - .env
        environment:
          DB_PORT: 5432
        ports:
          - ${DB_PORT}:5432
        volumes:
          - ${POSTGRES_VOLUME_DIR}:/var/lib/postgresql/data
    

    With the following Prisma Dockerfile:

    FROM node:14
    
    RUN echo $DATABASE_URL
    
    WORKDIR /app
    
    COPY ./package.json ./
    COPY . ./prisma/
    
    RUN chmod +x ./prisma/wait-for-postgres.sh
    
    RUN npm install
    RUN npx prisma generate
    
    RUN apt update
    RUN apt --assume-yes install postgresql-client
    
    # Git will replace the LF line-endings with CRLF, causing issues while executing the wait-for-postgres shell script
    # Install dos2unix and replace CRLF (\r\n) newlines with LF (\n)
    RUN apt --assume-yes install dos2unix
    RUN dos2unix ./prisma/wait-for-postgres.sh
    
    CMD sh ./prisma/wait-for-postgres.sh ${DB_HOST} ${POSTGRES_USER} npx prisma migrate deploy && npx prisma db seed --preview-feature
    

    EDIT: wait-for-postgres.sh:

    #!/bin/sh
    # wait-for-postgres.sh
    
    set -e
      
    host="$1"
    user="$2"
    shift
    shift
    cmd="$@"
      
    until PGPASSWORD=$POSTGRES_PASSWORD psql -h "$host" -U "$user" -c '\q'; do
      >&2 echo "Postgres is unavailable - sleeping"
      sleep 1
    done
      
    >&2 echo "Postgres is up - executing command"
    
    exec $cmd
    

    EDIT: example .env file:

    # ---- DB ----
    DB_HOST=localhost
    DB_PORT=5432
    DB_SCHEMA=example
    
    POSTGRES_DB=example
    POSTGRES_USER=example
    POSTGRES_VOLUME_DIR=/path/where/you/want/to/store
    POSTGRES_PASSWORD=example
    
    DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DB_HOST}:${DB_PORT}/${POSTGRES_DB}?schema=${DB_SCHEMA}