JavaScript + Backend Bootcamp
Day 9 : Node.js + TypeScript Project Setup
From today onward, all backend code in this bootcamp will use TypeScript.
Today you’ll learn how to create a professional Node.js project with:
- Node.js
- TypeScript
tsx- npm scripts
- Environment variables
- ES modules
- Git and GitHub
- Development and production builds
- A scalable folder structure
By the end of Day 9, you’ll have the foundation for every upcoming backend project.
1. What Is Node.js?
Node.js is a JavaScript runtime that allows JavaScript and TypeScript applications to run outside the browser.
It is commonly used to build:
- REST APIs
- Web servers
- Authentication systems
- Real-time applications
- Command-line tools
- Database-backed applications
Node.js can now execute some TypeScript syntax directly, but its built-in type stripping does not use tsconfig.json and does not perform type checking. For this bootcamp, we’ll use tsx during development and tsc for type checking and production compilation. (Node.js)
2. Check Node.js and npm
Open the terminal:
node --versionCheck npm:
npm --versionInstall the current Node.js LTS release when starting the bootcamp.
3. Create the Project
mkdir day-9-node-typescript
cd day-9-node-typescriptInitialize npm:
npm init -yThis creates:
package.jsonpackage.json stores project metadata, scripts, dependencies and development dependencies. (npm Docs)
4. Install TypeScript Tools
Install TypeScript, Node.js type definitions and tsx:
npm install --save-dev typescript tsx @types/nodeThese packages have different responsibilities:
typescript: type-checks and compiles TypeScripttsx: executes TypeScript during development@types/node: provides TypeScript definitions for Node.js APIs
Node.js officially documents tsx as an option for running TypeScript with broader TypeScript syntax support. However, tsx does not type-check the program, so tsc should still be run separately. (Node.js)
5. Why Use tsx Instead of ts-node?
Both tools can run TypeScript.
For this bootcamp, we’ll use:
tsxbecause it provides:
- Simple configuration
- Fast TypeScript execution
- ES module support
- Watch mode
- Good development experience
Run a file using:
npx tsx src/index.tsRun it in watch mode:
npx tsx watch src/index.tsWatch mode automatically reruns the application when an imported project file changes. (tsx.is)
6. Create the Project Structure
Create the following folders and files:
day-9-node-typescript/
│
├── src/
│ ├── config/
│ ├── controllers/
│ ├── routes/
│ ├── services/
│ ├── types/
│ ├── utils/
│ └── index.ts
│
├── dist/
├── .env
├── .env.example
├── .gitignore
├── package.json
├── package-lock.json
└── tsconfig.jsonFor Day 9, only src/index.ts is required.
The other folders prepare the project for Express, MongoDB and authentication later.
7. Create tsconfig.json
Run:
npx tsc --initReplace the generated content with:
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"sourceMap": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"types": ["node"]
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}moduleResolution: "NodeNext" makes TypeScript follow Node.js module resolution rules. Source maps create mappings between generated JavaScript and original TypeScript, which improves debugging. (TypeScript)
8. Understanding Important Compiler Options
target
"target": "ES2022"Controls the JavaScript version produced by TypeScript.
module
"module": "NodeNext"Configures TypeScript for Node.js module behavior.
rootDir
"rootDir": "./src"The location of TypeScript source files.
outDir
"outDir": "./dist"The location of compiled JavaScript.
strict
"strict": trueEnables TypeScript’s strict type-checking rules.
types
"types": ["node"]Loads Node.js type declarations without automatically loading every installed declaration package.
9. Configure ES Modules
Open package.json and add:
"type": "module"Example:
{
"name": "day-9-node-typescript",
"version": "1.0.0",
"type": "module",
"main": "dist/index.js"
}This project will use modern ES module syntax:
import
exportinstead of:
require()
module.exports10. Create Your First Node.js TypeScript Program
Create:
src/index.tsAdd:
const appName = "TypeScript Backend Bootcamp";
const port = 3000;console.log(`${appName} is starting...`);
console.log(`Port: ${port}`);Run it:
npx tsx src/index.tsOutput:
TypeScript Backend Bootcamp is starting...
Port: 300011. Add npm Scripts
Update package.json:
{
"name": "day-9-node-typescript",
"version": "1.0.0",
"private": true,
"type": "module",
"main": "dist/index.js",
"scripts": {
"dev": "tsx watch src/index.ts",
"typecheck": "tsc --noEmit",
"build": "tsc",
"start": "node dist/index.js"
},
"devDependencies": {
"@types/node": "^24.0.0",
"tsx": "^4.0.0",
"typescript": "^5.0.0"
}
}Do not manually copy the package versions above into an existing project. Your installed versions will be written automatically by npm.
The scripts field maps script names to commands that can be executed using npm run. Development-only tools such as TypeScript and tsx belong in devDependencies. (npm Docs)
12. Run Development Mode
npm run devThis runs:
tsx watch src/index.tsWhenever you save an imported source file, the application restarts automatically. (tsx.is)
13. Type-Check the Project
Run:
npm run typecheckThis executes:
tsc --noEmitIt checks the code without creating JavaScript output.
Example error:
const port: number = "3000";TypeScript reports:
Type 'string' is not assignable to type 'number'.Because tsx runs code without performing full TypeScript type checking, keeping a separate typecheck script is important. (Node.js)
14. Build the Project
Run:
npm run buildTypeScript compiles:
src/index.tsinto:
dist/index.jsProject structure after building:
day-9-node-typescript/
│
├── src/
│ └── index.ts
│
├── dist/
│ ├── index.js
│ └── index.js.map
│
├── package.json
└── tsconfig.jsontsx executes TypeScript during development, but it does not create the production JavaScript build. Compilation is handled by tsc. (tsx.is)
15. Run the Production Build
After building:
npm startThis executes:
node dist/index.jsRecommended workflow:
npm run typecheck
npm run build
npm start16. Development vs Production
Development
npm run devUses:
tsx watchBenefits:
- Runs TypeScript directly
- Watches file changes
- Restarts automatically
- Faster development workflow
Production
npm run build
npm startUses:
tsc → JavaScript → Node.jsBenefits:
- Runs compiled JavaScript
- Detects type errors before deployment
- Produces predictable output
- Does not require the TypeScript source runner
17. Creating Modules
Create:
src/utils/greet.tsAdd:
export function greet(name: string): string {
return `Hello, ${name}`;
}Update:
src/index.tsimport { greet } from "./utils/greet.js";console.log(greet("Ravi"));Notice the import:
"./utils/greet.js"With NodeNext and compiled ES modules, relative imports should use the file extension that Node.js will execute after compilation.
18. Default Exports
Create:
src/config/app.tsconst appConfig = {
name: "Backend Bootcamp",
version: "1.0.0"
};export default appConfig;Import it:
import appConfig from "./config/app.js";console.log(appConfig.name);19. Importing Node.js Built-in Modules
Node.js built-in modules can use the node: prefix.
import path from "node:path";const filePath = path.join(
process.cwd(),
"src",
"index.ts"
);console.log(filePath);
Using node: makes it clear that the import comes from Node.js rather than an npm package.
20. Understanding process
Node.js provides the global process object.
console.log(process.version);
console.log(process.platform);
console.log(process.cwd());Common properties:
process.env: environment variablesprocess.argv: command-line argumentsprocess.cwd(): current working directoryprocess.exit(): terminate the processprocess.version: current Node.js version
21. Command-Line Arguments
Run:
npm run dev -- RaviRead the arguments:
const args = process.argv.slice(2);const name = args[0] ?? "Guest";console.log(`Hello, ${name}`);
Output:
Hello, Ravi22. Environment Variables
Environment variables store values that can change between environments.
Get Bhaskar Chowdary’s stories in your inbox
Join Medium for free to get updates from this writer.
Examples:
- Server port
- Database connection string
- JWT secret
- API keys
- Application environment
Create:
.envAdd:
PORT=3000
NODE_ENV=development
APP_NAME=Backend Bootcamp23. Loading .env Values
Modern Node.js supports loading an environment file using its --env-file option.
Update the development script:
"dev": "tsx watch --env-file=.env src/index.ts"Update the production script:
"start": "node --env-file=.env dist/index.js"Alternatively, a package such as dotenv can be introduced later when the project needs more configuration control.
24. Reading Environment Variables
const port = process.env.PORT;
const environment = process.env.NODE_ENV;
const appName = process.env.APP_NAME;console.log({
port,
environment,
appName
});Environment variable values are typed as:
string | undefinedbecause the value may not exist.
25. Safely Parse the Port
Avoid:
const port = Number(process.env.PORT);If PORT is missing or invalid, this may produce NaN.
Use validation:
function getPort(value: string | undefined): number {
if (!value) {
return 3000;
}const port = Number(value); if (!Number.isInteger(port) || port <= 0) {
throw new Error("PORT must be a positive integer");
} return port;
}const port = getPort(process.env.PORT);console.log(`Application running on port ${port}`);
26. Create a Type-Safe Config Module
Create:
src/config/env.tsinterface EnvironmentConfig {
port: number;
nodeEnv:
| "development"
| "test"
| "production";
appName: string;
}function parseNodeEnv(
value: string | undefined
): EnvironmentConfig["nodeEnv"] {
if (
value === "development" ||
value === "test" ||
value === "production"
) {
return value;
} return "development";
}function parsePort(
value: string | undefined
): number {
const port = Number(value ?? 3000); if (!Number.isInteger(port) || port <= 0) {
throw new Error(
"PORT must be a positive integer"
);
} return port;
}export const env: EnvironmentConfig = {
port: parsePort(process.env.PORT),
nodeEnv: parseNodeEnv(process.env.NODE_ENV),
appName:
process.env.APP_NAME ??
"TypeScript Backend"
};
Use it in src/index.ts:
import { env } from "./config/env.js";console.log(`${env.appName} started`);
console.log(`Environment: ${env.nodeEnv}`);
console.log(`Port: ${env.port}`);27. Create .env.example
Create:
.env.exampleAdd:
PORT=3000
NODE_ENV=development
APP_NAME=Backend BootcampThis file documents required environment variables without exposing real secrets.
Commit:
.env.exampleDo not commit:
.env28. Create .gitignore
Create:
.gitignoreAdd:
# Dependencies
node_modules/# Compiled output
dist/# Environment variables
.env
.env.local
.env.*.local# TypeScript build information
*.tsbuildinfo# Logs
*.log
logs/# Editor files
.vscode/
.idea/# Operating system files
.DS_Store
Thumbs.db# Test coverage
coverage/
Do not place .env.example inside .gitignore.
29. Initialize Git
git initCheck project files:
git statusAdd files:
git add .Create the first commit:
git commit -m "Initialize Node.js TypeScript project"30. Connect to GitHub
Create an empty GitHub repository, then run:
git remote add origin YOUR_REPOSITORY_URLRename the current branch:
git branch -M mainPush:
git push -u origin mainNever push:
.env- Database passwords
- JWT secrets
- Private keys
- API credentials
31. Add a Build Validation Script
Update package.json:
{
"scripts": {
"dev": "tsx watch --env-file=.env src/index.ts",
"typecheck": "tsc --noEmit",
"build": "tsc",
"start": "node --env-file=.env dist/index.js",
"check": "npm run typecheck && npm run build"
}
}Run:
npm run checkThis checks types before creating the production build.
32. Optional Clean Build
Install a cross-platform cleanup package:
npm install --save-dev rimrafUpdate scripts:
{
"scripts": {
"clean": "rimraf dist",
"dev": "tsx watch --env-file=.env src/index.ts",
"typecheck": "tsc --noEmit",
"build": "npm run clean && tsc",
"start": "node --env-file=.env dist/index.js"
}
}Now every build removes old compiled files first.
33. Final package.json
A clean setup looks like this:
{
"name": "day-9-node-typescript",
"version": "1.0.0",
"description": "Node.js and TypeScript bootcamp project",
"private": true,
"type": "module",
"main": "dist/index.js",
"scripts": {
"clean": "rimraf dist",
"dev": "tsx watch --env-file=.env src/index.ts",
"typecheck": "tsc --noEmit",
"build": "npm run clean && tsc",
"start": "node --env-file=.env dist/index.js",
"check": "npm run typecheck && npm run build"
},
"devDependencies": {
"@types/node": "^24.0.0",
"rimraf": "^6.0.0",
"tsx": "^4.0.0",
"typescript": "^5.0.0"
}
}Allow npm to write the actual dependency versions when installing packages.
34. Final Project Structure
day-9-node-typescript/
│
├── src/
│ ├── config/
│ │ └── env.ts
│ ├── controllers/
│ ├── routes/
│ ├── services/
│ ├── types/
│ ├── utils/
│ │ └── greet.ts
│ └── index.ts
│
├── .env
├── .env.example
├── .gitignore
├── package.json
├── package-lock.json
└── tsconfig.jsonAfter building:
dist/
├── config/
│ ├── env.js
│ └── env.js.map
├── utils/
│ ├── greet.js
│ └── greet.js.map
├── index.js
└── index.js.map35. Mini Project — Typed Application Starter
src/types/app.types.ts
export type Environment =
| "development"
| "test"
| "production";export interface ApplicationInfo {
name: string;
version: string;
environment: Environment;
port: number;
}src/config/app.ts
import type {
ApplicationInfo,
Environment
} from "../types/app.types.js";function getEnvironment(
value: string | undefined
): Environment {
if (
value === "development" ||
value === "test" ||
value === "production"
) {
return value;
} return "development";
}function getPort(
value: string | undefined
): number {
const port = Number(value ?? 3000); if (!Number.isInteger(port) || port <= 0) {
throw new Error("Invalid server port");
} return port;
}export const application: ApplicationInfo = {
name:
process.env.APP_NAME ??
"Backend Bootcamp",
version: "1.0.0",
environment: getEnvironment(
process.env.NODE_ENV
),
port: getPort(process.env.PORT)
};
src/index.ts
import { application } from "./config/app.js";function startApplication(): void {
console.log("--------------------------------");
console.log(`${application.name}`);
console.log(`Version: ${application.version}`);
console.log(
`Environment: ${application.environment}`
);
console.log(`Port: ${application.port}`);
console.log("--------------------------------");
}try {
startApplication();
} catch (error: unknown) {
if (error instanceof Error) {
console.error(
`Startup failed: ${error.message}`
);
} else {
console.error(
"Startup failed due to an unknown error"
);
} process.exit(1);
}
Run:
npm run devExample output:
--------------------------------
Backend Bootcamp
Version: 1.0.0
Environment: development
Port: 3000
--------------------------------Common Beginner Mistakes
Running production with tsx
Avoid:
tsx src/index.tsas your final deployment approach.
Use:
npm run build
npm startForgetting type checking
tsx runs TypeScript but does not perform full type checking.
Run:
npm run typecheckbefore building or deploying. (Node.js)
Committing .env
Never commit sensitive values:
DATABASE_URL=...
JWT_SECRET=...
API_KEY=...Commit .env.example instead.
Mixing CommonJS and ES modules
Avoid combining:
require()with:
importUse one module system consistently.
This bootcamp uses ES modules.
Importing TypeScript files incorrectly
With the compiled NodeNext setup, use:
import { greet } from "./utils/greet.js";not:
import { greet } from "./utils/greet";Depending on TypeScript types for runtime validation
This interface:
interface Config {
port: number;
}does not validate environment variables while the application runs.
External values must still be checked and parsed.
Important Concepts Learned Today
✅ Node.js project setup
✅ npm and package.json
✅ TypeScript in Node.js
✅ tsx development runner
✅ Watch mode
✅ Node.js type definitions
✅ tsconfig.json for Node.js
✅ ES modules
✅ npm scripts
✅ Type checking
✅ Development and production builds
✅ Environment variables
✅ Type-safe configuration
✅ Folder structure
✅ .gitignore
✅ Git and GitHub
✅ Module imports and exports
Homework
Create a new Node.js TypeScript project with:
srcanddistdirectories- Strict TypeScript configuration
- ES module support
- Development watch script
- Type-check script
- Production build script
- Environment variables
- Type-safe configuration module
.gitignore- Git repository with an initial commit
Practice Challenge
Create an application configuration system that reads:
APP_NAME=Task Manager API
PORT=4000
NODE_ENV=developmentExpected output:
Task Manager API
Environment: development
Server port: 4000Requirements:
- Use an interface
- Validate
PORT - Use a literal union for
NODE_ENV - Throw an error for invalid configuration
- Handle startup errors using
unknown
Day 10
Node.js Core Modules + HTTP Server
We’ll learn:
node:fsnode:pathnode:osnode:eventsnode:http- Buffers
- Streams
- HTTP request and response objects
- HTTP methods
- Routes
- Status codes
- JSON responses
Mini Projects:
- File Manager
- Basic TypeScript API Server






