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
Once the build completes, optimized JavaScript files are generated in the dist directory.
Subcommands
You can build API and Web separately.
Command Description 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
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 API Build Configuration
Determines the tsdown configuration file to use for the API build.
// Priority
// 1. tsdown.config.ts in project root (custom override)
// 2. Sonamu default tsdown API config (if none exists)
Using custom config :
# Create tsdown.config.ts in project root
touch tsdown.config.ts
Using default config :
Using default tsdown API config from sonamu package...
3. Build API Project
Compiles TypeScript to JavaScript.
Build start
📦 API Server
Building production-ready API
/path/to/project
Run compilation
tsc --noEmit && pnpm exec tsdown --config /path/to/tsdown.config.ts
Build tool : tsdown with OXC/Rolldown
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.
Build start
🌐 Web Application
Building static web assets
/path/to/project/web
Vite build
Optimizes and bundles static assets.
Copy files
Copies for serving from API server.
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 API Build Configuration
Create a tsdown.config.ts file in the project root to override the default API build.
import { defineConfig } from "tsdown" ;
export default defineConfig ({
clean: true ,
entry: {
index: "src/index.ts" ,
} ,
format: "esm" ,
platform: "node" ,
sourcemap: "inline" ,
target: "esnext" ,
unbundle: true ,
}) ;
Key options :
Option Description Default targetCompilation target esnextdecoratorsDecorator support truesourceMapsGenerate source maps inlineunbundlePreserve file layout true
If no tsdown.config.ts file exists, Sonamu falls back to its bundled default API build config.
Build Output
API Build Result
Features :
TypeScript → JavaScript conversion
Decorator transformation complete
Source maps included (.js.map)
Import path resolution complete
Web Build Result (Optional)
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
tsdown reuses incremental dependency analysis across builds.
# 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
tsdown Configuration Error
Problem : tsdown.config.ts configuration error
Error: Failed to load tsdown config
Solution :
# Restore to default config
rm tsdown.config.ts
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?
Benefit Description Automation Eliminate manual build/deploy tasks Consistency Always build the same way Fast feedback Immediately detect build failures Safety Deploy only after tests pass Traceability Preserve 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 :
Code checkout
Use actions/checkout@v3 to fetch repository code.
Install pnpm
Install pnpm package manager with pnpm/action-setup@v2.
Setup Node.js
Install Node.js and enable pnpm cache with actions/setup-node@v3. Cache makes dependency
installation much faster.
Install dependencies
Install project dependencies with pnpm install.
Build
Perform production build with pnpm build.
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.
# 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 :
Benefit Description Small image size Exclude build tools from final image Fast deployment Smaller images have faster pull/push Security Remove unnecessary development tools Layer caching Reuse if dependencies unchanged
Docker Compose
Use Docker Compose to run with database:
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
Production builds are much faster and more efficient than development server.
Metric Development Server Production Build Difference Startup time 2-3s 0.5s 4-6x faster Memory usage 200MB 100MB 50% reduction Request handling Slower Faster 2-3x faster File size Original Compressed 30-50% smaller
Why production is faster :
Pre-compiled : Don’t transform TypeScript in real-time
Code optimization : Remove unnecessary code, compression
No HMR overhead : No file watching and reloading costs
Production mode : Node.js and dependencies run in optimized mode
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
start Run the built server
dev Return to development server