Node.js: Server-Side JavaScript

Node.js allows you to run JavaScript on the server. It's the foundation of modern full-stack JavaScript development, powering everything from simple APIs to Netflix's streaming platform.

What is Node.js?

Node.js is a JavaScript runtime built on Chrome's V8 engine. It enables JavaScript to run outside the browser, making it possible to build servers, command-line tools, and more.

Why Node.js?

  • JavaScript everywhere: Use one language for frontend and backend
  • Non-blocking I/O: Handle thousands of concurrent connections efficiently
  • npm ecosystem: Access millions of open-source packages
  • Fast development: Quick to prototype and iterate
  • Great for real-time: WebSockets, chat apps, live updates

Getting Started

Installation

Download Node.js from nodejs.org or use a version manager:

# Check installation
node --version  # Should show v18.x or higher
npm --version   # Node Package Manager

# Run JavaScript files
node app.js

# Start interactive REPL
node

Your First Node.js Script

// hello.js
console.log('Hello from Node.js!');

// Access command line arguments
console.log('Arguments:', process.argv);

// Environment variables
console.log('Node version:', process.version);
console.log('Current directory:', process.cwd());

npm Basics

# Initialize a new project
npm init -y

# Install dependencies
npm install express        # Production dependency
npm install nodemon -D     # Development dependency

# Run scripts defined in package.json
npm run dev
npm start

Core Concepts

Modules

// math.js - Export functions
export function add(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

// app.js - Import and use
import { add, multiply } from './math.js';

console.log(add(2, 3));       // 5
console.log(multiply(4, 5));  // 20

Built-in Modules

Module Purpose
fs File system operations
path File path utilities
http HTTP server and client
crypto Cryptographic functions
os Operating system info

Creating a Basic HTTP Server

import http from 'http';

const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('Hello, World!');
});

server.listen(3000, () => {
  console.log('Server running at http://localhost:3000');
});

Building APIs with Express

Express is the most popular Node.js web framework. It simplifies building APIs and web applications.

Basic Express Setup

import express from 'express';

const app = express();

// Middleware to parse JSON
app.use(express.json());

// Routes
app.get('/', (req, res) => {
  res.json({ message: 'Welcome to the API' });
});

app.get('/users', (req, res) => {
  res.json([
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ]);
});

app.post('/users', (req, res) => {
  const { name, email } = req.body;
  // Save to database...
  res.status(201).json({ id: 3, name, email });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Route Parameters & Query Strings

// Route parameters - /users/123
app.get('/users/:id', (req, res) => {
  const userId = req.params.id;
  res.json({ id: userId, name: 'User ' + userId });
});

// Query strings - /search?q=node&limit=10
app.get('/search', (req, res) => {
  const { q, limit = 10 } = req.query;
  res.json({ query: q, limit: parseInt(limit) });
});

Middleware

// Logging middleware
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next();  // Continue to next middleware
};

app.use(logger);

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
});

Working with Files

import fs from 'fs/promises';
import path from 'path';

// Read file
const content = await fs.readFile('data.txt', 'utf-8');
console.log(content);

// Write file
await fs.writeFile('output.txt', 'Hello, World!');

// Append to file
await fs.appendFile('log.txt', 'New log entry\n');

// Check if file exists
try {
  await fs.access('config.json');
  console.log('File exists');
} catch {
  console.log('File does not exist');
}

// Read directory
const files = await fs.readdir('./src');
console.log(files);

// Create directory
await fs.mkdir('uploads', { recursive: true });

Database Integration

MongoDB with Mongoose

import mongoose from 'mongoose';

// Connect to MongoDB
await mongoose.connect('mongodb://localhost:27017/myapp');

// Define a schema
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, unique: true },
  createdAt: { type: Date, default: Date.now }
});

const User = mongoose.model('User', userSchema);

// CRUD operations
const user = await User.create({ name: 'Alice', email: 'alice@example.com' });
const users = await User.find();
const found = await User.findById(user._id);
await User.findByIdAndUpdate(user._id, { name: 'Alicia' });
await User.findByIdAndDelete(user._id);

PostgreSQL with Prisma

import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// Create user
const user = await prisma.user.create({
  data: { name: 'Alice', email: 'alice@example.com' }
});

// Find users
const users = await prisma.user.findMany({
  where: { name: { contains: 'Ali' } }
});

// Update user
await prisma.user.update({
  where: { id: 1 },
  data: { name: 'Alicia' }
});

Async Patterns

Promises and Async/Await

// Async function
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch failed:', error);
    throw error;
  }
}

// Parallel execution
async function fetchMultiple() {
  const [users, posts] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json())
  ]);
  return { users, posts };
}

// Error handling with Promise.allSettled
const results = await Promise.allSettled([
  fetch('/api/fast'),
  fetch('/api/slow'),
  fetch('/api/might-fail')
]);
// results contain status: 'fulfilled' or 'rejected'

Best Practices

Environment Variables

// Use dotenv for local development
import 'dotenv/config';

const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;

// Never commit secrets to git!
// .env file (add to .gitignore)
// PORT=3000
// DATABASE_URL=mongodb://localhost:27017/myapp
// JWT_SECRET=your-secret-key

Project Structure

my-api/
├── src/
│   ├── routes/        # Route handlers
│   ├── controllers/   # Business logic
│   ├── models/        # Database models
│   ├── middleware/    # Custom middleware
│   ├── utils/         # Helper functions
│   └── index.js       # Entry point
├── tests/             # Test files
├── .env               # Environment variables
├── .gitignore
└── package.json

Security Tips

  • Validate input: Never trust user input, use validation libraries
  • Use HTTPS: Encrypt data in transit
  • Hash passwords: Use bcrypt, never store plain text
  • Rate limiting: Prevent abuse and DDoS attacks
  • Helmet: Set security headers automatically

Related Guides