Node.js Project Setup
Node.js Project Setup
package.json Configuration
{
"name": "my-ts-server",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "tsc",
"start": "node dist/index.js",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"typescript": "^5.4.0",
"tsx": "^4.7.0",
"@types/node": "^20.11.0"
}
}
tsconfig.json for Node.js
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Project Structure
my-ts-server/
├── src/
│ ├── index.ts
│ ├── config/
│ │ └── env.ts
│ ├── routes/
│ │ └── users.ts
│ ├── services/
│ │ └── user-service.ts
│ └── types/
│ └── index.ts
├── dist/
├── tsconfig.json
└── package.json
Running TypeScript Files
Running TypeScript Files
ts-node vs tsx
# tsx - fast, modern, supports ESM natively
npm install --save-dev tsx
npx tsx src/index.ts
npx tsx watch src/index.ts # watch mode
# ts-node - traditional, more configurable
npm install --save-dev ts-node
npx ts-node src/index.ts
tsx Advantages
- Faster startup than ts-node
- Supports ESM and CJS seamlessly
- No extra configuration needed
- Built on esbuild for speed
Dev Watch Mode
// src/index.ts
import { createServer } from 'http';
const server = createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from TypeScript!');
});
const PORT = process.env.PORT ?? 3000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
# Development with hot reload
npx tsx watch src/index.ts
Path Aliases
Path Aliases
Configuration
// tsconfig.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@config/*": ["src/config/*"],
"@routes/*": ["src/routes/*"],
"@services/*": ["src/services/*"],
"@types/*": ["src/types/*"]
}
}
}
Using Path Aliases
// Without aliases
import { UserService } from '../services/user-service.js';
import { User } from '../types/index.js';
// With aliases
import { UserService } from '@services/user-service.js';
import { User } from '@types/index.js';
tsx Support
# tsx supports tsconfig paths natively
npx tsx src/index.ts
# For other tools, use tsconfig-paths
npm install --save-dev tsconfig-paths
npx ts-node -r tsconfig-paths/register src/index.ts
Environment Variables
Environment Variables
Type-Safe Environment Variables
// src/config/env.ts
interface Env {
NODE_ENV: 'development' | 'production' | 'test';
PORT: number;
DATABASE_URL: string;
JWT_SECRET: string;
CORS_ORIGIN: string;
}
function getEnv(): Env {
const required = ['DATABASE_URL', 'JWT_SECRET'] as const;
for (const key of required) {
if (!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
}
return {
NODE_ENV: (process.env.NODE_ENV as Env['NODE_ENV']) ?? 'development',
PORT: parseInt(process.env.PORT ?? '3000', 10),
DATABASE_URL: process.env.DATABASE_URL!,
JWT_SECRET: process.env.JWT_SECRET!,
CORS_ORIGIN: process.env.CORS_ORIGIN ?? 'http://localhost:3000'
};
}
export const env = getEnv();
.env.example
NODE_ENV=development
PORT=3000
DATABASE_URL=postgresql://localhost:5432/mydb
JWT_SECRET=your-secret-key
CORS_ORIGIN=http://localhost:3000
Using dotenv
import 'dotenv/config'; // loads .env into process.env
import { env } from './config/env.js';
console.log(env.PORT); // typed as number
console.log(env.NODE_ENV); // typed as 'development' | 'production' | 'test'