Home >Web Front-end >JS Tutorial >Dockerize PERN-TypeScript App Using Prisma ORM With Docker Compose

Dockerize PERN-TypeScript App Using Prisma ORM With Docker Compose

Patricia Arquette
Patricia ArquetteOriginal
2024-10-17 06:29:29751browse

Dockerize PERN-TypeScript App Using Prisma ORM With Docker Compose

Introduction

This article won't go into detailed explanations. Instead, I’ll just provide the code snippets you need to dockerize a PERN stack application. If you want a more detailed explanation, check out this article where I’ve covered everything in more depth.

The Project Structure

pern-project/
--- frontend/
------ .dockerignore
------ frontend.dockerfile
------ ...
--- backend/
------ .dockerignore
------ backend.dockerfile
------ ...
--- docker-compose.yml

Code Backend

Create a node.js-express.js app first:

mkdir backend && cd backend
npm init -y

Install all the necessary dependencies from npm:

npm install express dotenv cors

Install Typescript related dev dependencies as well:

npm install --save-dev typescript ts-node @types/express @types/node @types/cors

Generate tsconfig.json file:

tsc --init

Replace everything inside tsconfig.json with this piece of code in the following:

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "rootDir": "./src",
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts", "data-types.d.ts"]
}

Create src folder and index.ts file inside src folder. So, now the backend folder structure:

backend/
--- node_modules/
--- src/
------ index.ts
--- package.json
--- tsconfig.json
--- ...

Integrate Postgres With Prisma

Install Prisma and Prisma Client first:

npm i @prisma/client
npm i --save-dev prisma

Generate a prisma folder:

npx prisma init

Write a User model in the schema.prisma file:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id        String   @id @default(uuid())
  name      String
  username  String
  email     String
  password  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@map("users")
}

Set DATABASE_URL in the .env file:

DATABASE_URL=postgresql://postgres:postgres@db:5432/pern_db?schema=public

Create a file prismadb.ts in the src folder:

import { PrismaClient } from "@prisma/client"
import "dotenv/config"

// Extend the global object with PrismaClient
declare global {
  var prisma: PrismaClient | undefined
}

// Prevent multiple instances of Prisma Client in development
const prisma = global.prisma || new PrismaClient()

if (process.env.NODE_ENV !== "production") global.prisma = prisma

export default prisma

Define /register endpoint in the index.ts file. The index.ts file:

import express, { Request, Response } from "express"
import "dotenv/config"
import cors from "cors"
import { corsOptions } from "./constants/config"

const app = express()
const PORT = process.env.PORT || 3000

app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(cors({
    const corsOptions = {
    origin: process.env.CLIENT_URL || 'http://localhost:5173',
    credentials: true,
  }
}))

app.get("/", (req: Request, res: Response) => {
  res.json({
    message: "Hello, TypeScript with Express! Updated!",
  })
})

app.post("/register", async (req: Request, res: Response) => {
  const { name, username, email, password } = req.body

  await prisma.user.create({
    data: {
      name,
      username,
      email,
      password,
    },
  })

  res.json({
    message: "User created successfully",
  })
})

app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`)
})

Backend Dockerfile

Create a file naming backend.dockerfile in the root of the backend directory and write:

FROM node:20
WORKDIR /app
COPY package*.json .
RUN npm install
COPY prisma ./prisma
RUN npx prisma generate
COPY . .
EXPOSE 3000
RUN npm install -g nodemon ts-node
CMD ["nodemon", "src/index.ts"]

To exclude node_modules, create a .dockerignore file:

node_modules

Code Frontend

Create the frontend:

npm create vite@latest frontend -- --template react-ts

Make API call in the React app:

// App.tsx

import { FormEvent, useEffect, useState } from "react"
....

function App() {
  const [name, setName] = useState("")
  const [username, setUsername] = useState("")
  const [email, setEmail] = useState("")
  const [password, setPassword] = useState("")

  const saveUser = async (e: FormEvent) => {
    e.preventDefault()
    await fetch("http://localhost:3000/register", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name,
        username,
        email,
        password,
      }),
    })
      .then((res) => res.json())
      .then((data) => console.log(data))
  }

  useEffect(() => {
    fetch("http://localhost:3000")
      .then((res) => res.json())
      .then((data) => console.log(data))
  }, [])

  return (
    <>
      <form onSubmit={saveUser}>
        <input
          type="text"
          onChange={(e) => setName(e.target.value)}
          placeholder="Enter your name"
        />
        <input
          type="text"
          onChange={(e) => setUsername(e.target.value)}
          placeholder="Enter your username"
        />
        <input
          type="email"
          onChange={(e) => setEmail(e.target.value)}
          placeholder="Enter your email"
        />
        <input
          type="password"
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Enter your password"
        />
        <button type="submit">Submit</button>
      </form>
    </>
  )
}

export default App

Frontend Dockerfile

Create a file naming frontend.dockerfile in the root of the frontend directory and write:

FROM node:20
WORKDIR /app
COPY package*.json .
RUN npm install
EXPOSE 5173
COPY . .
CMD [ "npm", "run", "dev" ]

Docker-Compose file

Add the following in the .env file of the backend folder:

// backend/.env

POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=pern_db

DATABASE_URL=postgresql://postgres:postgres@db:5432/pern_db?schema=public

The docker-compose.yml file:

services:
  frontend:
    container_name: frontend
    build:
      context: ./frontend
      dockerfile: frontend.dockerfile
    ports:
      - "5173:5173"
    networks:
      - pern_net
    volumes:
      - ./frontend:/app
      - /app/node_modules
    depends_on:
      - server

  backend:
    container_name: backend
    build:
      context: ./backend
      dockerfile: backend.dockerfile
    env_file:
      - ./backend/.env
    ports:
      - "3000:3000"
    networks:
      - pern_net
    volumes:
      - ./backend:/app
      - /app/node_modules
    depends_on:
      - db

  db:
    container_name: db
    image: postgres:14
    restart: always
    ports:
      - "5432:5432"
    networks:
      - pern_net
    env_file:
      - ./backend/.env
    volumes:
      - pgdata:/data/db

networks:
  pern_net:
    driver: bridge

volumes:
  pgdata: {}

Run this command:

docker compose up -d

Migrate the prisma schema:

docker backend -it backend npx prisma migrate dev --name init

The above is the detailed content of Dockerize PERN-TypeScript App Using Prisma ORM With Docker Compose. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn