new-project-scaffolding
New Project Scaffolding
This skill guides you through scaffolding a new Nx monorepo with a standard structure including backend API, frontend web app, shared types library, and essential tooling (Just, direnv).
Project Structure Overview
{project-name}/
├── apps/
│ ├── backend/ # Node.js/Koa backend API
│ └── webapp/ # React frontend application
├── libs/
│ └── types/ # Shared TypeScript types (@{project}/types)
├── scripts/ # Utility TypeScript scripts (run with vite-node)
├── .envrc # direnv configuration
├── .env.example # Environment variable template
├── .env.local # Local environment variables (gitignored)
├── justfile # Task runner commands
├── nx.json # Nx configuration
├── tsconfig.base.json # Base TypeScript configuration
└── package.json # Root package.json
Scaffolding Steps
Step 1: Create Nx Workspace
npx create-nx-workspace@latest {project-name} \
--preset=ts \
--packageManager=npm \
--nxCloud=skip \
--interactive=false
This creates a minimal TypeScript workspace. The --preset=ts gives us a clean slate to add exactly what we need.
Step 2: Install Required Nx Plugins
cd {project-name}
npm install -D @nx/node @nx/react @nx/webpack @nx/jest @nx/eslint
Step 3: Generate Backend Application
npx nx generate @nx/node:application \
--name=backend \
--directory=apps/backend \
--framework=express \
--projectNameAndRootFormat=as-provided \
--e2eTestRunner=none \
--unitTestRunner=jest
Step 4: Generate Frontend Application
npx nx generate @nx/react:application \
--name=webapp \
--directory=apps/webapp \
--projectNameAndRootFormat=as-provided \
--bundler=webpack \
--style=css \
--routing=true \
--e2eTestRunner=none \
--unitTestRunner=jest
Step 5: Generate Shared Types Library
npx nx generate @nx/js:library \
--name=types \
--directory=libs/types \
--projectNameAndRootFormat=as-provided \
--unitTestRunner=jest \
--bundler=tsc \
--strict
After generation, update libs/types/package.json:
{
"name": "@{project}/types",
"version": "0.0.1",
"private": true,
"type": "module",
"main": "./src/index.ts",
"types": "./src/index.ts",
"exports": {
"./package.json": "./package.json",
".": {
"types": "./src/index.ts",
"import": "./src/index.ts",
"default": "./src/index.ts"
}
},
"dependencies": {
"tslib": "^2.3.0"
}
}
Step 6: Configure TypeScript Path Aliases
Update tsconfig.base.json to include the types library path:
{
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": true,
"importHelpers": true,
"isolatedModules": true,
"lib": ["es2022"],
"module": "esnext",
"moduleResolution": "bundler",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"skipLibCheck": true,
"strict": true,
"target": "es2022",
"baseUrl": ".",
"paths": {
"@{project}/types": ["libs/types/src/index.ts"]
}
}
}
Step 7: Create Scripts Directory
mkdir -p scripts
Create scripts/example-script.ts as a template:
/**
* Example utility script
* Run with: npx vite-node scripts/example-script.ts
*/
const run = async () => {
console.log('Script executed successfully');
process.exit(0);
};
run().catch((error) => {
console.error('Error:', error);
process.exit(1);
});
Install vite-node for running scripts:
npm install -D vite-node
Step 8: Create justfile
Create justfile in the project root:
# {Project Name} - Just Command Runner
# Run `just --list` to see all available commands
# Default recipe to display help
default:
@just --list
# === Development ===
# Serve backend
serve-backend:
echo "Starting backend..."
npx nx serve backend
# Serve webapp
serve-webapp:
echo "Starting webapp..."
npx nx serve webapp
# Serve all projects in parallel (backend + webapp)
serve-all:
echo "Starting all projects..."
npx nx run-many --targets=serve --projects=backend,webapp --parallel=2
# Serve with debugger
serve-all-debug:
echo "Starting all projects with debugger..."
NODE_OPTIONS='--inspect' npx nx run-many --targets=serve --projects=backend,webapp --parallel=2
# === Building ===
# Build a specific project
build project:
echo "Building {{project}}..."
npx nx build {{project}}
# Build all projects
build-all:
echo "Building all projects..."
npx nx run-many --targets=build --all
# === Testing ===
# Run unit tests for a project
test project:
echo "Running tests for {{project}}..."
npx nx test {{project}}
# Run all tests
test-all:
echo "Running all tests..."
npx nx run-many --targets=test --all
# === Linting ===
# Lint a specific project
lint project:
echo "Linting {{project}}..."
npx nx lint {{project}} --fix
# Lint all projects
lint-all:
echo "Linting all projects..."
npx nx run-many --targets=lint --all --fix
# === Utilities ===
# Clean NX cache
clean:
echo "Cleaning NX cache..."
npx nx reset
# Show project details
show project:
npx nx show project {{project}}
# View dependency graph
graph:
npx nx graph
# Install dependencies
install:
npm install
# Generate a new library
gen-lib name:
npx nx generate @nx/js:library --name={{name}} --directory=libs/{{name}} --projectNameAndRootFormat=as-provided --unitTestRunner=jest --bundler=tsc --strict
# Run a TypeScript script
run-script script:
npx vite-node scripts/{{script}}.ts
Step 9: Set Up Environment Variables
Create .env.example:
# Database
MONGODB_URI=mongodb://localhost:27017/{project-name}
# Server
PORT=3005
NODE_ENV=development
# Frontend
VITE_API_URL=http://localhost:3005
# Add your environment variables here
Create .envrc:
# Use direnv to auto load environment variables upon entering this project directory
set -o allexport; source .env.local; set +o allexport
Copy to local:
cp .env.example .env.local
Step 10: Update .gitignore
Add these entries to .gitignore:
# Environment
.env.local
.env*.local
# Nx
.nx/cache
dist/
# Dependencies
node_modules/
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Build outputs
*.tsbuildinfo
Step 11: Configure nx.json
Update nx.json with recommended settings:
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"plugins": [
{
"plugin": "@nx/js/typescript",
"options": {
"typecheck": {
"targetName": "typecheck"
},
"build": {
"targetName": "build",
"configName": "tsconfig.lib.json",
"buildDepsName": "build-deps",
"watchDepsName": "watch-deps"
}
}
},
{
"plugin": "@nx/webpack/plugin",
"options": {
"buildTargetName": "build",
"serveTargetName": "serve",
"previewTargetName": "preview",
"buildDepsTargetName": "build-deps",
"watchDepsTargetName": "watch-deps",
"serveStaticTargetName": "serve-static"
}
},
{
"plugin": "@nx/eslint/plugin",
"options": {
"targetName": "lint"
}
},
{
"plugin": "@nx/jest/plugin",
"options": {
"targetName": "test"
}
}
],
"targetDefaults": {
"test": {
"dependsOn": ["^build"]
}
},
"generators": {
"@nx/react": {
"application": {
"babel": true,
"style": "css",
"linter": "eslint",
"bundler": "webpack"
},
"component": {
"style": "css"
},
"library": {
"style": "css",
"linter": "eslint"
}
}
},
"tui": {
"enabled": false
}
}
Post-Scaffolding Checklist
After scaffolding, verify:
- Install dependencies:
npm install - Allow direnv:
direnv allow - Test backend:
just serve-backend - Test frontend:
just serve-webapp - Test both:
just serve-all - View dependency graph:
just graph
Using Shared Types
In the backend, import types:
import { MyType } from '@{project}/types';
In the frontend, import types:
import type { MyType } from '@{project}/types';
Adding New Libraries
# Generate a new shared library
just gen-lib my-utils
# Or manually
npx nx generate @nx/js:library \
--name=my-utils \
--directory=libs/my-utils \
--projectNameAndRootFormat=as-provided \
--unitTestRunner=jest \
--bundler=tsc \
--strict
Then add the path alias to tsconfig.base.json:
{
"paths": {
"@{project}/types": ["libs/types/src/index.ts"],
"@{project}/my-utils": ["libs/my-utils/src/index.ts"]
}
}
Troubleshooting
NX Cache Issues
just clean
# or
npx nx reset
rm -rf .nx/cache
Port Already in Use
lsof -i :3005
kill -9 <PID>
direnv Not Loading
direnv allow
direnv status
TypeScript Path Not Resolving
Ensure:
- Path is in
tsconfig.base.json - Restart your IDE/editor
- Run
npx nx resetto clear cache
More from workshop-ventures/skills
frontend-scaffolding
Scaffold a React frontend with Tailwind CSS, React Router, React Query, and standard project structure. Use when asked to "create a frontend", "scaffold webapp", "set up React app", or "initialize frontend structure".
16backend-metrics
Add OpenTelemetry metrics and observability to the backend. Use when asked to "add metrics", "add observability", "track requests", or "add OpenTelemetry".
10frontend-hooks-creation
Create React Query hooks for API endpoints with proper typing and cache invalidation. Use when asked to "create hooks", "add frontend hooks", "create API hooks", or "add React Query hooks".
9backend-scaffolding
Scaffold a Koa-based backend server with standard structure including config, logging, routes, models, and database setup. Use when asked to "create a backend", "scaffold backend", "set up an API server", or "initialize backend structure".
9backend-ai-tools
Create AI tools for use with Vercel AI SDK agents. Use when asked to "create AI tools", "add agent tools", "create tool for AI", or "add tools to agent".
8backend-ai-agent
Create AI agents using Vercel AI SDK with tool use, tracing, and failover. Use when asked to "create an AI agent", "add AI", "create LLM integration", or "build an assistant".
8