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
- 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'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.