Skip to content

APIs and Integration

Overview

Node.js excels at building APIs and integrating with external services. This chapter covers REST API development, GraphQL implementation, third-party service integration, authentication, and best practices for API design and consumption.

REST API Development

Basic REST API with Express

javascript
// rest-api-basic.js
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

const app = express();

// Security and middleware
app.use(helmet());
app.use(cors());
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));

// Rate limiting
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // limit each IP to 100 requests per windowMs
  message: {
    error: 'Too many requests from this IP',
    retryAfter: '15 minutes'
  }
});
app.use('/api/', limiter);

// Mock database
let users = [
  { id: 1, name: 'John Doe', email: 'john@example.com', createdAt: new Date() },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com', createdAt: new Date() }
];
let nextId = 3;

// Validation middleware
const validateUser = (req, res, next) => {
  const { name, email } = req.body;
  const errors = [];

  if (!name || typeof name !== 'string' || name.trim().length === 0) {
    errors.push('Name is required and must be a non-empty string');
  }

  if (!email || typeof email !== 'string' || !email.includes('@')) {
    errors.push('Valid email is required');
  }

  if (errors.length > 0) {
    return res.status(400).json({
      success: false,
      message: 'Validation failed',
      errors
    });
  }

  next();
};

// GET /api/users - Get all users with pagination
app.get('/api/users', (req, res) => {
  const { page = 1, limit = 10, search, sortBy = 'id', sortOrder = 'asc' } = req.query;
  
  let filteredUsers = [...users];
  
  // Search functionality
  if (search) {
    const searchLower = search.toLowerCase();
    filteredUsers = users.filter(user => 
      user.name.toLowerCase().includes(searchLower) ||
      user.email.toLowerCase().includes(searchLower)
    );
  }
  
  // Sorting
  filteredUsers.sort((a, b) => {
    const aValue = a[sortBy];
    const bValue = b[sortBy];
    
    if (sortOrder === 'desc') {
      return aValue < bValue ? 1 : -1;
    }
    return aValue > bValue ? 1 : -1;
  });
  
  // Pagination
  const startIndex = (page - 1) * limit;
  const endIndex = startIndex + parseInt(limit);
  const paginatedUsers = filteredUsers.slice(startIndex, endIndex);
  
  res.json({
    success: true,
    data: paginatedUsers,
    pagination: {
      page: parseInt(page),
      limit: parseInt(limit),
      total: filteredUsers.length,
      pages: Math.ceil(filteredUsers.length / limit),
      hasNext: endIndex < filteredUsers.length,
      hasPrev: page > 1
    }
  });
});

// GET /api/users/:id - Get user by ID
app.get('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const user = users.find(u => u.id === id);
  
  if (!user) {
    return res.status(404).json({
      success: false,
      message: 'User not found'
    });
  }
  
  res.json({
    success: true,
    data: user
  });
});

// POST /api/users - Create new user
app.post('/api/users', validateUser, (req, res) => {
  const { name, email } = req.body;
  
  // Check for duplicate email
  const existingUser = users.find(u => u.email === email);
  if (existingUser) {
    return res.status(409).json({
      success: false,
      message: 'User with this email already exists'
    });
  }
  
  const newUser = {
    id: nextId++,
    name: name.trim(),
    email: email.trim().toLowerCase(),
    createdAt: new Date(),
    updatedAt: new Date()
  };
  
  users.push(newUser);
  
  res.status(201).json({
    success: true,
    message: 'User created successfully',
    data: newUser
  });
});

// PUT /api/users/:id - Update user
app.put('/api/users/:id', validateUser, (req, res) => {
  const id = parseInt(req.params.id);
  const userIndex = users.findIndex(u => u.id === id);
  
  if (userIndex === -1) {
    return res.status(404).json({
      success: false,
      message: 'User not found'
    });
  }
  
  const { name, email } = req.body;
  
  // Check for duplicate email (excluding current user)
  const existingUser = users.find(u => u.email === email && u.id !== id);
  if (existingUser) {
    return res.status(409).json({
      success: false,
      message: 'Another user with this email already exists'
    });
  }
  
  users[userIndex] = {
    ...users[userIndex],
    name: name.trim(),
    email: email.trim().toLowerCase(),
    updatedAt: new Date()
  };
  
  res.json({
    success: true,
    message: 'User updated successfully',
    data: users[userIndex]
  });
});

// DELETE /api/users/:id - Delete user
app.delete('/api/users/:id', (req, res) => {
  const id = parseInt(req.params.id);
  const userIndex = users.findIndex(u => u.id === id);
  
  if (userIndex === -1) {
    return res.status(404).json({
      success: false,
      message: 'User not found'
    });
  }
  
  const deletedUser = users.splice(userIndex, 1)[0];
  
  res.json({
    success: true,
    message: 'User deleted successfully',
    data: deletedUser
  });
});

// Health check endpoint
app.get('/health', (req, res) => {
  res.json({
    status: 'healthy',
    timestamp: new Date().toISOString(),
    uptime: process.uptime(),
    memory: process.memoryUsage()
  });
});

// 404 handler
app.use('*', (req, res) => {
  res.status(404).json({
    success: false,
    message: 'Endpoint not found',
    path: req.originalUrl
  });
});

// Error handler
app.use((error, req, res, next) => {
  console.error('API Error:', error);
  
  res.status(error.status || 500).json({
    success: false,
    message: error.message || 'Internal server error',
    ...(process.env.NODE_ENV === 'development' && { stack: error.stack })
  });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`REST API server running on http://localhost:${PORT}`);
});

Advanced REST API Features

javascript
// rest-api-advanced.js
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const multer = require('multer');
const path = require('path');

const app = express();
app.use(express.json());

// JWT Authentication middleware
const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) {
    return res.status(401).json({ error: 'Access token required' });
  }

  jwt.verify(token, process.env.JWT_SECRET || 'secret', (err, user) => {
    if (err) {
      return res.status(403).json({ error: 'Invalid or expired token' });
    }
    req.user = user;
    next();
  });
};

// Role-based authorization
const authorize = (roles = []) => {
  return (req, res, next) => {
    if (!req.user) {
      return res.status(401).json({ error: 'Authentication required' });
    }

    if (roles.length && !roles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }

    next();
  };
};

// File upload configuration
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads/');
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
    cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname));
  }
});

const upload = multer({
  storage,
  limits: {
    fileSize: 5 * 1024 * 1024 // 5MB limit
  },
  fileFilter: (req, file, cb) => {
    const allowedTypes = /jpeg|jpg|png|gif|pdf/;
    const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
    const mimetype = allowedTypes.test(file.mimetype);

    if (mimetype && extname) {
      return cb(null, true);
    } else {
      cb(new Error('Invalid file type'));
    }
  }
});

// API versioning
const v1Router = express.Router();
const v2Router = express.Router();

// Version 1 endpoints
v1Router.get('/users', (req, res) => {
  res.json({
    version: 'v1',
    users: [{ id: 1, name: 'John' }]
  });
});

// Version 2 endpoints (enhanced response format)
v2Router.get('/users', (req, res) => {
  res.json({
    version: 'v2',
    data: {
      users: [{ 
        id: 1, 
        firstName: 'John', 
        lastName: 'Doe',
        profile: { email: 'john@example.com' }
      }],
      meta: {
        total: 1,
        page: 1,
        limit: 10
      }
    }
  });
});

app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

// Content negotiation
app.get('/api/data', (req, res) => {
  const data = { message: 'Hello World', timestamp: new Date() };
  
  res.format({
    'application/json': () => {
      res.json(data);
    },
    'application/xml': () => {
      const xml = `<?xml version="1.0"?>
        <response>
          <message>${data.message}</message>
          <timestamp>${data.timestamp}</timestamp>
        </response>`;
      res.type('application/xml').send(xml);
    },
    'text/plain': () => {
      res.send(`${data.message} - ${data.timestamp}`);
    },
    default: () => {
      res.status(406).json({ error: 'Not Acceptable' });
    }
  });
});

// File upload endpoint
app.post('/api/upload', authenticateToken, upload.single('file'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'No file uploaded' });
  }

  res.json({
    success: true,
    message: 'File uploaded successfully',
    file: {
      filename: req.file.filename,
      originalName: req.file.originalname,
      size: req.file.size,
      mimetype: req.file.mimetype,
      path: req.file.path
    }
  });
});

// Batch operations
app.post('/api/users/batch', authenticateToken, authorize(['admin']), (req, res) => {
  const { operations } = req.body;
  
  if (!Array.isArray(operations)) {
    return res.status(400).json({ error: 'Operations must be an array' });
  }

  const results = operations.map((operation, index) => {
    try {
      switch (operation.type) {
        case 'create':
          return { index, success: true, data: { id: Date.now() + index, ...operation.data } };
        case 'update':
          return { index, success: true, data: { id: operation.id, ...operation.data } };
        case 'delete':
          return { index, success: true, message: `User ${operation.id} deleted` };
        default:
          return { index, success: false, error: 'Unknown operation type' };
      }
    } catch (error) {
      return { index, success: false, error: error.message };
    }
  });

  res.json({
    success: true,
    results,
    summary: {
      total: operations.length,
      successful: results.filter(r => r.success).length,
      failed: results.filter(r => !r.success).length
    }
  });
});

// Server-Sent Events (SSE)
app.get('/api/events', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
    'Access-Control-Allow-Origin': '*'
  });

  // Send initial event
  res.write(`data: ${JSON.stringify({ type: 'connected', timestamp: new Date() })}\n\n`);

  // Send periodic updates
  const interval = setInterval(() => {
    const event = {
      type: 'update',
      data: { value: Math.random(), timestamp: new Date() }
    };
    res.write(`data: ${JSON.stringify(event)}\n\n`);
  }, 5000);

  // Clean up on client disconnect
  req.on('close', () => {
    clearInterval(interval);
    res.end();
  });
});

app.listen(3000);

GraphQL Implementation

Basic GraphQL Server

javascript
// graphql-server.js
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');

// GraphQL schema
const schema = buildSchema(`
  type User {
    id: ID!
    name: String!
    email: String!
    posts: [Post!]!
    createdAt: String!
  }

  type Post {
    id: ID!
    title: String!
    content: String!
    author: User!
    published: Boolean!
    createdAt: String!
  }

  input UserInput {
    name: String!
    email: String!
  }

  input PostInput {
    title: String!
    content: String!
    authorId: ID!
    published: Boolean = false
  }

  type Query {
    users: [User!]!
    user(id: ID!): User
    posts: [Post!]!
    post(id: ID!): Post
    searchUsers(query: String!): [User!]!
  }

  type Mutation {
    createUser(input: UserInput!): User!
    updateUser(id: ID!, input: UserInput!): User!
    deleteUser(id: ID!): Boolean!
    createPost(input: PostInput!): Post!
    updatePost(id: ID!, input: PostInput!): Post!
    deletePost(id: ID!): Boolean!
  }

  type Subscription {
    userAdded: User!
    postAdded: Post!
  }
`);

// Mock data
let users = [
  { id: '1', name: 'John Doe', email: 'john@example.com', createdAt: new Date().toISOString() },
  { id: '2', name: 'Jane Smith', email: 'jane@example.com', createdAt: new Date().toISOString() }
];

let posts = [
  { id: '1', title: 'First Post', content: 'Hello World', authorId: '1', published: true, createdAt: new Date().toISOString() },
  { id: '2', title: 'Second Post', content: 'GraphQL is awesome', authorId: '2', published: false, createdAt: new Date().toISOString() }
];

let nextUserId = 3;
let nextPostId = 3;

// Resolvers
const root = {
  // Queries
  users: () => users,
  user: ({ id }) => users.find(user => user.id === id),
  posts: () => posts.map(post => ({
    ...post,
    author: users.find(user => user.id === post.authorId)
  })),
  post: ({ id }) => {
    const post = posts.find(post => post.id === id);
    if (post) {
      return {
        ...post,
        author: users.find(user => user.id === post.authorId)
      };
    }
    return null;
  },
  searchUsers: ({ query }) => {
    const searchTerm = query.toLowerCase();
    return users.filter(user => 
      user.name.toLowerCase().includes(searchTerm) ||
      user.email.toLowerCase().includes(searchTerm)
    );
  },

  // Mutations
  createUser: ({ input }) => {
    const newUser = {
      id: String(nextUserId++),
      name: input.name,
      email: input.email,
      createdAt: new Date().toISOString()
    };
    users.push(newUser);
    return newUser;
  },

  updateUser: ({ id, input }) => {
    const userIndex = users.findIndex(user => user.id === id);
    if (userIndex === -1) {
      throw new Error('User not found');
    }
    
    users[userIndex] = {
      ...users[userIndex],
      ...input
    };
    
    return users[userIndex];
  },

  deleteUser: ({ id }) => {
    const userIndex = users.findIndex(user => user.id === id);
    if (userIndex === -1) {
      return false;
    }
    
    users.splice(userIndex, 1);
    // Also delete user's posts
    posts = posts.filter(post => post.authorId !== id);
    return true;
  },

  createPost: ({ input }) => {
    const author = users.find(user => user.id === input.authorId);
    if (!author) {
      throw new Error('Author not found');
    }

    const newPost = {
      id: String(nextPostId++),
      title: input.title,
      content: input.content,
      authorId: input.authorId,
      published: input.published,
      createdAt: new Date().toISOString()
    };
    
    posts.push(newPost);
    
    return {
      ...newPost,
      author
    };
  },

  updatePost: ({ id, input }) => {
    const postIndex = posts.findIndex(post => post.id === id);
    if (postIndex === -1) {
      throw new Error('Post not found');
    }
    
    posts[postIndex] = {
      ...posts[postIndex],
      ...input
    };
    
    const author = users.find(user => user.id === posts[postIndex].authorId);
    
    return {
      ...posts[postIndex],
      author
    };
  },

  deletePost: ({ id }) => {
    const postIndex = posts.findIndex(post => post.id === id);
    if (postIndex === -1) {
      return false;
    }
    
    posts.splice(postIndex, 1);
    return true;
  }
};

// Add posts field to User type
const originalUserResolver = root.user;
root.user = ({ id }) => {
  const user = users.find(user => user.id === id);
  if (user) {
    return {
      ...user,
      posts: posts.filter(post => post.authorId === id).map(post => ({
        ...post,
        author: user
      }))
    };
  }
  return null;
};

const app = express();

app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true, // Enable GraphiQL interface
}));

app.listen(4000, () => {
  console.log('GraphQL server running on http://localhost:4000/graphql');
});

Third-Party API Integration

HTTP Client Wrapper

javascript
// api-client.js
const axios = require('axios');

class APIClient {
  constructor(baseURL, options = {}) {
    this.client = axios.create({
      baseURL,
      timeout: options.timeout || 10000,
      headers: {
        'Content-Type': 'application/json',
        ...options.headers
      }
    });

    this.retryConfig = {
      retries: options.retries || 3,
      retryDelay: options.retryDelay || 1000,
      retryCondition: options.retryCondition || this.defaultRetryCondition
    };

    this.setupInterceptors();
  }

  setupInterceptors() {
    // Request interceptor
    this.client.interceptors.request.use(
      (config) => {
        console.log(`Making ${config.method.toUpperCase()} request to ${config.url}`);
        return config;
      },
      (error) => {
        console.error('Request error:', error);
        return Promise.reject(error);
      }
    );

    // Response interceptor
    this.client.interceptors.response.use(
      (response) => {
        console.log(`Response received: ${response.status} ${response.statusText}`);
        return response;
      },
      async (error) => {
        const originalRequest = error.config;

        if (this.shouldRetry(error) && !originalRequest._retry) {
          originalRequest._retry = true;
          originalRequest._retryCount = (originalRequest._retryCount || 0) + 1;

          if (originalRequest._retryCount <= this.retryConfig.retries) {
            console.log(`Retrying request (${originalRequest._retryCount}/${this.retryConfig.retries})`);
            
            await this.delay(this.retryConfig.retryDelay * originalRequest._retryCount);
            return this.client(originalRequest);
          }
        }

        return Promise.reject(error);
      }
    );
  }

  defaultRetryCondition(error) {
    return (
      error.code === 'ECONNABORTED' ||
      error.code === 'ENOTFOUND' ||
      error.code === 'ECONNRESET' ||
      (error.response && error.response.status >= 500)
    );
  }

  shouldRetry(error) {
    return this.retryConfig.retryCondition(error);
  }

  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  // Authentication methods
  setAuthToken(token) {
    this.client.defaults.headers.common['Authorization'] = `Bearer ${token}`;
  }

  setApiKey(key, headerName = 'X-API-Key') {
    this.client.defaults.headers.common[headerName] = key;
  }

  // HTTP methods
  async get(url, config = {}) {
    try {
      const response = await this.client.get(url, config);
      return response.data;
    } catch (error) {
      throw this.handleError(error);
    }
  }

  async post(url, data, config = {}) {
    try {
      const response = await this.client.post(url, data, config);
      return response.data;
    } catch (error) {
      throw this.handleError(error);
    }
  }

  async put(url, data, config = {}) {
    try {
      const response = await this.client.put(url, data, config);
      return response.data;
    } catch (error) {
      throw this.handleError(error);
    }
  }

  async delete(url, config = {}) {
    try {
      const response = await this.client.delete(url, config);
      return response.data;
    } catch (error) {
      throw this.handleError(error);
    }
  }

  handleError(error) {
    if (error.response) {
      // Server responded with error status
      const apiError = new Error(error.response.data?.message || error.message);
      apiError.status = error.response.status;
      apiError.data = error.response.data;
      return apiError;
    } else if (error.request) {
      // Request was made but no response received
      const networkError = new Error('Network error: No response received');
      networkError.code = 'NETWORK_ERROR';
      return networkError;
    } else {
      // Something else happened
      return error;
    }
  }
}

// Service-specific API clients
class GitHubAPI extends APIClient {
  constructor(token) {
    super('https://api.github.com', {
      headers: {
        'Authorization': `token ${token}`,
        'Accept': 'application/vnd.github.v3+json'
      }
    });
  }

  async getUser(username) {
    return this.get(`/users/${username}`);
  }

  async getUserRepos(username) {
    return this.get(`/users/${username}/repos`);
  }

  async createRepo(data) {
    return this.post('/user/repos', data);
  }
}

class StripeAPI extends APIClient {
  constructor(secretKey) {
    super('https://api.stripe.com/v1', {
      headers: {
        'Authorization': `Bearer ${secretKey}`
      }
    });
  }

  async createCustomer(data) {
    return this.post('/customers', data);
  }

  async createPaymentIntent(data) {
    return this.post('/payment_intents', data);
  }

  async getCustomer(customerId) {
    return this.get(`/customers/${customerId}`);
  }
}

// Usage examples
async function demonstrateAPIIntegration() {
  // GitHub API example
  const github = new GitHubAPI(process.env.GITHUB_TOKEN);
  
  try {
    const user = await github.getUser('octocat');
    console.log('GitHub user:', user.name);
    
    const repos = await github.getUserRepos('octocat');
    console.log('Repository count:', repos.length);
  } catch (error) {
    console.error('GitHub API error:', error.message);
  }

  // Generic API client example
  const jsonPlaceholder = new APIClient('https://jsonplaceholder.typicode.com');
  
  try {
    const posts = await jsonPlaceholder.get('/posts');
    console.log('Posts count:', posts.length);
    
    const newPost = await jsonPlaceholder.post('/posts', {
      title: 'Test Post',
      body: 'This is a test post',
      userId: 1
    });
    console.log('Created post:', newPost.id);
  } catch (error) {
    console.error('JSONPlaceholder API error:', error.message);
  }
}

module.exports = { APIClient, GitHubAPI, StripeAPI };

Webhook Handling

javascript
// webhook-handler.js
const express = require('express');
const crypto = require('crypto');
const bodyParser = require('body-parser');

class WebhookHandler {
  constructor() {
    this.handlers = new Map();
    this.middleware = [];
  }

  // Register webhook handler
  register(event, handler) {
    if (!this.handlers.has(event)) {
      this.handlers.set(event, []);
    }
    this.handlers.get(event).push(handler);
  }

  // Add middleware
  use(middleware) {
    this.middleware.push(middleware);
  }

  // Verify webhook signature
  verifySignature(payload, signature, secret, algorithm = 'sha256') {
    const expectedSignature = crypto
      .createHmac(algorithm, secret)
      .update(payload)
      .digest('hex');
    
    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expectedSignature)
    );
  }

  // Process webhook
  async process(event, payload, headers = {}) {
    // Run middleware
    for (const middleware of this.middleware) {
      await middleware(event, payload, headers);
    }

    // Get handlers for event
    const handlers = this.handlers.get(event) || [];
    
    // Execute all handlers
    const results = await Promise.allSettled(
      handlers.map(handler => handler(payload, headers))
    );

    // Log any failures
    results.forEach((result, index) => {
      if (result.status === 'rejected') {
        console.error(`Handler ${index} failed for event ${event}:`, result.reason);
      }
    });

    return results;
  }

  // Create Express middleware
  createExpressHandler(options = {}) {
    const {
      path = '/webhook',
      secret,
      signatureHeader = 'x-signature',
      eventHeader = 'x-event-type'
    } = options;

    const router = express.Router();

    // Raw body parser for signature verification
    router.use(path, bodyParser.raw({ type: 'application/json' }));

    router.post(path, async (req, res) => {
      try {
        const payload = req.body;
        const signature = req.headers[signatureHeader];
        const event = req.headers[eventHeader];

        if (!event) {
          return res.status(400).json({ error: 'Missing event type header' });
        }

        // Verify signature if secret is provided
        if (secret && signature) {
          if (!this.verifySignature(payload, signature, secret)) {
            return res.status(401).json({ error: 'Invalid signature' });
          }
        }

        // Parse JSON payload
        const parsedPayload = JSON.parse(payload.toString());

        // Process webhook
        await this.process(event, parsedPayload, req.headers);

        res.status(200).json({ success: true, message: 'Webhook processed' });
      } catch (error) {
        console.error('Webhook processing error:', error);
        res.status(500).json({ error: 'Webhook processing failed' });
      }
    });

    return router;
  }
}

// Usage example
const webhookHandler = new WebhookHandler();

// Add logging middleware
webhookHandler.use(async (event, payload, headers) => {
  console.log(`Received webhook: ${event}`, {
    timestamp: new Date().toISOString(),
    payloadSize: JSON.stringify(payload).length,
    userAgent: headers['user-agent']
  });
});

// Register event handlers
webhookHandler.register('user.created', async (payload) => {
  console.log('New user created:', payload.user.email);
  // Send welcome email
  // Update analytics
});

webhookHandler.register('payment.completed', async (payload) => {
  console.log('Payment completed:', payload.payment.id);
  // Update order status
  // Send confirmation email
});

webhookHandler.register('order.shipped', async (payload) => {
  console.log('Order shipped:', payload.order.id);
  // Send tracking information
  // Update inventory
});

// Create Express app
const app = express();

// Mount webhook handler
app.use(webhookHandler.createExpressHandler({
  path: '/webhooks',
  secret: process.env.WEBHOOK_SECRET,
  signatureHeader: 'x-hub-signature-256',
  eventHeader: 'x-github-event'
}));

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

module.exports = WebhookHandler;

Next Steps

In the next chapter, we'll explore error handling strategies and best practices for building robust Node.js applications.

Practice Exercises

  1. Build a complete REST API with authentication, authorization, and file uploads
  2. Create a GraphQL API with subscriptions using WebSockets
  3. Implement a webhook system for processing GitHub events
  4. Build an API gateway that aggregates multiple microservices

Key Takeaways

  • REST APIs provide a standardized way to expose application functionality
  • GraphQL offers flexible data querying and real-time subscriptions
  • Proper authentication and authorization are essential for API security
  • HTTP client wrappers simplify third-party API integration
  • Webhook handlers enable real-time event processing
  • API versioning ensures backward compatibility
  • Rate limiting and validation protect against abuse
  • Comprehensive error handling improves API reliability

Content is for learning and research only.