A Technical Overview
Today's web applications are built with powerful, scalable technologies:
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>
);
}
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());
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);
}
}
// 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);
}
});
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();
});
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);
});
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"]
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:
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');
});
});
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
Press Esc for overview | Press ? for help