In today's fast-paced development landscape, building robust and scalable web applications requires a powerful tech stack. Next.js, Prisma, and Docker are three essential tools that can help you achieve this goal.
In this blog, we’ll explore how to build, integrate, and deploy a powerful web application using Next.js, Prisma, and Docker. By combining Next.js for the frontend, Prisma as an efficient database management layer, and Docker for seamless containerization, we can create a scalable and production-ready application.
We’ll walk through setting up each component, configuring a multi-stage Docker build for optimized deployment, and managing services with Docker Compose.
Next.js, Prisma, and Docker are a powerful combination for building and deploying modern web applications. Each tool plays a critical role in the stack:
Next.js is a React-based framework that provides server-side rendering and other optimizations, making it ideal for dynamic applications.
Prisma simplifies database management by acting as an ORM (Object Relational Mapper), allowing you to write clean, type-safe code to manage your database interactions. Prisma integrates well with PostgreSQL, MySQL, and other databases, making it flexible and scalable.
Docker is a containerization tool that encapsulates your entire app (including dependencies and environment configurations) into portable containers. This is especially useful for creating consistent development, testing, and production environments.
Together, Next.js, Prisma, and Docker form a solid foundation for building and deploying scalable applications that are both developer-friendly and highly adaptable.
• Next.js as the frontend: Next.js handles the frontend of your app, serving pages dynamically. By using the app directory in Next.js, you can organize your components, API routes, and utility functions in a logical structure.
• Prisma as the database layer: Prisma manages the database schema, data modeling, and interactions with the database. It allows you to work with data seamlessly by defining models in the prisma schema file, then generating the Prisma client, which lets you run queries directly from your Next.js app.
• Docker for containerization: Docker simplifies deployment by packaging your entire Next.js app, Prisma schema file, and database configuration into a Docker container. This ensures consistency across various environments and helps avoid compatibility issues. You can further configure multiple services using docker compose, allowing you to set up containers for the frontend, backend, and database components.
By combining these technologies, you can create a scalable and maintainable web app. In the following sections, you’ll learn how to install and configure each component step-by-step, from creating a new Next.js project to setting up Docker containers for seamless deployment.
1npx create-next-app my-nextjs-prisma-docker-app
This command sets up the app directory, containing essential files and folders. Once created, navigate to your project folder:
1cd my-nextjs-prisma-docker-app
1DATABASE_URL=postgresql://username:password@localhost:5432/mydatabase
This DATABASE_URL
connects your app to the database using the datasource db in Prisma.
To get started with Next.js, you first need to initialize a new project. This can be done easily with the create next app command. This command sets up a basic Next.js application with all the essential configurations and folder structures in place, allowing you to dive into development right away.
Run the following command in your terminal:
1npx create-next-app my-nextjs-app
This command will create a new folder, my-nextjs-app, containing all the files and configurations required for a Next.js app. Once the app is created, you can navigate to this folder:
1cd my-nextjs-app
The new app directory includes the following key folders and files:
• pages/: The primary location for page components. Each file here automatically maps to a route in your app.
• public/: Stores static files like images and other assets.
• styles/: Houses CSS files, such as global styles and module-based styles.
• next.config.js: Contains custom configurations for the Next.js app.
• package.json: Lists all dependencies and scripts for running and building the app.
This structure provides an efficient way to manage your application files. Additionally, with Next.js 13 and later, you may use the app directory, which introduces a more streamlined way of managing routes and components, especially in larger applications.
Environment variables are essential in any application for securely storing sensitive information such as API keys, database connection strings, and other configuration values. In a Next.js and Prisma project, environment variables help you manage the connection to your database, API configurations, and other settings that vary between development and production environments.
By convention, these variables are stored in an env file located at the root of your project. This file is then referenced by both Next.js and Prisma. Since it contains sensitive information, make sure to add it to your gitignore file to prevent accidental exposure when pushing your code to a git repository.
Create an env file: Start by creating a new file called .env.local in the root directory of your Next.js project. This file will hold your environment variables for the local development environment.
Add Database Connection URL: In the env file, specify the DATABASE_URL
for Prisma to connect to your database. This will be used as the datasource db in your Prisma configuration.
1DATABASE_URL="postgresql://username:password@localhost:5432/mydatabase"
Here, replace username, password, localhost, and mydatabase with the details specific to your database. If you’re using Docker, you can define the hostname as the postgres container name specified in docker compose.
DATABASE_URL
variable in your prisma schema file. When you run the npx prisma generate or npx prisma migrate dev commands, Prisma will automatically reference the database specified in your env file.1datasource db { 2 provider = "postgresql" 3 url = env("DATABASE_URL") 4}
In Next.js, you can access these variables directly using the process.env object:
1export default function HomePage() { 2 console.log(process.env.DATABASE_URL); // Example usage 3 return <h1>Welcome to My Next.js App</h1>; 4}
In this setup, Next.js will load the environment variables securely, allowing you to reference your database or other configurations seamlessly throughout your app.
Prisma is a powerful tool that simplifies database management by providing an ORM (Object Relational Mapper) for efficient interaction with your database. To start integrating Prisma with your Next.js project, follow these steps:
1npm install prisma @prisma/client
1npx prisma init
After running this command, you’ll see a new folder named prisma and an env file at the root of your project.
In your env file, specify the DATABASE_URL
for connecting Prisma to your database. Prisma supports databases like PostgreSQL, MySQL, SQLite, and others. Here’s an example connection string for PostgreSQL:
1DATABASE_URL="postgresql://username:password@localhost:5432/mydatabase"
Replace username, password, localhost, and mydatabase with the details specific to your database setup. In the prisma schema file, configure Prisma to use this database as its datasource db.
1datasource db { 2 provider = "postgresql" 3 url = env("DATABASE_URL") 4}
This setup allows Prisma to connect to the specified database and begin managing it. With this setup, Prisma will pull connection details directly from your env file, helping secure your application by keeping sensitive information out of your codebase.
With Prisma, you define your database schema in the prisma schema file using a simple syntax that Prisma will translate into database tables and columns.
In the schema.prisma file, you define models to represent the structure of your data. For example, if you want a User model, you might define it like this:
1model User { 2 id Int @id @default(autoincrement()) 3 name String 4 email String @unique 5 createdAt DateTime @default(now()) 6}
Each model represents a database table, with each property corresponding to a column. The above User model includes fields for id, name, email, and createdAt. The @id and @default(autoincrement()) annotations configure Prisma to use an auto-incremented integer as the primary key.
Once you’ve defined your models in the prisma schema file, you’ll need to create and apply migrations to sync your database with the new model structure.
1npx prisma migrate dev --name init
This command will create a new folder with migration SQL files in the Prisma directory. These files define the SQL commands to set up the database schema based on your Prisma models.
After defining models and running migrations, Prisma generates a type-safe Prisma Client for accessing your database. You can import and use this client throughout your Next.js app to perform database operations.
To regenerate Prisma Client, run:
1npx prisma generate
You can now import Prisma Client in your Next.js API routes or server-side functions. Here’s an example of querying the User table in an API route:
1// pages/api/users.js 2import { PrismaClient } from "@prisma/client"; 3 4const prisma = new PrismaClient(); 5 6export default async function handler(req, res) { 7 const users = await prisma.user.findMany(); // Fetch all users 8 res.json(users); 9}
This handler retrieves all User entries from the database and sends them in the response. The Prisma Client provides an easy-to-use API for running complex SQL queries without needing to write raw SQL.
Dockerizing your Next.js and Prisma application makes it easy to deploy and manage by encapsulating the entire setup within a container. For this, you’ll create a multi-stage Dockerfile to optimize the build and separate dependencies for the development and production environments.
A multi-stage Dockerfile allows you to reduce the image size by splitting the build process into distinct stages. Here’s an example Dockerfile for a Next.js and Prisma application:
1# Stage 1: Install dependencies and build the application 2FROM node:16 AS builder 3 4# Set working directory 5WORKDIR /app 6 7# Copy package.json and package-lock.json 8COPY package.json package-lock.json ./ 9 10# Install dependencies 11RUN npm install 12 13# Copy all source code into the container 14COPY . . 15 16# Build the Next.js application 17RUN npm run build 18 19# Stage 2: Production image 20FROM node:16-alpine AS production 21 22# Set working directory 23WORKDIR /app 24 25# Copy only the necessary files from the build stage 26COPY --from=builder /app/node_modules ./node_modules 27COPY --from=builder /app/.next ./.next 28COPY --from=builder /app/package.json ./ 29 30# Expose the port that the app will run on 31EXPOSE 3000 32 33# Start the application 34CMD ["npm", "start"]
This Dockerfile does the following:
Install dependencies: The first stage (builder) installs all the required dependencies and builds the Next.js application.
Copy only necessary files: The second stage (production image) copies only essential files (like the .next build folder and node_modules
) to keep the final Docker image slim and optimized.
Expose and run the app: Exposes port 3000 for the app to run and starts the application.
Prisma relies on environment variables (such as the DATABASE_URL
) to connect to the database. Add these variables in an env file or directly within the Dockerfile. Additionally, you may need to install the Prisma CLI in the builder stage to handle migrations and schema generation.
To ensure Prisma works in the Docker environment, add DATABASE_URL
to your env file and ensure it points to the database service in your Docker Compose setup.
Docker Compose allows you to define and manage multiple containers (services) in a single configuration file, which is particularly useful when running Next.js, Prisma, and your database as separate containers.
1version: '3.8' 2 3services: 4 # Database service (e.g., PostgreSQL) 5 db: 6 image: postgres:13 7 environment: 8 POSTGRES_USER: postgres 9 POSTGRES_PASSWORD: postgres 10 POSTGRES_DB: mydatabase 11 volumes: 12 - db_data:/var/lib/postgresql/data 13 networks: 14 - app-network 15 16 # Prisma service (Next.js application) 17 app: 18 build: 19 context: . 20 dockerfile: Dockerfile 21 environment: 22 DATABASE_URL: postgres://postgres:postgres@db:5432/mydatabase 23 NODE_ENV: production 24 ports: 25 - "3000:3000" 26 depends_on: 27 - db 28 networks: 29 - app-network 30 31volumes: 32 db_data: 33 34networks: 35 app-network:
In this setup:
• The db service runs PostgreSQL as the database. It’s configured with the environment variables POSTGRES_USER
, POSTGRES_PASSWORD
, and POSTGRES_DB
for database setup and uses a named volume (db_data
) to persist data.
• The app service builds the Dockerfile we created for Next.js. The DATABASE_URL
points to the db service for database access. Ports are mapped to expose the application on port 3000, and the service depends on db to ensure the database is available when the app starts.
Docker Compose allows you to manage the network configuration between containers. In the above file:
• Networks: Both services are assigned to the app-network, ensuring they can communicate.
• Dependencies: The depends_on
configuration ensures the database service starts before the Next.js application.
To start all the services, simply use:
1docker-compose up --build
This command builds and starts each container according to the configuration in the docker-compose.yml file. You should see logs from both the database and the Next.js application in the terminal.
After setting up the Dockerfile and Docker Compose configurations, you’re ready to build and run your containerized Next.js and Prisma application. Here’s how to get started:
1docker-compose build
This command will process the Dockerfile for each service and create images for both the Next.js app (app service) and the database (db service).
1docker-compose up
This will launch both the app and db containers. The Next.js app should now be running and accessible via http://localhost:3000, while the database service will be accessible internally within the container network.
When deploying a containerized Next.js and Prisma application, there are common issues you may encounter. Here are some troubleshooting tips:
• Database Connection Issues: Ensure that your DATABASE_URL
environment variable in the env file or docker-compose.yml is correctly set. If you’re using a postgres container, make sure the db service is referenced correctly in the URL (postgres://postgres:postgres@db:5432/mydatabase).
• Prisma Client Not Found: If Prisma Client is not generated, make sure to run npx prisma generate either as part of the Dockerfile or manually within the container after running docker-compose up. You can use an exec command to run npx prisma generate in the running container:
1docker-compose exec app npx prisma generate
• Outdated Migrations: If you make changes to the Prisma schema file, you’ll need to rerun migrations. Use:
1docker-compose exec app npx prisma migrate dev
• File Permissions Errors: Sometimes Docker containers, especially those using the alpine version, may have file permission issues. You can resolve this by ensuring the correct permissions on your node_modules
and other key directories in the Dockerfile.
To optimize your Docker Compose setup for a production environment, a few changes are recommended:
NODE_ENV
environment variable is set to production for your app service. This disables Next.js dev features that aren’t needed in production and optimizes performance.1environment: 2 DATABASE_URL: postgres://postgres:postgres@db:5432/mydatabase 3 NODE_ENV: production
Minimize Image Size: You might consider using a smaller base image, like node:16-alpine, in the Dockerfile to reduce the size of the final container. Additionally, remove any dev dependencies and only copy what’s necessary for production.
Handle Static Files: If your app includes a lot of static assets, consider using a CDN to serve those files in production rather than hosting them within the Docker container.
Once configured for production, it’s crucial to test the deployment. Here are some tips to verify everything is working correctly:
1docker-compose up -d
This will keep the services running in the background, allowing you to continue working or inspect logs as needed.
1docker-compose logs -f
1docker-compose exec app npx prisma studio
This opens a UI in your browser where you can inspect and manipulate data.
In conclusion, this article provided a step-by-step guide on building and deploying a robust application with Next.js, Prisma, and Docker. By setting up Next.js as the frontend framework, Prisma for seamless database management, and Docker for containerized deployment, we created a streamlined, scalable, and production-ready app. We covered configuring a multi-stage Docker build, managing services with Docker Compose, and ensuring secure and efficient database connections. The main takeaway is that using Next.js, Prisma, and Docker together allows for a modern, scalable approach to web development, making it easy to deploy and maintain applications across environments.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.