🚀 Modern Web Development

Building the Future, Today

A Technical Overview

The Modern Web Stack

Today's web applications are built with powerful, scalable technologies:

⚛️
React
💚
Node.js
🔷
TypeScript
🎨
Tailwind
📦
Webpack
🐳
Docker

React Component Example

Modern functional components with hooks:


import React, { useState, useEffect } from 'react';
import './UserProfile.css';
import { fetchUserData } from './api';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetchUserData(userId)
      .then(data => setUser(data))
      .catch(err => setError(err))
      .finally(() => setLoading(false));
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;

  return (
    <div className="user-profile">
      <h2>{user.name}</h2>
      <p>{user.email}</p>
    </div>
  );
}
        

TypeScript for Type Safety

Adding types makes your code more robust:


interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
  createdAt: Date;
}

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

async function getUser(id: number): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  const json: ApiResponse<User> = await response.json();
  
  if (json.status !== 200) {
    throw new Error(json.message);
  }
  
  return json.data;
}

// Type checking at compile time!
const user: User = await getUser(123);
console.log(user.name.toUpperCase());
        

Async/Await Pattern

Modern asynchronous JavaScript is clean and readable:


// Old way with callbacks (callback hell)
getData(function(a) {
  getMoreData(a, function(b) {
    getMoreData(b, function(c) {
      getMoreData(c, function(d) {
        console.log(d);
      });
    });
  });
});

// Modern way with async/await
async function fetchAllData() {
  try {
    const a = await getData();
    const b = await getMoreData(a);
    const c = await getMoreData(b);
    const d = await getMoreData(c);
    console.log(d);
  } catch (error) {
    console.error('Error:', error);
  }
}
          

Parallel Async Operations


// Sequential (slow)
const user = await fetchUser(id);
const posts = await fetchPosts(id);
const comments = await fetchComments(id);

// Parallel (fast!)
const [user, posts, comments] = await Promise.all([
  fetchUser(id),
  fetchPosts(id),
  fetchComments(id)
]);

// With error handling
const results = await Promise.allSettled([
  fetchUser(id),
  fetchPosts(id),
  fetchComments(id)
]);

results.forEach((result, index) => {
  if (result.status === 'fulfilled') {
    console.log(`Success ${index}:`, result.value);
  } else {
    console.error(`Failed ${index}:`, result.reason);
  }
});
          

RESTful API Design

Clean, predictable endpoints:


// Express.js API routes
const express = require('express');
const router = express.Router();

// GET all users
router.get('/api/users', async (req, res) => {
  const users = await User.findAll();
  res.json({ data: users });
});

// GET single user
router.get('/api/users/:id', async (req, res) => {
  const user = await User.findById(req.params.id);
  if (!user) return res.status(404).json({ error: 'Not found' });
  res.json({ data: user });
});

// POST create user
router.post('/api/users', async (req, res) => {
  const user = await User.create(req.body);
  res.status(201).json({ data: user });
});

// PUT update user
router.put('/api/users/:id', async (req, res) => {
  const user = await User.update(req.params.id, req.body);
  res.json({ data: user });
});

// DELETE user
router.delete('/api/users/:id', async (req, res) => {
  await User.delete(req.params.id);
  res.status(204).send();
});
        

Real-time with WebSockets

Bidirectional communication for live updates:


// Server side (Node.js with ws)
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  console.log('Client connected');
  
  ws.on('message', (message) => {
    console.log('Received:', message);
    
    // Broadcast to all clients
    wss.clients.forEach((client) => {
      if (client.readyState === WebSocket.OPEN) {
        client.send(message);
      }
    });
  });
  
  ws.on('close', () => {
    console.log('Client disconnected');
  });
});

// Client side
const socket = new WebSocket('ws://localhost:8080');

socket.addEventListener('open', () => {
  console.log('Connected to server');
  socket.send('Hello Server!');
});

socket.addEventListener('message', (event) => {
  console.log('Message from server:', event.data);
});
        

Containerization with Docker

Package your app with all dependencies:


# Dockerfile
FROM node:18-alpine

# Set working directory
WORKDIR /app

# Copy package files
COPY package*.json ./

# Install dependencies
RUN npm ci --only=production

# Copy application code
COPY . .

# Build the app
RUN npm run build

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s \
  CMD node healthcheck.js

# Start the application
CMD ["node", "dist/server.js"]
        

Multi-Container Apps

Docker Compose for complex setups:


# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://db:5432/myapp
    depends_on:
      - db
      - redis
    volumes:
      - ./logs:/app/logs

  db:
    image: postgres:15-alpine
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_PASSWORD=secret
    volumes:
      - postgres_data:/var/lib/postgresql/data

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

volumes:
  postgres_data:
        

Testing Best Practices

Unit tests with Jest:


// sum.test.js
import { sum, multiply, divide } from './math';

describe('Math utilities', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(sum(1, 2)).toBe(3);
  });

  test('multiplies 3 * 4 to equal 12', () => {
    expect(multiply(3, 4)).toBe(12);
  });

  test('divides 10 / 2 to equal 5', () => {
    expect(divide(10, 2)).toBe(5);
  });

  test('throws error when dividing by zero', () => {
    expect(() => divide(10, 0)).toThrow('Division by zero');
  });
});

// Async testing
describe('API calls', () => {
  test('fetches user data', async () => {
    const user = await fetchUser(1);
    expect(user).toHaveProperty('name');
    expect(user).toHaveProperty('email');
  });

  test('handles errors gracefully', async () => {
    await expect(fetchUser(999)).rejects.toThrow('User not found');
  });
});
        

Performance Optimization

  • Code Splitting - Load only what you need
  • Lazy Loading - Defer non-critical resources
  • Caching - Store frequently accessed data
  • Compression - Gzip/Brotli for smaller payloads
  • CDN - Serve static assets from edge locations
  • Image Optimization - WebP, lazy loading, responsive images

Security Best Practices

  • 🔒 HTTPS - Always use SSL/TLS
  • 🛡️ Input Validation - Never trust user input
  • 🔑 Authentication - JWT, OAuth, or session-based
  • 🚫 CORS - Configure cross-origin policies
  • 💉 SQL Injection - Use parameterized queries
  • 🎭 XSS Protection - Sanitize output
  • 🔐 Environment Variables - Never commit secrets

CI/CD Pipeline

Automated testing and deployment:


# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm ci
      - run: npm test
      - run: npm run lint

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v3
        with:
          name: build
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v3
      - run: |
          echo "Deploying to production..."
          # Deploy commands here
        

🎯 Key Takeaways

  • Use modern JavaScript features (ES6+)
  • TypeScript adds safety and better tooling
  • Async/await makes async code readable
  • Docker ensures consistency across environments
  • Testing catches bugs early
  • Security should be built in, not bolted on
  • Automate everything with CI/CD

Thank You! 🚀

Questions?

Press Esc for overview | Press ? for help