Project Setup Basics
9 examples to get you started with Project Setup - 7 basic and 2 intermediate.
Search across all documentation pages
9 examples to get you started with Project Setup - 7 basic and 2 intermediate.
npm init -y && npm install -D typescript@5.6 tsx @types/node.git init && git add . && git commit -m "init".Separate source, tests, and config at the repo root for clarity.
billing-api/
src/
server.ts
routes/
services/
test/
health.test.ts
package.json
tsconfig.json
Dockerfile
.gitignore
src/ holds runtime TypeScript; test/ mirrors domain folders.Related: Scaffolding APIs - bootstrap from templates
Wire the daily commands every contributor and CI job runs.
{
"type": "module",
"scripts": {
"dev": "tsx watch src/server.ts",
"build": "tsc -p tsconfig.build.json",
"start": "node dist/server.js",
"test": "node --import tsx --test",
"typecheck": "tsc --noEmit"
}
}dev for local iteration; start runs compiled output in production.typecheck fails fast without emitting files.Use separate configs for build output vs editor/CI typecheck.
// tsconfig.json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"noEmit": true,
"rootDir": ".",
"types": ["node"]
},
"include": ["src", "test"]
}// tsconfig.build.json
{
"extends": "./tsconfig.json",
"compilerOptions": { "noEmit": false, "outDir": "dist", "rootDir": "src" },
"include": ["src"]
}NodeNext matches Node 24 ESM resolution.dist/.Keep secrets out of git; document required variables.
# .gitignore
node_modules/
dist/
.env
.env.local
coverage/
# .env.example (committed)
PORT=3000
DATABASE_URL=postgres://localhost:5432/billing
LOG_LEVEL=info
.env.example, never .env.Multi-stage build: install, compile, run slim runtime image.
FROM node:24.18.0-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:24.18.0-alpine
WORKDIR /app
ENV NODE_ENV=production
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/server.js"]FROM to match engines.Every service exposes a liveness route from day one.
// src/server.ts
import express from "express";
const app = express();
app.get("/health", (_req, res) => {
res.json({ status: "ok" });
});
const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => console.log(`listening on ${port}`));/health is load balancer and orchestrator friendly.New hires run three commands and get a green test.
## Quick start
npm ci
cp .env.example .env
npm run dev
## Checks
npm run typecheck
npm testpackage.json scripts exactly.Pick one convention per repo and enforce it.
# Option A: top-level test/ (shown above)
test/routes/health.test.ts
# Option B: colocated
src/routes/health.test.ts
test/ keeps dist/ clean without extra exclude rules.Related: Testing Basics - pyramid for APIs
Start single-service; split when boundaries are clear.
| Layout | Choose when |
|---|---|
Single repo / single src/ | One deployable API, team < 8 |
apps/ + packages/ workspaces | 2+ deployables sharing types/libs |
Related: Multi-Service Monorepo - boundary rules
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 18, 2026