Session 6.6 – APIs and Web Services

Module 6: Advanced Web Technologies | Duration: 1 hr

Learning Objectives

By the end of this session, students will be able to:

  • Understand the principles of RESTful API design
  • Create RESTful APIs with proper HTTP methods
  • Implement API authentication and authorization
  • Consume third-party APIs in applications
  • Work with JSON and XML data formats
  • Apply API security and best practices

Introduction to APIs and Web Services

APIs (Application Programming Interfaces) enable different software applications to communicate with each other. Web services provide standardized ways for applications to interact over the internet.

What is an API?

An API is a set of rules and protocols that allows one piece of software to interact with another. It defines the methods and data structures that developers can use to interact with an external system.

  • Interface: Defines how components interact
  • Contract: Specifies requests and responses
  • Abstraction: Hides implementation details
  • Standardization: Provides consistent communication
REST APIs

Representational State Transfer - Most common web API architecture

SOAP Services

Simple Object Access Protocol - Enterprise web services standard

GraphQL

Query language for APIs - Flexible data fetching

RESTful API Principles

REST Constraints
Client-Server

Separation of concerns between client and server

Stateless

Each request contains all necessary information

Cacheable

Responses must define cacheability

Uniform Interface

Standardized communication interface

HTTP Methods
GET Read Resources
GET /api/users # Get all users GET /api/users/1 # Get user with ID 1 GET /api/users?page=2 # Get with parameters
POST Create Resources
POST /api/users # Create new user Body: {"name": "John", "email": "john@example.com"}
PUT Update Resources
PUT /api/users/1 # Update entire user Body: {"name": "John Doe", "email": "john@example.com"}
DELETE Delete Resources
DELETE /api/users/1 # Delete user with ID 1
HTTP Status Codes
Success (2xx):
  • 200 OK - Request succeeded
  • 201 Created - Resource created
  • 204 No Content - Success, no response body
Client Errors (4xx):
  • 400 Bad Request - Invalid syntax
  • 401 Unauthorized - Authentication required
  • 403 Forbidden - Insufficient permissions
  • 404 Not Found - Resource doesn't exist
Redirection (3xx):
  • 301 Moved Permanently - Permanent redirect
  • 302 Found - Temporary redirect
Server Errors (5xx):
  • 500 Internal Server Error
  • 503 Service Unavailable

Creating RESTful APIs

PHP REST API Example
<?php // api/users.php header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE'); header('Access-Control-Allow-Headers: Content-Type'); require_once '../config/database.php'; $method = $_SERVER['REQUEST_METHOD']; $request = explode('/', trim($_SERVER['PATH_INFO'], '/')); $userId = isset($request[0]) ? (int)$request[0] : null; try { $pdo = getDatabaseConnection(); switch ($method) { case 'GET': if ($userId) { // Get single user $stmt = $pdo->prepare("SELECT id, name, email FROM users WHERE id = ?"); $stmt->execute([$userId]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if ($user) { http_response_code(200); echo json_encode($user); } else { http_response_code(404); echo json_encode(['error' => 'User not found']); } } else { // Get all users $page = $_GET['page'] ?? 1; $limit = $_GET['limit'] ?? 10; $offset = ($page - 1) * $limit; $stmt = $pdo->prepare("SELECT id, name, email FROM users LIMIT ? OFFSET ?"); $stmt->bindValue(1, (int)$limit, PDO::PARAM_INT); $stmt->bindValue(2, (int)$offset, PDO::PARAM_INT); $stmt->execute(); $users = $stmt->fetchAll(PDO::FETCH_ASSOC); http_response_code(200); echo json_encode([ 'data' => $users, 'page' => (int)$page, 'limit' => (int)$limit ]); } break; case 'POST': // Create new user $input = json_decode(file_get_contents('php://input'), true); if (empty($input['name']) || empty($input['email'])) { http_response_code(400); echo json_encode(['error' => 'Name and email are required']); break; } $stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (?, ?)"); $stmt->execute([$input['name'], $input['email']]); http_response_code(201); echo json_encode([ 'id' => $pdo->lastInsertId(), 'name' => $input['name'], 'email' => $input['email'] ]); break; case 'PUT': // Update user if (!$userId) { http_response_code(400); echo json_encode(['error' => 'User ID required']); break; } $input = json_decode(file_get_contents('php://input'), true); $stmt = $pdo->prepare("UPDATE users SET name = ?, email = ? WHERE id = ?"); $stmt->execute([$input['name'], $input['email'], $userId]); if ($stmt->rowCount() > 0) { http_response_code(200); echo json_encode(['message' => 'User updated successfully']); } else { http_response_code(404); echo json_encode(['error' => 'User not found']); } break; case 'DELETE': // Delete user if (!$userId) { http_response_code(400); echo json_encode(['error' => 'User ID required']); break; } $stmt = $pdo->prepare("DELETE FROM users WHERE id = ?"); $stmt->execute([$userId]); if ($stmt->rowCount() > 0) { http_response_code(204); } else { http_response_code(404); echo json_encode(['error' => 'User not found']); } break; default: http_response_code(405); echo json_encode(['error' => 'Method not allowed']); } } catch (PDOException $e) { http_response_code(500); echo json_encode(['error' => 'Database error']); } ?>
API Router Class
<?php // Router.php class Router { private $routes = []; public function get($path, $callback) { $this->addRoute('GET', $path, $callback); } public function post($path, $callback) { $this->addRoute('POST', $path, $callback); } public function put($path, $callback) { $this->addRoute('PUT', $path, $callback); } public function delete($path, $callback) { $this->addRoute('DELETE', $path, $callback); } private function addRoute($method, $path, $callback) { $this->routes[] = [ 'method' => $method, 'path' => $path, 'callback' => $callback ]; } public function dispatch() { $method = $_SERVER['REQUEST_METHOD']; $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); foreach ($this->routes as $route) { if ($route['method'] === $method) { $pattern = preg_replace('/\{[a-zA-Z]+\}/', '([^/]+)', $route['path']); $pattern = '#^' . $pattern . '$#'; if (preg_match($pattern, $path, $matches)) { array_shift($matches); return call_user_func_array($route['callback'], $matches); } } } http_response_code(404); echo json_encode(['error' => 'Route not found']); } } // Usage $router = new Router(); $router->get('/api/users', function() { // Get all users }); $router->get('/api/users/{id}', function($id) { // Get user by ID }); $router->post('/api/users', function() { // Create new user }); $router->put('/api/users/{id}', function($id) { // Update user }); $router->delete('/api/users/{id}', function($id) { // Delete user }); $router->dispatch(); ?>

Consuming APIs

JavaScript Fetch API
// GET request async function getUsers() { try { const response = await fetch('https://api.example.com/users'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log(data); return data; } catch (error) { console.error('Error fetching users:', error); } } // POST request async function createUser(userData) { try { const response = await fetch('https://api.example.com/users', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_TOKEN' }, body: JSON.stringify(userData) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log('User created:', data); return data; } catch (error) { console.error('Error creating user:', error); } } // PUT request async function updateUser(id, userData) { const response = await fetch(`https://api.example.com/users/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(userData) }); return await response.json(); } // DELETE request async function deleteUser(id) { const response = await fetch(`https://api.example.com/users/${id}`, { method: 'DELETE' }); return response.ok; }
PHP cURL
<?php // GET request function getUsers() { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer YOUR_TOKEN' ]); $response = curl_exec($ch); $statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($statusCode === 200) { return json_decode($response, true); } return null; } // POST request function createUser($userData) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'https://api.example.com/users'); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($userData)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json' ]); $response = curl_exec($ch); curl_close($ch); return json_decode($response, true); } // PUT request function updateUser($id, $userData) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://api.example.com/users/$id"); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT'); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($userData)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json' ]); $response = curl_exec($ch); curl_close($ch); return json_decode($response, true); } ?>
Third-Party API Examples
// Weather API Example async function getWeather(city) { const apiKey = 'YOUR_API_KEY'; const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}`; try { const response = await fetch(url); const data = await response.json(); return data; } catch (error) { console.error('Error fetching weather:', error); } } // GitHub API Example async function getGitHubUser(username) { const url = `https://api.github.com/users/${username}`; const response = await fetch(url, { headers: { 'Accept': 'application/vnd.github.v3+json' } }); return await response.json(); } // Twitter API Example (requires OAuth) async function getTwitterTweets(username) { const url = `https://api.twitter.com/2/users/${username}/tweets`; const response = await fetch(url, { headers: { 'Authorization': 'Bearer YOUR_BEARER_TOKEN' } }); return await response.json(); }

API Authentication

API Key Authentication
<?php // api/endpoint.php $apiKey = $_SERVER['HTTP_X_API_KEY'] ?? ''; if (empty($apiKey) || !isValidApiKey($apiKey)) { http_response_code(401); echo json_encode(['error' => 'Invalid API key']); exit; } function isValidApiKey($key) { $pdo = getDatabaseConnection(); $stmt = $pdo->prepare("SELECT COUNT(*) FROM api_keys WHERE key_value = ? AND is_active = 1"); $stmt->execute([$key]); return $stmt->fetchColumn() > 0; } // Client usage fetch('https://api.example.com/endpoint', { headers: { 'X-API-Key': 'your-api-key-here' } });
JWT (JSON Web Token) Authentication
<?php // Generate JWT function generateJWT($userId) { $header = base64_encode(json_encode(['alg' => 'HS256', 'typ' => 'JWT'])); $payload = base64_encode(json_encode([ 'user_id' => $userId, 'exp' => time() + 3600 // 1 hour expiration ])); $signature = hash_hmac('sha256', "$header.$payload", 'your-secret-key', true); $signature = base64_encode($signature); return "$header.$payload.$signature"; } // Validate JWT function validateJWT($token) { $parts = explode('.', $token); if (count($parts) !== 3) { return false; } list($header, $payload, $signature) = $parts; $validSignature = hash_hmac('sha256', "$header.$payload", 'your-secret-key', true); $validSignature = base64_encode($validSignature); if ($signature !== $validSignature) { return false; } $payload = json_decode(base64_decode($payload), true); if ($payload['exp'] < time()) { return false; // Token expired } return $payload; } // Middleware $authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; $token = str_replace('Bearer ', '', $authHeader); if (!$payload = validateJWT($token)) { http_response_code(401); echo json_encode(['error' => 'Invalid token']); exit; } $userId = $payload['user_id']; ?>
OAuth 2.0
// OAuth 2.0 Authorization Code Flow // Step 1: Redirect user to authorization URL const authUrl = 'https://provider.com/oauth/authorize?' + 'client_id=YOUR_CLIENT_ID&' + 'redirect_uri=https://yourapp.com/callback&' + 'response_type=code&' + 'scope=read write'; window.location.href = authUrl; // Step 2: Handle callback and exchange code for token async function handleCallback(code) { const response = await fetch('https://provider.com/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'authorization_code', code: code, client_id: 'YOUR_CLIENT_ID', client_secret: 'YOUR_CLIENT_SECRET', redirect_uri: 'https://yourapp.com/callback' }) }); const data = await response.json(); const accessToken = data.access_token; // Use access token for API requests return accessToken; } // Step 3: Make authenticated requests async function makeAuthenticatedRequest(accessToken) { const response = await fetch('https://api.provider.com/user', { headers: { 'Authorization': `Bearer ${accessToken}` } }); return await response.json(); }

API Best Practices

Do's
  • Use proper HTTP methods and status codes
  • Version your API (e.g., /api/v1/users)
  • Implement rate limiting
  • Provide comprehensive documentation
  • Use HTTPS for all endpoints
  • Implement proper error handling
  • Return consistent JSON responses
  • Support pagination for lists
  • Validate all input data
  • Use meaningful resource names
Don'ts
  • Don't use verbs in URLs (use HTTP methods)
  • Don't expose sensitive data
  • Don't ignore authentication
  • Don't return inconsistent responses
  • Don't use GET for state-changing operations
  • Don't ignore versioning
  • Don't allow unlimited requests
  • Don't use plain HTTP
  • Don't return full stack traces to clients
  • Don't make breaking changes without notice
API Documentation Example
/** * Get all users * * @route GET /api/v1/users * @param {number} page - Page number (default: 1) * @param {number} limit - Items per page (default: 10) * @returns {object} 200 - List of users * @returns {Error} 500 - Server error * @example * GET /api/v1/users?page=1&limit=10 * * Response: * { * "data": [ * { * "id": 1, * "name": "John Doe", * "email": "john@example.com" * } * ], * "page": 1, * "limit": 10, * "total": 100 * } */

Session Summary

Key Points
  • APIs enable communication between different software systems
  • REST is the most common architectural style for web APIs
  • Use appropriate HTTP methods (GET, POST, PUT, DELETE)
  • Return proper HTTP status codes for different scenarios
  • Implement authentication (API keys, JWT, OAuth)
  • Version your APIs to maintain backwards compatibility
  • Document your API endpoints comprehensively
  • Use HTTPS and validate all input data
  • Implement rate limiting to prevent abuse
  • Return consistent JSON responses with error messages
Next Session Preview

In the next session, we will explore Content Management Systems (CMS), understanding how they work and how to build custom CMS solutions for managing web content.