Skip to main content

Overview

TailStack uses a PNPM workspace-based monorepo structure that enables efficient development across multiple packages. This guide covers the essential workflows for daily development.

Running Development Servers

The Core package (full-stack monorepo) includes a unified dev command that runs both frontend and backend simultaneously.

Start All Services

From the packages/core directory:
pnpm dev
This command uses Concurrently to run multiple dev servers in parallel:
{
  "scripts": {
    "dev": "concurrently \"pnpm --filter ./source/frontend dev\" \"pnpm --filter ./source/Server dev\""
  }
}
What happens:
  • Frontend (Vite) starts on http://localhost:5173
  • Backend (Express) starts on http://localhost:3000
  • Both output logs to the same terminal with color-coded prefixes
Hot Reload: Both frontend and backend support hot module replacement (HMR). Changes are reflected immediately without restarting servers.

Start Individual Services

pnpm --filter ./source/frontend dev
Starts only the React frontend with Vite.

Development URLs

ServiceURLDescription
Frontendhttp://localhost:5173React app with Vite HMR
Backendhttp://localhost:3000Express API server
Backend Healthhttp://localhost:3000/healthAPI health check endpoint

PNPM Workspace Commands

TailStack’s monorepo structure uses PNPM workspaces for package management. Understanding workspace commands is essential for efficient development.

Filtering Packages

Run command in specific package:
pnpm --filter <package-name> <command>
Examples:
# Install dependencies for frontend only
pnpm --filter ./source/frontend install

# Build backend only
pnpm --filter ./source/Server build

# Add dependency to specific package
pnpm --filter ./source/frontend add axios

Running Commands Across Workspace

Run command in all packages:
pnpm -r <command>
Examples:
# Run build in all packages
pnpm -r build

# Run tests in all packages
pnpm -r test

# Clean all packages
pnpm -r clean

Installing Dependencies

1
Install All Dependencies
2
From the root directory:
3
pnpm install
4
This installs dependencies for all packages in the workspace.
5
Install Specific Package
6
Add dependency to a specific package:
7
# Add to frontend
pnpm --filter ./source/frontend add react-query

# Add dev dependency to backend
pnpm --filter ./source/Server add -D jest

# Add dependency to root (affects all packages)
pnpm add -w husky
8
Update Dependencies
9
Update all dependencies:
10
# Update all packages
pnpm update -r

# Update specific package
pnpm --filter ./source/frontend update

Useful PNPM Commands

# List all workspace packages
pnpm list -r --depth 0

# Check for outdated packages
pnpm outdated -r

# Why is a package installed?
pnpm why <package-name>

# Remove package from workspace
pnpm --filter <package-name> remove <dependency>

# Rebuild all node_modules
pnpm install --force
Workspace Flag: Use -w flag when installing root-level dependencies (like Husky, ESLint configs). Without it, PNPM will show an error.

Git Workflow

TailStack enforces strict commit standards using Commitlint and Husky.

Commit Message Format

All commits must follow Conventional Commits specification:
<type>(<scope>): <subject>

<body>

<footer>
Type must be one of:
  • feat - New feature
  • fix - Bug fix
  • docs - Documentation changes
  • style - Code style changes (formatting, semicolons, etc.)
  • refactor - Code refactoring
  • perf - Performance improvements
  • test - Adding or updating tests
  • build - Build system changes
  • ci - CI configuration changes
  • chore - Other changes (dependencies, configs)
  • revert - Revert previous commit

Commit Examples

# Feature with scope
git commit -m "feat(auth): add JWT authentication middleware"

# Bug fix with detailed body
git commit -m "fix(api): resolve memory leak in WebSocket connections

The WebSocket connections were not properly closed on client disconnect,
causing memory accumulation over time.

Fixes #123"

# Breaking change
git commit -m "feat(api): migrate to REST API v2

BREAKING CHANGE: API endpoints now require /v2/ prefix"

Commit Limits

Configured in commitlint.config.cjs:
modules.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'header-max-length': [2, 'always', 150],
    'body-max-line-length': [2, 'always', 250],
  },
};
  • Header: Maximum 150 characters
  • Body lines: Maximum 250 characters each

Pre-commit Checks

Every commit triggers automated checks:
1
Stage Your Changes
2
git add .
3
Attempt Commit
4
git commit -m "feat(api): add user endpoint"
5
Pre-commit Hook Runs
6
The .husky/pre-commit hook automatically executes:
7
🔍 Running pre-commit security checks...
pnpm lint-staged
8
Checks performed:
9
  • Gitleaks scans staged files for secrets
  • If secrets detected, commit is blocked
  • Sensitive data is automatically redacted in output
  • 10
    Commit Message Validation
    11
    The .husky/commit-msg hook validates your commit message:
    12
    pnpm commitlint --edit
    
    13
    If validation passes:
    14
    ✔ Commit message meets Conventional Commits standards
    
    15
    If validation fails:
    16
    ✖ subject may not be empty [subject-empty]
    ✖ type may not be empty [type-empty]
    
    Quick Tip: If you need to bypass hooks temporarily (NOT recommended), use:
    git commit --no-verify -m "emergency fix"
    
    Only use this in genuine emergencies!

    Branch Workflow

    Recommended branching strategy:
    # Create feature branch
    git checkout -b feat/user-authentication
    
    # Make changes and commit
    git add .
    git commit -m "feat(auth): implement JWT tokens"
    
    # Push to remote
    git push origin feat/user-authentication
    
    # Create pull request
    gh pr create --title "Add user authentication" --body "Implements JWT-based auth"
    

    Building for Production

    Build All Packages

    # From root
    pnpm -r build
    

    Build Specific Package

    # Build frontend
    pnpm --filter ./source/frontend build
    
    # Build backend
    pnpm --filter ./source/Server build
    

    Build Output

    • Frontend: Built to source/frontend/dist/
    • Backend: Built to source/Server/dist/ (if using TypeScript compilation)

    Testing

    Run Tests

    # Run all tests
    pnpm -r test
    
    # Run tests for specific package
    pnpm --filter ./source/frontend test
    
    # Run tests in watch mode
    pnpm --filter ./source/frontend test:watch
    

    Test Coverage

    # Generate coverage report
    pnpm -r test:coverage
    

    Environment Variables

    Frontend Environment

    Create .env in source/frontend/:
    VITE_API_URL=http://localhost:3000
    VITE_APP_TITLE=TailStack App
    
    Access in code:
    const apiUrl = import.meta.env.VITE_API_URL;
    

    Backend Environment

    Create .env in source/Server/:
    PORT=3000
    NODE_ENV=development
    DATABASE_URL=postgresql://localhost:5432/mydb
    JWT_SECRET=your-secret-key
    
    Access in code:
    const port = process.env.PORT || 3000;
    
    Security: Never commit .env files to Git. They’re included in .gitignore by default. Use .env.example as a template.

    Debugging

    Frontend Debugging

    VS Code Launch Configuration:
    {
      "type": "chrome",
      "request": "launch",
      "name": "Debug Frontend",
      "url": "http://localhost:5173",
      "webRoot": "${workspaceFolder}/source/frontend"
    }
    

    Backend Debugging

    VS Code Launch Configuration:
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Backend",
      "runtimeExecutable": "pnpm",
      "runtimeArgs": ["--filter", "./source/Server", "dev"],
      "skipFiles": ["<node_internals>/**"],
      "console": "integratedTerminal"
    }
    

    Common Workflows

    Adding a New Feature

    1
    Create Feature Branch
    2
    git checkout -b feat/new-feature
    
    3
    Make Changes
    4
    Edit code in frontend and/or backend.
    5
    Test Locally
    6
    pnpm dev  # Verify changes work
    
    7
    Commit Changes
    8
    git add .
    git commit -m "feat(feature): add new feature implementation"
    
    9
    Push and Create PR
    10
    git push origin feat/new-feature
    gh pr create
    

    Updating Dependencies

    1
    Check for Updates
    2
    pnpm outdated -r
    
    3
    Update Specific Dependency
    4
    pnpm --filter <package> update <dependency>
    
    5
    Test Changes
    6
    pnpm dev
    pnpm -r test
    
    7
    Commit Changes
    8
    git commit -m "chore(deps): update dependencies to latest versions"
    

    Fixing Build Errors

    1
    Clean Everything
    2
    ./scripts/clean.sh
    
    3
    Fresh Install
    4
    ./scripts/install.sh
    
    5
    Rebuild
    6
    pnpm -r build
    
    7
    Verify
    8
    pnpm dev
    

    Performance Tips

    Faster Installations

    # Use TailStack's parallel install script
    ./scripts/install.sh  # 2x faster than standard install
    
    # Skip optional dependencies
    pnpm install --no-optional
    
    # Use frozen lockfile (CI/CD)
    pnpm install --frozen-lockfile
    

    Faster Builds

    # Build in parallel (default with -r flag)
    pnpm -r build
    
    # Build only changed packages (requires Turborepo)
    pnpm build --filter=...^HEAD
    

    Reduce Context Switching

    # Run everything from root
    pnpm dev           # from packages/core
    pnpm -r test       # from root
    pnpm -r build      # from root
    

    Troubleshooting

    Port Already in Use

    # Find process using port 3000
    lsof -i :3000
    
    # Kill process
    kill -9 <PID>
    
    # Or change port in .env
    PORT=3001 pnpm dev
    
    # Rebuild node_modules
    pnpm install --force
    
    # Or use clean script
    ./scripts/clean.sh
    ./scripts/install.sh
    

    Git Hooks Not Working

    # Reinstall Husky
    pnpm husky
    
    # Make hooks executable (Unix)
    chmod +x .husky/*
    

    Next Steps

    Build docs developers (and LLMs) love