Skip to main content
The pnpm build command builds the project into optimized code for deployment to production environments. It compiles TypeScript to JavaScript and removes unnecessary code to improve execution speed.

Basic Usage

pnpm build
Once the build completes, optimized JavaScript files are generated in the dist directory.

Subcommands

You can build API and Web separately.
CommandDescription
pnpm buildFull API + Web build (default, skips Web build if no Web directory)
pnpm build apiBuild API project only
pnpm build webBuild Web project only
πŸ“my-project/
πŸ“dist/ - Build output
πŸ“„JSindex.js
πŸ“models/
πŸ“api/
πŸ“src/ - Original source
πŸ“„JSONpackage.json

Build Process

The build proceeds in the following steps:

1. Remove Existing Build Artifacts

Cleanly removes residual files from previous builds.
βœ“ Build artifacts removed successfully.
Directories removed:
  • dist/ - API build output
  • web/dist/ - Web build output
  • web-dist/ - Copied Web output

2. Prepare SWC Configuration

Determines the SWC configuration file to use for the build.
// Priority
// 1. .swcrc in project root (custom config)
// 2. Sonamu default .swcrc (if none exists)
Using custom config:
# Create .swcrc in project root
touch .swcrc
Using default config:
Using default .swcrc from sonamu package...

3. Build API Project

Compiles TypeScript to JavaScript.
1

Build start

πŸ“¦ API Server
Building production-ready API
/path/to/project
2

Run compilation

swc src -d dist --config-file .swcrc
Build tool: SWC - Rust-based ultra-fast TypeScript compiler
3

Complete

βœ“ build completed (2.5s)
βœ“ API build completed in 2.5s

4. Build Web Project (Optional)

If a Web project exists, builds it and copies to the API project.
1

Build start

🌐 Web Application
Building static web assets
/path/to/project/web
2

Vite build

vite build
Optimizes and bundles static assets.
3

Copy files

web/dist β†’ web-dist
Copies for serving from API server.
4

Complete

βœ“ build completed (8.2s)
βœ“ copy completed (0.1s)
βœ“ Web build completed in 8.3s
Web build output:
  • web/dist/ - Original build result
  • web-dist/ - Copy for serving from API server

Build Configuration

Customizing SWC Configuration

Create a .swcrc file in the project root to customize the build.
.swcrc
{
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "tsx": false,
      "decorators": true
    },
    "target": "es2022",
    "loose": false,
    "externalHelpers": false,
    "keepClassNames": true,
    "transform": {
      "legacyDecorator": true,
      "decoratorMetadata": true
    }
  },
  "module": {
    "type": "es6"
  },
  "sourceMaps": true
}
Key options:
OptionDescriptionDefault
targetCompilation targetes2022
decoratorsDecorator supporttrue
sourceMapsGenerate source mapstrue
keepClassNamesPreserve class namestrue
If no .swcrc file exists, optimized default configuration provided by Sonamu is used.

Build Output

API Build Result

πŸ“dist/
πŸ“„JSindex.js - Entry point
πŸ“models/ - Model files
πŸ“„JSuser.model.js
πŸ“„JSpost.model.js
πŸ“api/ - API logic
πŸ“„JSsonamu.js
πŸ“entities/ - Entity definitions
πŸ“„JSentities.js
πŸ“practices/ - Practice scripts
πŸ“„JSp1-test.js
Features:
  • TypeScript β†’ JavaScript conversion
  • Decorator transformation complete
  • Source maps included (.js.map)
  • Import path resolution complete

Web Build Result (Optional)

πŸ“web-dist/
πŸ“„HTMLindex.html - HTML entry
πŸ“assets/ - Optimized assets
πŸ“„JSindex-abc123.js
πŸ“„CSSindex-def456.css
πŸ“„ICOfavicon.ico
Features:
  • Code minification
  • Asset hashing (cache busting)
  • Tree shaking (remove unused code)

Build Optimization

1. Separate Type Checking

To speed up builds, perform type checking separately.
# Type check only (fast)
pnpm tsc --noEmit

# Build only (no type check)
pnpm build

2. Incremental Builds

Use the development server to rebuild only changed files.
# Full build (slow)
pnpm build

# HMR development server (fast)
pnpm dev

3. Cache Utilization

SWC automatically uses build cache.
# First build (slow)
pnpm build  # 5 seconds

# Second build (fast)
pnpm build  # 2 seconds

Troubleshooting

Build Failure

Problem: Build fails with TypeScript error
Error: Cannot find module 'some-module'
Solution:
# 1. Type check
pnpm tsc --noEmit

# 2. Check dependencies
pnpm install

# 3. Reinstall node_modules
rm -rf node_modules
pnpm install

SWC Configuration Error

Problem: .swcrc configuration error
Error: Invalid .swcrc configuration
Solution:
# Restore to default config
rm .swcrc
pnpm build

Web Build Failure

Problem: Vite build error
Error: Could not resolve './some-file'
Solution:
# Move to Web project directory
cd web

# Check dependencies
pnpm install

# Test Web build only
pnpm build

# Return to root and build all
cd ..
pnpm build

Running After Build

After build completes, run the production server with the start command.
# Build
pnpm build

# Run
pnpm start
Production execution features:
  • Fast startup: Run pre-compiled JavaScript
  • Low memory: No TypeScript transformation overhead
  • Source map support: Show original file location on errors

Direct Execution

You can also run directly with Node.js instead of pnpm start:
node --enable-source-maps dist/index.js
Option description:
  • --enable-source-maps: Show original TypeScript file location on errors
  • dist/index.js: Built entry point

Loading Environment Variables

When environment variables are needed:
# Load .env file with dotenv
node -r dotenv/config --enable-source-maps dist/index.js

# Or specify directly
NODE_ENV=production PORT=3000 node --enable-source-maps dist/index.js

CI/CD Configuration

Setting up CI/CD pipelines enables automatic building and deployment whenever code is pushed. Various tools like GitHub Actions, GitLab CI, Jenkins, etc. can be used.

Why is CI/CD Needed?

BenefitDescription
AutomationEliminate manual build/deploy tasks
ConsistencyAlways build the same way
Fast feedbackImmediately detect build failures
SafetyDeploy only after tests pass
TraceabilityPreserve all deployment records

GitHub Actions

GitHub Actions is a CI/CD platform integrated with GitHub. Define workflows by writing YAML files in the .github/workflows/ directory.
.github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]  # Run when pushing to main branch

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      # 1. Checkout code
      - uses: actions/checkout@v3

      # 2. Install pnpm
      - uses: pnpm/action-setup@v2
        with:
          version: 8

      # 3. Setup Node.js (enable pnpm cache)
      - uses: actions/setup-node@v3
        with:
          node-version: 20
          cache: 'pnpm'

      # 4. Install dependencies
      - run: pnpm install

      # 5. Build
      - run: pnpm build

      # 6. Deploy
      - name: Deploy
        run: |
          # Upload build output with rsync
          rsync -avz dist/ server:/app/
Key step descriptions:
1

Code checkout

Use actions/checkout@v3 to fetch repository code.
2

Install pnpm

Install pnpm package manager with pnpm/action-setup@v2.
3

Setup Node.js

Install Node.js and enable pnpm cache with actions/setup-node@v3. Cache makes dependency installation much faster.
4

Install dependencies

Install project dependencies with pnpm install.
5

Build

Perform production build with pnpm build.
6

Deploy

Upload build output to server. Various methods like rsync, scp, FTP can be used.

Adding Tests

Run tests before deployment to catch bugs early:
.github/workflows/deploy.yml
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
        with:
          version: 8
      - uses: actions/setup-node@v3
        with:
          node-version: 20
          cache: 'pnpm'
      - run: pnpm install
      - run: pnpm test  # Run tests

  build:
    needs: test  # Build only after tests pass
    runs-on: ubuntu-latest
    steps:
      # ... build steps

Environment-based Deployment

Separate Staging and Production environments:
on:
  push:
    branches:
      - develop    # Deploy to Staging
      - main       # Deploy to Production

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      # ... build steps

      - name: Deploy to Staging
        if: github.ref == 'refs/heads/develop'
        run: |
          rsync -avz dist/ staging-server:/app/

      - name: Deploy to Production
        if: github.ref == 'refs/heads/main'
        run: |
          rsync -avz dist/ prod-server:/app/

Docker

Using Docker guarantees a consistent execution environment. Development, staging, and production all run in the same environment.
Dockerfile
# Stage 1: Build stage
FROM node:20-alpine AS builder

WORKDIR /app

# Install pnpm
RUN npm install -g pnpm

# Install dependencies (copy only package.json first for caching)
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

# Copy source code
COPY . .

# Build
RUN pnpm build

# Stage 2: Production stage
FROM node:20-alpine

WORKDIR /app

# Install pnpm
RUN npm install -g pnpm

# Install production dependencies only
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --prod --frozen-lockfile

# Copy build output
COPY --from=builder /app/dist ./dist

# Environment variables
ENV NODE_ENV=production

# Expose port
EXPOSE 3000

# Run
CMD ["pnpm", "start"]
Benefits of multi-stage builds:
BenefitDescription
Small image sizeExclude build tools from final image
Fast deploymentSmaller images have faster pull/push
SecurityRemove unnecessary development tools
Layer cachingReuse if dependencies unchanged

Docker Compose

Use Docker Compose to run with database:
docker-compose.yml
version: '3.8'

services:
  api:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:password@db:5432/myapp
    depends_on:
      - db

  db:
    image: pgvector/pgvector:pg16
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_DB=myapp
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
Run:
# Build and run
docker-compose up --build

# Run in background
docker-compose up -d

# Stop
docker-compose down

Performance Comparison

Production builds are much faster and more efficient than development server.
MetricDevelopment ServerProduction BuildDifference
Startup time2-3s0.5s4-6x faster
Memory usage200MB100MB50% reduction
Request handlingSlowerFaster2-3x faster
File sizeOriginalCompressed30-50% smaller
Why production is faster:
  1. Pre-compiled: Don’t transform TypeScript in real-time
  2. Code optimization: Remove unnecessary code, compression
  3. No HMR overhead: No file watching and reloading costs
  4. Production mode: Node.js and dependencies run in optimized mode

Actual Performance Measurement

Compare development server and production build performance directly:
# Measure development server startup time
time pnpm dev

# Measure production build + startup time
time (pnpm build && pnpm start)
API response time comparison:
# Development server
curl -w "\n%{time_total}s\n" http://localhost:3000/api/users
# Example: 0.05s

# Production
curl -w "\n%{time_total}s\n" http://localhost:3000/api/users
# Example: 0.02s

Next Steps