Skip to main content

Architecture overview

This project implements a full-stack web application with a React frontend and Express backend, containerized using a multi-stage Docker build process. The architecture is designed for production deployment with optimized build steps and minimal container size.

System components

The application consists of three main components:
  1. React frontend - A client-side application built with React 19.2.0
  2. Express backend - A Node.js API server using Express 5.1.0
  3. Docker container - Multi-stage build process for optimized production deployment

Component interaction

The system follows a traditional client-server architecture:
1

Client request

Users access the application through their web browser
2

Express server

The Express backend serves both the API endpoints and static frontend files
3

API response

Backend processes requests and returns data via RESTful endpoints
4

React rendering

The React frontend receives data and updates the user interface

Multi-stage Docker build process

The application uses a three-stage Docker build to optimize the final production image:

Stage 1: Frontend build

FROM node:18-alpine AS frontend-build

WORKDIR /app

# Install frontend dependencies
COPY client/package*.json ./client/
RUN cd client && npm install

# Build frontend
COPY client ./client
RUN cd client && npm run build
This stage:
  • Uses Node.js 18 Alpine for a lightweight base image
  • Installs dependencies from client/package.json
  • Runs npm run build to create production-optimized static files
  • Outputs built files to client/build directory

Stage 2: Backend build

FROM node:18-alpine AS backend-build

WORKDIR /app

# Install backend dependencies
COPY server/package*.json ./server/
RUN cd server && npm install

# Copy backend source
COPY server ./server
This stage:
  • Prepares the Express server with production dependencies
  • Installs only the packages needed for runtime (express, cors)
  • Copies the server source code

Stage 3: Production image

FROM node:18-alpine

WORKDIR /app

# Copy backend from backend-build stage
COPY --from=backend-build /app/server ./server

# Copy built frontend from frontend-build stage
COPY --from=frontend-build /app/client/build ./server/client/build

# Expose backend port
EXPOSE 5000

# Start backend server
CMD ["node", "server/index.js"]
The final production image:
  • Combines artifacts from both build stages
  • Places the built React app in server/client/build
  • Exposes port 5000 for the Express server
  • Starts the application with a single command: node server/index.js
The multi-stage build ensures the final image only contains production dependencies and compiled code, significantly reducing the image size and attack surface.

Port configuration

The application runs on port 5000 by default:
  • Configurable via the PORT environment variable
  • Falls back to port 5000 if not specified
  • Exposed in the Dockerfile for container networking

Container structure

In the production container, the file structure is:
/app/
├── server/
│   ├── index.js          # Express server entry point
│   ├── package.json      # Backend dependencies
│   ├── node_modules/     # Runtime dependencies
│   └── client/
│       └── build/        # Built React app (static files)
This structure allows the Express server to serve the static React files while also providing API endpoints.

Deployment benefits

Optimized size

Multi-stage builds eliminate build tools and source files from the final image

Single container

Both frontend and backend run in one container, simplifying deployment

Consistent environment

Node.js 18 Alpine provides a stable, lightweight runtime

Fast builds

Separate build stages allow Docker to cache layers efficiently

Next steps

Frontend architecture

Learn about the React application structure

Backend architecture

Explore the Express server implementation

Build docs developers (and LLMs) love