Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 14, 2023 04:21 pm GMT

Containerizing A NextJs Application For Development

Introduction

Containerizing your NextJs application can be an efficient way of developing it, as it provides a more isolated and reproducible environment. In this article, we will go through the steps required to containerize a NextJs application and explain each step in detail.

Prerequisites

Before we begin, you should have a basic knowledge of NextJs and familiarity with Docker CLI and Docker Compose commands.

Note: The docker image we'll be building should not be used in a production environment.

Creating A NextJs Application

The first step is to create a NextJs application. To do this, you can run the following command:

npx create-next-app docker-next

This will create a NextJs application called docker-next in your current working directory.

You will be prompt to decide whether to include TypeScript, TailwindCSS and a few other things. Feel free to choose whichever options as we won't be going through NextJs in depth.

Setting Up Docker

If you are on Windows or Mac, you can directly install docker desktop. If you are on linux, you may want to follow the instructions listed here.

Once you have Docker installed, verify that the Docker CLI and Docker Compose are installed correctly by running the following commands:

docker -vdocker-compose -v

These commands should output the versions of Docker and Docker Compose, respectively.

Containerizing NextJs

We should delete our node_modules folder and package-lock.json file because it helps to reduce the size of the image and ensure that the dependencies in the image are consistent with the ones declared in the package.json file.

Now that we have the necessary tools to run docker, we can start containerizing our NextJs application.

Creating A Dockerfile

The first step is to create a Dockerfile for our application. You can do this by running the following command:

touch Dockerfile

This command will create a new empty file named Dockerfile in your current directory.

The contents of your Dockerfile should look like this.

FROM node:18-alpineWORKDIR /appCOPY package.json ./RUN npm installCOPY . .CMD ["npm", "run", "dev"]

Let's go through each line of this Dockerfile:

  • FROM node:18-alpine: This line specifies the base image to use for our container. We're using the node image with version 18 that is built on top of the alpine Linux distribution.

  • WORKDIR /app: This line sets the working directory for our container to /app. This is important to avoid clashing of folder names between our application and the container's.

  • COPY package.json ./: This line copies the package.json file from our local machine to the /app directory inside the container.

  • RUN npm install: This line installs the dependencies required by our application inside the container.

  • COPY . .: This line copies the entire contents of our application from our local machine to the /app directory inside the container.

  • CMD ["npm", "run", "dev"]: This line specifies the command to run when the container starts. In this case, we're running the npm run dev command, which will start our NextJs development server.

Note: Replace npm with your choice of package manager.

Bulding The Docker Image

Now that we have our Dockerfile, we can build the Docker image by running the following command:

docker build -t docker-next .

This command will build a new Docker image named docker-next from the Dockerfile in our current directory. The -t flag specifies the name of the image, and the . specifies the build context.

Running The Container

To run the container, we can use the following command:

docker run docker-next -p 3000:3000 -v /app/node_modules -v .:/app

Here's what each option means:

  • -p 3000:3000: maps port 3000 of the container to port 3000 of the host machine. This means that you can access the application by navigating to http://localhost:3000 in your web browser.

  • -v /app/node_modules: mounts the /app/node_modules directory in the container as a volume. This is useful because it allows you to take advantage of Docker's caching mechanism. Since node_modules is typically a large directory that doesn't change very often, mounting it as a volume means that it won't have to be rebuilt every time you make changes to your application's code.

  • -v .:/app: mounts the current directory (i.e., the directory where you run the command) as a volume at the /app directory inside the container. This is where your Next.js application code lives.

So in summary, this command runs a Docker container from the docker-next image, maps port 3000 of the container to port 3000 of the host machine, mounts the /app/node_modules directory in the container as a volume, and mounts the current directory as a volume at /app inside the container

Once the container has run, visit localhost:3000 in your browser and you should be greeted with the NextJs default page.

Image description

Having to type this command:

docker run docker-next -p 3000:3000 -v /app/node_modules -v .:/app

every single time, will slowly chip away your sanity. That's where docker compose comes in.

Enter Docker Compose

Docker Compose is a tool for defining and running multi-container Docker applications. It allows you to describe the services that make up your application in a YAML file, and then start and stop those services using a single command.

With Docker Compose, you can also scale your application's services, set up networking between containers, and more. Essentially, Docker Compose makes it easier to manage and deploy multi-container Docker applications.

Creating The Docker Compose File

We can create the docker compose file by running:

touch docker-compose.yml

Paste this into the docker-compose.yml file:

version: '3.5'services:  app:    build:      context: .      dockerfile: Dockerfile    container_name: docker-next    ports:      - '3000:3000'    volumes:      - .:/app      - /app/node_modules

First we define a service called app, that will be build using the Dockerfile located in our root directory and run in a container. The service exposes the container's port 3000 to the host's port 3000 and mounts two volumes:

  • .:app: This will mount the directory where the Docker Compose file is located as a volume in the container at the /app directory. This is useful for development as it allows for live reloading of code changes made on the host machine to be reflected in the container.

  • /app/node_modules: This mounts the node_modules directory in the container as a separate volume. This is done to avoid overriding the dependencies installed by npm during the image build process with the dependencies installed on the host machine.

To start our application, just run the following command:

docker-compose up

Congratulations, you just containerized a NextJs application.

Bonus

There is one caveat developing with a containerized application, which is running cli commands. Specifically, in our case npm commands.

If you need to add new dependencies, you will need to run:

docker-compose run --rm app npm install <package name>

The docker-compose run command allows you to run a one-time command against a service defined in your docker-compose.yml file. The --rm option specifies that the container created from the service should be removed after the command is executed. This option ensures that the container is cleaned up and does not consume resources on your system after it's no longer needed.

In this case, app is the name of the service defined in docker-compose.yml that the command is being run against.

To keep our sanity in check, we'll be building a script to help us run docker-compose, npm and node commands.

Creating A Script To Run Docker Compose, NPM And Node Commands

We will be building a script based on laravel/sail.

Create a file called harbor and make it executable.

touch harborchmod +x harbor

Paste the following into the harbor file:

#!/usr/bin/env bashfunction display_help {  echo "Harbor"  echo  echo "Usage:" >&2  echo "  harbor COMMAND [options] [arguments]"  echo  echo "Unknown commands are passed to the docker-compose binary."  echo  echo "docker-compose Commands:"  echo "  harbor up        Start the application"  echo "  harbor up -d     Start the application in the background"  echo "  harbor stop      Stop the application"  echo "  harbor down      Stop the application and remove related resources"  echo "  harbor restart   Restart the application"  echo "  harbor ps        Display the status of all containers"  echo  echo "Node Commands:"  echo "  harbor node ...         Run a Node command"  echo "  harbor node --version"  echo  echo "Npm Commands:"  echo "  harbor npm ...        Run a Npm command"  echo "  harbor npm test"  echo  echo "Customization:"  echo "  harbor build --no-cache       Rebuild all of the harbor containers"  exit 1}if [ $# -gt 0 ]; then  if [ "$1" == "npm" ]; then    shift 1    docker-compose run --rm app npm "$@"  elif [ "$1" == "node" ]; then    shift 1    docker-compose run --rm app node "$@"  elif [ "$1" == "help" ] || [ "$1" == "--help" ] || [ "$1" == "-h" ]; then    display_help  else    docker-compose -f docker/dev/docker-compose.yml "$@"  fielse  display_helpfi

The display_help function is defined first, which prints out the available commands, options, and arguments to the user.

The script then checks if there is at least one command-line argument ($# -gt 0). If there is, it checks if the first argument is npm or node. If the argument is npm or node, it runs the corresponding command using docker-compose run --rm app. If the argument is help, --help, or -h, it prints out the help information using the display_help function. Otherwise, it passes the command and any arguments to docker-compose to execute.

If there are no command-line arguments, the script also prints out the help information using the display_help function.

The help output will look like this.
Image description

Now we are able to run npm and node commands by using the script that we just created. You can take it up a notch and include conditions for yarn and pnpm or put everything in an npm package and publish it, since it's basically framework agnostic.

Conclusion

Containerizing a Next.js application with Docker provides a more isolated and reproducible environment, making it an efficient way of developing and onboarding new developers.

Hopefully, this article has helped provide some clarity and deepen your understanding regarding docker. If you have any feedback or comments, feel free to leave them in the comments section.

Note: Link to GitHub Repository


Original Link: https://dev.to/scaabel/containerizing-a-nextjs-application-for-development-204d

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To