A comprehensive guide to building, integrating, and deploying REST APIs with authentication, database connectivity, and external service integration. Suitable for beginners to advanced developers.
Table of Contents
- 1. Introduction to APIs
- 2. Setting Up Backend API
- 3. Frontend Integration
- 4. Authentication Flow
- 5. Integrating Two Different Systems
- 6. Database Integration
- 7. Advanced Concepts
- 8. Architecture Explanation
- 9. Best Practices
- 10. Getting Started & Deployment
An API (Application Programming Interface) is a set of rules and protocols that allows different software applications to communicate with each other. It defines the methods and data formats that applications can use to request and exchange information.
Think of it like a waiter in a restaurant:
- Customer = Client Application
- Waiter = API
- Kitchen = Server/Backend
- Dish = Response/Data
The customer doesn't need to know how the kitchen works; the waiter handles the communication.
- Uses HTTP methods (GET, POST, PUT, DELETE, PATCH)
- Stateless architecture
- Easy to understand and implement
- Returns JSON or XML
- Most popular for web applications
Example:
GET /api/users → Fetch all users
POST /api/users → Create new user
GET /api/users/1 → Fetch user with ID 1
PUT /api/users/1 → Update user with ID 1
DELETE /api/users/1 → Delete user with ID 1
- Protocol-heavy, uses XML
- More complex and structured
- Better for enterprise systems
- Slower than REST
- Less commonly used in modern development
- Query language for APIs
- Request only the data you need
- Single endpoint for all queries
- Reduces over-fetching of data
- Growing in popularity
Example GraphQL Query:
query {
user(id: 1) {
name
email
posts {
title
}
}
}| Method | Purpose | Safe | Idempotent | Body |
|---|---|---|---|---|
| GET | Retrieve data | Yes | Yes | No |
| POST | Create new resource | No | No | Yes |
| PUT | Replace entire resource | No | Yes | Yes |
| PATCH | Partially update resource | No | No | Yes |
| DELETE | Delete resource | No | Yes | No |
| HEAD | Like GET but without response body | Yes | Yes | No |
Key Terms:
- Safe: Method doesn't modify data
- Idempotent: Multiple identical requests = single request
| Code | Meaning |
|---|---|
| 200 | OK - Request successful |
| 201 | Created - Resource created successfully |
| 204 | No Content - Successful but no content to return |
| Code | Meaning |
|---|---|
| 301 | Moved Permanently |
| 302 | Found (Temporary redirect) |
| 304 | Not Modified |
| Code | Meaning |
|---|---|
| 400 | Bad Request - Invalid syntax |
| 401 | Unauthorized - Authentication required |
| 403 | Forbidden - Authenticated but no permission |
| 404 | Not Found - Resource doesn't exist |
| 429 | Too Many Requests - Rate limited |
| Code | Meaning |
|---|---|
| 500 | Internal Server Error |
| 502 | Bad Gateway |
| 503 | Service Unavailable |
JSON (JavaScript Object Notation) is the standard format for API responses.
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"isActive": true,
"age": null,
"roles": ["admin", "user"],
"metadata": {
"createdAt": "2024-01-15",
"lastLogin": "2024-02-16"
}
}JSON Data Types:
string- Text enclosed in quotesnumber- Integer or decimalboolean- true or falsenull- Empty/no valuearray- List of items[]object- Key-value pairs{}
Simple but less secure. Key sent with each request.
headers: {
"Authorization": "Bearer YOUR_API_KEY"
}Use Case: Public APIs, internal services
Pros: Simple, lightweight Cons: Less secure, key exposed if leaked
Industry standard. Token contains encoded claims.
Structure: header.payload.signature
Example:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Use Case: Modern web applications, microservices
Pros: Stateless, scalable, includes claims Cons: Token size, can't revoke immediately
Third-party authentication. User grants permission to access resources.
1. User clicks "Login with Google"
2. Redirected to Google login
3. Google returns authorization code
4. App exchanges code for access token
5. App uses token to access user data
Use Case: Social login, delegated access
Pros: Secure, user doesn't share password, granular permissions Cons: Complex implementation, external dependency
backend/
├── server.js # Entry point
├── package.json # Dependencies
├── .env # Environment variables (local)
├── .env.example # Example env file
├── routes/
│ ├── users.js # User routes
│ └── auth.js # Auth routes
├── controllers/
│ ├── userController.js # User logic
│ └── authController.js # Auth logic
├── middleware/
│ ├── auth.js # JWT verification
│ ├── errorHandler.js # Error handling
│ └── rateLimiter.js # Rate limiting
├── models/
│ └── User.js # Data model
├── config/
│ └── db.js # Database config
└── utils/
└── logger.js # Logging utility
# Initialize Node project
npm init -y
# Install dependencies
npm install express dotenv jsonwebtoken bcrypt cors axios
npm install --save-dev nodemon
# Or install from package.json
npm installCreate server.js:
const express = require("express");
const dotenv = require("dotenv");
const cors = require("cors");
const errorHandler = require("./middleware/errorHandler");
// Load environment variables
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
// ============ MIDDLEWARE ============
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
// ============ ROUTES ============
app.get("/", (req, res) => {
res.json({
message: "Welcome to API Integration Guide",
version: "1.0.0",
endpoints: {
users: "/api/v1/users",
auth: "/api/v1/auth"
}
});
});
app.get("/api/users", (req, res) => {
res.status(200).json({
message: "Users fetched successfully",
users: [
{ id: 1, name: "John Doe", email: "john@example.com" },
{ id: 2, name: "Jane Smith", email: "jane@example.com" }
]
});
});
// Health check endpoint
app.get("/health", (req, res) => {
res.status(200).json({ status: "Server is running" });
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: "Route not found" });
});
// Error handling middleware
app.use(errorHandler);
// ============ START SERVER ============
app.listen(PORT, () => {
console.log(`✓ Server running on http://localhost:${PORT}`);
});
module.exports = app;Create controllers/userController.js:
// In-memory database (replace with real DB)
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;
// GET all users
exports.getAllUsers = (req, res) => {
const { page = 1, limit = 10 } = req.query;
const startIndex = (page - 1) * limit;
const paginatedUsers = users.slice(startIndex, startIndex + parseInt(limit));
res.status(200).json({
data: paginatedUsers,
total: users.length,
page: parseInt(page),
limit: parseInt(limit)
});
};
// GET single user
exports.getUserById = (req, res) => {
const { id } = req.params;
const user = users.find(u => u.id === parseInt(id));
if (!user) {
return res.status(404).json({ error: "User not found" });
}
res.status(200).json({ data: user });
};
// CREATE user
exports.createUser = (req, res) => {
const { name, email } = req.body;
// Validation
if (!name || !email) {
return res.status(400).json({
error: "Name and email are required"
});
}
// Check if email exists
if (users.some(u => u.email === email)) {
return res.status(409).json({
error: "Email already exists"
});
}
const newUser = {
id: nextId++,
name,
email,
createdAt: new Date()
};
users.push(newUser);
res.status(201).json({
message: "User created successfully",
data: newUser
});
};
// UPDATE user
exports.updateUser = (req, res) => {
const { id } = req.params;
const { name, email } = req.body;
const user = users.find(u => u.id === parseInt(id));
if (!user) {
return res.status(404).json({ error: "User not found" });
}
// Update only provided fields
if (name) user.name = name;
if (email) user.email = email;
user.updatedAt = new Date();
res.status(200).json({
message: "User updated successfully",
data: user
});
};
// DELETE user
exports.deleteUser = (req, res) => {
const { id } = req.params;
const index = users.findIndex(u => u.id === parseInt(id));
if (index === -1) {
return res.status(404).json({ error: "User not found" });
}
const deletedUser = users.splice(index, 1);
res.status(200).json({
message: "User deleted successfully",
data: deletedUser[0]
});
};Create routes/users.js:
const express = require("express");
const userController = require("../controllers/userController");
const auth = require("../middleware/auth");
const router = express.Router();
// Public routes
router.get("/", userController.getAllUsers);
router.get("/:id", userController.getUserById);
// Protected routes (require authentication)
router.post("/", auth, userController.createUser);
router.put("/:id", auth, userController.updateUser);
router.delete("/:id", auth, userController.deleteUser);
module.exports = router;Update server.js to include routes:
const userRoutes = require("./routes/users");
// After other middleware
app.use("/api/v1/users", userRoutes);Create middleware/auth.js:
const jwt = require("jsonwebtoken");
const auth = (req, res, next) => {
try {
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "No token provided" });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: "Invalid token" });
}
};
module.exports = auth;Create middleware/errorHandler.js:
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
res.status(err.status || 500).json({
error: err.message || "Internal Server Error",
status: err.status || 500
});
};
module.exports = errorHandler;Create middleware/rateLimiter.js:
const rateLimit = require("express-rate-limit");
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Max 100 requests per windowMs
message: "Too many requests, please try again later",
standardHeaders: true,
legacyHeaders: false
});
module.exports = limiter;Best practice error handling in controllers:
exports.createUser = async (req, res, next) => {
try {
const { name, email } = req.body;
// Validation
if (!name || !email) {
const error = new Error("Name and email are required");
error.status = 400;
throw error;
}
// Database operation
const newUser = await User.create({ name, email });
res.status(201).json({
message: "User created",
data: newUser
});
} catch (error) {
next(error); // Pass to error handler middleware
}
};Create .env:
PORT=3000
NODE_ENV=development
# Database
DB_HOST=localhost
DB_USER=admin
DB_PASSWORD=yourpassword
DB_NAME=api_db
# JWT
JWT_SECRET=your_super_secret_jwt_key_change_this_in_production
JWT_EXPIRE=7d
# External APIs
WEATHER_API_KEY=your_weather_api_key
EXTERNAL_API_URL=https://api.example.com
# Logging
LOG_LEVEL=debug
Create .env.example (commit this, not .env):
PORT=3000
NODE_ENV=development
DB_HOST=localhost
DB_USER=admin
DB_PASSWORD=
DB_NAME=api_db
JWT_SECRET=
JWT_EXPIRE=7d
WEATHER_API_KEY=
EXTERNAL_API_URL=
LOG_LEVEL=debug
Access in code:
const PORT = process.env.PORT || 3000;
const JWT_SECRET = process.env.JWT_SECRET;
const NODE_ENV = process.env.NODE_ENV;Create controllers/authController.js:
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
// Simulated database
const users = [
{
id: 1,
email: "user@example.com",
password: "$2b$10$..." // bcrypt hashed
}
];
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
// Validate input
if (!email || !password) {
return res.status(400).json({ error: "Email and password required" });
}
// Find user
const user = users.find(u => u.email === email);
if (!user) {
return res.status(401).json({ error: "Invalid credentials" });
}
// Verify password
const isPasswordValid = await bcrypt.compare(password, user.password);
if (!isPasswordValid) {
return res.status(401).json({ error: "Invalid credentials" });
}
// Generate JWT token
const token = jwt.sign(
{
id: user.id,
email: user.email,
role: "user"
},
process.env.JWT_SECRET,
{ expiresIn: process.env.JWT_EXPIRE || "7d" }
);
res.status(200).json({
message: "Login successful",
token,
user: {
id: user.id,
email: user.email
}
});
} catch (error) {
res.status(500).json({ error: "Login failed" });
}
};
exports.register = async (req, res) => {
try {
const { email, password } = req.body;
// Validation
if (!email || !password) {
return res.status(400).json({ error: "Email and password required" });
}
// Check if user exists
if (users.find(u => u.email === email)) {
return res.status(409).json({ error: "Email already registered" });
}
// Hash password
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = {
id: users.length + 1,
email,
password: hashedPassword
};
users.push(newUser);
res.status(201).json({
message: "Registration successful",
user: {
id: newUser.id,
email: newUser.email
}
});
} catch (error) {
res.status(500).json({ error: "Registration failed" });
}
};The Fetch API is a modern browser API for making HTTP requests. It's the replacement for XMLHttpRequest and provides a simpler, promise-based approach.
fetch(url, options)
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));Frontend (Browser)
↓
Fetch Request
↓
Backend Server
↓
Processing
↓
Send Response
↓
Frontend receives JSON
↓
Display/Process Data
// Simple GET request
fetch("http://localhost:3000/api/v1/users")
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log("Users:", data);
// Update DOM
displayUsers(data.data);
})
.catch(error => console.error("Error fetching users:", error));
// With query parameters
const params = new URLSearchParams({
page: 1,
limit: 10
});
fetch(`http://localhost:3000/api/v1/users?${params}`)
.then(res => res.json())
.then(data => console.log(data));// Create new user
fetch("http://localhost:3000/api/v1/users", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${token}` // Include JWT token
},
body: JSON.stringify({
name: "John Doe",
email: "john@example.com"
})
})
.then(response => {
if (response.status === 201) {
console.log("User created successfully");
return response.json();
} else if (response.status === 400) {
throw new Error("Invalid input");
} else if (response.status === 401) {
throw new Error("Unauthorized - please login");
}
})
.then(data => {
console.log("New user:", data.data);
// Refresh user list
loadUsers();
})
.catch(error => console.error("Error creating user:", error));async function fetchUserData(userId) {
try {
const response = await fetch(`http://localhost:3000/api/v1/users/${userId}`);
if (!response.ok) {
// Handle different error statuses
switch (response.status) {
case 404:
throw new Error("User not found");
case 401:
throw new Error("Please login first");
case 500:
throw new Error("Server error - please try again");
default:
throw new Error(`Error: ${response.status}`);
}
}
const data = await response.json();
return data;
} catch (error) {
console.error("Fetch error:", error.message);
// Update UI with error message
showErrorMessage(error.message);
throw error;
}
}let isLoading = false;
async function loadUsers() {
if (isLoading) return; // Prevent duplicate requests
isLoading = true;
showLoadingSpinner(true);
try {
const response = await fetch("http://localhost:3000/api/v1/users");
const data = await response.json();
displayUsers(data.data);
} catch (error) {
showErrorMessage("Failed to load users");
} finally {
isLoading = false;
showLoadingSpinner(false);
}
}Create frontend/index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Integration - User Management</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 900px;
margin: 0 auto;
background: white;
border-radius: 10px;
padding: 30px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
}
h1 {
color: #333;
margin-bottom: 30px;
text-align: center;
}
.auth-section {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
color: #333;
font-weight: 500;
}
input, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
font-size: 14px;
}
button {
background: #667eea;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-weight: 600;
margin-right: 10px;
}
button:hover {
background: #764ba2;
}
.users-section {
margin-top: 30px;
}
.user-card {
background: #f9f9f9;
border: 1px solid #ddd;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
display: flex;
justify-content: space-between;
align-items: center;
}
.user-info h3 {
margin: 5px 0;
color: #333;
}
.user-info p {
color: #666;
font-size: 14px;
}
.user-actions button {
padding: 5px 10px;
font-size: 13px;
margin: 0 5px;
}
.loading {
text-align: center;
padding: 20px;
color: #666;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
margin: 10px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.error {
background: #fee;
color: #c33;
padding: 15px;
border-radius: 5px;
margin: 10px 0;
}
.success {
background: #efe;
color: #3c3;
padding: 15px;
border-radius: 5px;
margin: 10px 0;
}
.token-display {
background: #f0f0f0;
padding: 10px;
border-radius: 5px;
word-break: break-all;
font-size: 12px;
font-family: monospace;
max-height: 100px;
overflow-y: auto;
}
</style>
</head>
<body>
<div class="container">
<h1>📱 API Integration - User Management</h1>
<!-- Authentication Section -->
<div class="auth-section">
<h2>Login</h2>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" placeholder="user@example.com" value="user@example.com">
</div>
<div class="form-group">
<label for="password">Password:</label>
<input type="password" id="password" placeholder="password" value="password123">
</div>
<button onclick="login()">Login</button>
<div id="tokenDisplay" style="margin-top: 15px; display: none;">
<p><strong>JWT Token:</strong></p>
<div class="token-display" id="tokenValue"></div>
<button onclick="logout()" style="margin-top: 10px;">Logout</button>
</div>
</div>
<!-- Message Display -->
<div id="message"></div>
<!-- Create User Section -->
<div class="auth-section" style="margin-top: 30px;">
<h2>Create New User</h2>
<div class="form-group">
<label for="newName">Name:</label>
<input type="text" id="newName" placeholder="Enter name">
</div>
<div class="form-group">
<label for="newEmail">Email:</label>
<input type="email" id="newEmail" placeholder="Enter email">
</div>
<button onclick="createUser()">Create User</button>
</div>
<!-- Users List Section -->
<div class="users-section">
<h2>Users List</h2>
<button onclick="loadUsers()">🔄 Refresh</button>
<div id="usersList"></div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>Create frontend/app.js:
const API_BASE_URL = "http://localhost:3000/api/v1";
let authToken = localStorage.getItem("authToken");
// ============ AUTHENTICATION ============
async function login() {
const email = document.getElementById("email").value;
const password = document.getElementById("password").value;
if (!email || !password) {
showMessage("Email and password required", "error");
return;
}
try {
const response = await fetch(`${API_BASE_URL}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (response.ok) {
authToken = data.token;
localStorage.setItem("authToken", authToken);
showMessage("Login successful!", "success");
displayToken(authToken);
loadUsers();
} else {
showMessage(data.error || "Login failed", "error");
}
} catch (error) {
showMessage("Error: " + error.message, "error");
}
}
function logout() {
authToken = null;
localStorage.removeItem("authToken");
document.getElementById("tokenDisplay").style.display = "none";
showMessage("Logged out successfully", "success");
loadUsers();
}
function displayToken(token) {
document.getElementById("tokenDisplay").style.display = "block";
document.getElementById("tokenValue").textContent = token;
}
// ============ USER OPERATIONS ============
async function loadUsers() {
const usersList = document.getElementById("usersList");
usersList.innerHTML = '<div class="loading"><div class="spinner"></div> Loading users...</div>';
try {
const response = await fetch(`${API_BASE_URL}/users`);
const data = await response.json();
if (response.ok && data.data.length > 0) {
usersList.innerHTML = data.data.map(user => `
<div class="user-card">
<div class="user-info">
<h3>${user.name || 'N/A'}</h3>
<p>📧 ${user.email}</p>
<p>🕐 Created: ${new Date(user.createdAt).toLocaleDateString()}</p>
</div>
<div class="user-actions">
<button onclick="deleteUser(${user.id})">Delete</button>
</div>
</div>
`).join("");
} else {
usersList.innerHTML = "<p>No users found</p>";
}
} catch (error) {
usersList.innerHTML = `<div class="error">Error loading users: ${error.message}</div>`;
}
}
async function createUser() {
const name = document.getElementById("newName").value;
const email = document.getElementById("newEmail").value;
if (!name || !email) {
showMessage("Name and email required", "error");
return;
}
if (!authToken) {
showMessage("Please login first", "error");
return;
}
try {
const response = await fetch(`${API_BASE_URL}/users`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${authToken}`
},
body: JSON.stringify({ name, email })
});
const data = await response.json();
if (response.ok) {
showMessage("User created successfully!", "success");
document.getElementById("newName").value = "";
document.getElementById("newEmail").value = "";
loadUsers();
} else {
showMessage(data.error || "Error creating user", "error");
}
} catch (error) {
showMessage("Error: " + error.message, "error");
}
}
async function deleteUser(userId) {
if (!authToken) {
showMessage("Please login first", "error");
return;
}
if (!confirm("Are you sure?")) return;
try {
const response = await fetch(`${API_BASE_URL}/users/${userId}`, {
method: "DELETE",
headers: { "Authorization": `Bearer ${authToken}` }
});
if (response.ok) {
showMessage("User deleted successfully!", "success");
loadUsers();
} else {
showMessage("Error deleting user", "error");
}
} catch (error) {
showMessage("Error: " + error.message, "error");
}
}
// ============ UTILITIES ============
function showMessage(text, type) {
const messageDiv = document.getElementById("message");
messageDiv.className = type;
messageDiv.textContent = text;
setTimeout(() => {
messageDiv.textContent = "";
}, 4000);
}
// Load users on page load
window.addEventListener("load", () => {
loadUsers();
if (authToken) {
displayToken(authToken);
}
});Login Flow:
1. User enters email/password
↓
2. Frontend sends POST to /api/auth/login
↓
3. Backend verifies credentials
↓
4. Backend generates JWT token
↓
5. Token sent back to frontend
↓
6. Frontend stores token (localStorage)
↓
7. Token sent in Authorization header for protected routes
↓
8. Backend verifies token with middleware
↓
9. Route handler executes
Already shown in section 2 as authController.js. Key points:
// 1. Verify credentials
const user = users.find(u => u.email === email);
const isPasswordValid = await bcrypt.compare(password, user.password);
// 2. Generate JWT token
const token = jwt.sign(
{ id: user.id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: "7d" }
);
// 3. Return token
res.json({ token, user });A JWT consists of three parts separated by dots:
Header.Payload.Signature
Header (Base64 encoded):
{
"alg": "HS256",
"typ": "JWT"
}Payload (Base64 encoded):
{
"id": 1,
"email": "user@example.com",
"role": "user",
"iat": 1705335600,
"exp": 1706194800
}Signature (HMAC SHA256):
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
// After login, store token
localStorage.setItem("authToken", token);
// Retrieve token
const token = localStorage.getItem("authToken");
// Send with requests
fetch(url, {
headers: {
"Authorization": `Bearer ${token}`
}
});
// Clear on logout
localStorage.removeItem("authToken");const auth = (req, res, next) => {
try {
// Extract token from header
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "No token provided" });
}
// Verify token
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Attach user to request
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: "Invalid token" });
}
};class AuthManager {
static setToken(token) {
localStorage.setItem("authToken", token);
}
static getToken() {
return localStorage.getItem("authToken");
}
static removeToken() {
localStorage.removeItem("authToken");
}
static isAuthenticated() {
return !!this.getToken();
}
static getHeaders() {
const token = this.getToken();
return {
"Content-Type": "application/json",
...(token && { "Authorization": `Bearer ${token}` })
};
}
}
// Usage
async function makeAuthenticatedRequest(url, options = {}) {
const response = await fetch(url, {
...options,
headers: AuthManager.getHeaders()
});
if (response.status === 401) {
AuthManager.removeToken();
window.location.href = "/login";
}
return response;
}System A: Your backend server System B: External weather API service
Frontend (React/Vue/HTML)
↓
Backend Server (Node.js)
↓
External API (OpenWeatherMap)
↓
Get weather data
↓
Transform response
↓
Return to frontend
Create controllers/weatherController.js:
const axios = require("axios");
const EXTERNAL_API_KEY = process.env.WEATHER_API_KEY;
const EXTERNAL_API_URL = "https://api.openweathermap.org/data/2.5/weather";
// Cache to reduce API calls
const weatherCache = new Map();
const CACHE_DURATION = 10 * 60 * 1000; // 10 minutes
exports.getWeather = async (req, res) => {
try {
const { city } = req.query;
if (!city) {
return res.status(400).json({ error: "City parameter required" });
}
// Check cache
if (weatherCache.has(city)) {
const cached = weatherCache.get(city);
if (Date.now() - cached.timestamp < CACHE_DURATION) {
console.log(`✓ Returning cached weather for ${city}`);
return res.json({ data: cached.data, cached: true });
}
}
// Call external API
const response = await axios.get(EXTERNAL_API_URL, {
params: {
q: city,
appid: EXTERNAL_API_KEY,
units: "metric" // Celsius
},
timeout: 5000 // 5 second timeout
});
// Transform response
const transformedData = {
city: response.data.name,
country: response.data.sys.country,
temperature: response.data.main.temp,
feelsLike: response.data.main.feels_like,
humidity: response.data.main.humidity,
pressure: response.data.main.pressure,
description: response.data.weather[0].description,
icon: response.data.weather[0].main,
windSpeed: response.data.wind.speed,
cloudiness: response.data.clouds.all
};
// Cache response
weatherCache.set(city, {
data: transformedData,
timestamp: Date.now()
});
res.json({ data: transformedData, cached: false });
} catch (error) {
handleExternalApiError(error, res);
}
};
function handleExternalApiError(error, res) {
if (error.response) {
// API returned error status
if (error.response.status === 404) {
res.status(404).json({ error: "City not found" });
} else if (error.response.status === 401) {
res.status(500).json({ error: "API authentication failed" });
} else {
res.status(error.response.status).json({ error: "External API error" });
}
} else if (error.code === "ECONNABORTED") {
res.status(504).json({ error: "External API timeout" });
} else {
res.status(500).json({ error: "Failed to fetch weather data" });
}
}// Raw API response
{
"coord": {"lon": 10.99, "lat": 44.34},
"weather": [{"id": 500, "main": "Rain", "description": "light rain"}],
"main": {"temp": 10.94, "feels_like": 10.03, "humidity": 72},
...
}
// Transform to clean response
{
"city": "Zocca",
"temperature": 10.94,
"description": "light rain",
"humidity": 72,
"windSpeed": 6.5
}
// Benefits:
// 1. Only return needed fields
// 2. Consistent naming
// 3. Easier for frontend
// 4. Hide API structure changesconst rateLimit = require("express-rate-limit");
// Rate limit external API calls
const externalApiLimiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 50, // Max 50 requests per minute
message: "Too many API requests",
keyGenerator: (req) => req.user?.id || req.ip // Per user or IP
});
app.get("/api/weather", externalApiLimiter, weatherController.getWeather);class CacheManager {
constructor(duration = 5 * 60 * 1000) {
this.cache = new Map();
this.duration = duration;
}
set(key, value) {
this.cache.set(key, {
value,
timestamp: Date.now()
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
// Check if expired
if (Date.now() - item.timestamp > this.duration) {
this.cache.delete(key);
return null;
}
return item.value;
}
clear() {
this.cache.clear();
}
}
const apiCache = new CacheManager(10 * 60 * 1000); // 10 minutes
app.get("/api/weather/:city", (req, res) => {
const { city } = req.params;
// Try cache first
const cached = apiCache.get(city);
if (cached) {
return res.json({ ...cached, cached: true });
}
// Fetch from external API
fetchWeatherFromAPI(city)
.then(data => {
apiCache.set(city, data); // Cache for next time
res.json({ ...data, cached: false });
});
});async function callExternalAPI(url, options = {}) {
const maxRetries = 3;
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await axios.get(url, {
timeout: 5000,
...options
});
return response.data;
} catch (error) {
lastError = error;
// Retry logic
if (attempt < maxRetries && isRetryable(error)) {
const delay = Math.pow(2, attempt - 1) * 1000; // Exponential backoff
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
}
function isRetryable(error) {
// Retry on network errors or 5xx
return !error.response || error.response.status >= 500;
}Installation:
npm install mongooseCreate config/db.js:
const mongoose = require("mongoose");
const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
});
console.log(`✓ MongoDB Connected: ${conn.connection.host}`);
return conn;
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
};
module.exports = connectDB;Create models/User.js:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, "Please provide a name"],
trim: true,
minlength: [2, "Name must be at least 2 characters"]
},
email: {
type: String,
required: [true, "Please provide an email"],
unique: true,
lowercase: true,
match: [
/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/,
"Please provide a valid email"
]
},
password: {
type: String,
required: [true, "Please provide a password"],
minlength: [6, "Password must be at least 6 characters"],
select: false // Don't return password by default
},
role: {
type: String,
enum: ["user", "admin"],
default: "user"
},
isActive: {
type: Boolean,
default: true
}
},
{
timestamps: true // Adds createdAt and updatedAt
}
);
// Hash password before saving
userSchema.pre("save", async function(next) {
if (!this.isModified("password")) return next();
try {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
} catch (error) {
next(error);
}
});
// Method to compare passwords
userSchema.methods.comparePassword = async function(enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
module.exports = mongoose.model("User", userSchema);const User = require("../models/User");
// GET all users with pagination
exports.getAllUsers = async (req, res, next) => {
try {
const { page = 1, limit = 10 } = req.query;
const skip = (page - 1) * limit;
const users = await User.find()
.skip(skip)
.limit(parseInt(limit))
.select("-password"); // Exclude password
const total = await User.countDocuments();
res.status(200).json({
data: users,
pagination: {
total,
page: parseInt(page),
pages: Math.ceil(total / limit)
}
});
} catch (error) {
next(error);
}
};
// GET single user
exports.getUserById = async (req, res, next) => {
try {
const { id } = req.params;
// Validate MongoDB ObjectId
if (!id.match(/^[0-9a-fA-F]{24}$/)) {
return res.status(400).json({ error: "Invalid user ID" });
}
const user = await User.findById(id).select("-password");
if (!user) {
return res.status(404).json({ error: "User not found" });
}
res.status(200).json({ data: user });
} catch (error) {
next(error);
}
};
// CREATE user
exports.createUser = async (req, res, next) => {
try {
const { name, email, password } = req.body;
// Validation
if (!name || !email || !password) {
return res.status(400).json({
error: "Name, email, and password are required"
});
}
// Check if email exists
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(409).json({ error: "Email already exists" });
}
// Create user
const user = new User({ name, email, password });
await user.save();
res.status(201).json({
message: "User created successfully",
data: {
id: user._id,
name: user.name,
email: user.email
}
});
} catch (error) {
if (error.name === "ValidationError") {
const messages = Object.values(error.errors).map(err => err.message);
return res.status(400).json({ error: messages });
}
next(error);
}
};
// UPDATE user
exports.updateUser = async (req, res, next) => {
try {
const { id } = req.params;
const { name, email } = req.body;
if (!id.match(/^[0-9a-fA-F]{24}$/)) {
return res.status(400).json({ error: "Invalid user ID" });
}
const user = await User.findByIdAndUpdate(
id,
{ name, email },
{ new: true, runValidators: true }
);
if (!user) {
return res.status(404).json({ error: "User not found" });
}
res.status(200).json({
message: "User updated successfully",
data: user
});
} catch (error) {
next(error);
}
};
// DELETE user
exports.deleteUser = async (req, res, next) => {
try {
const { id } = req.params;
if (!id.match(/^[0-9a-fA-F]{24}$/)) {
return res.status(400).json({ error: "Invalid user ID" });
}
const user = await User.findByIdAndDelete(id);
if (!user) {
return res.status(404).json({ error: "User not found" });
}
res.status(200).json({
message: "User deleted successfully",
data: user
});
} catch (error) {
next(error);
}
};Update server.js:
const express = require("express");
const dotenv = require("dotenv");
const connectDB = require("./config/db");
dotenv.config();
const app = express();
// Connect to database
connectDB();
app.use(express.json());
// Routes
app.use("/api/v1/users", require("./routes/users"));
app.listen(process.env.PORT || 3000, () => {
console.log("Server running");
});// URL-based versioning (most common)
app.use("/api/v1/users", userRoutesV1);
app.use("/api/v2/users", userRoutesV2);
// Header-based versioning
app.get("/api/users", (req, res) => {
const version = req.headers["api-version"] || "v1";
if (version === "v2") {
// Return v2 response
} else {
// Return v1 response
}
});
// Query parameter versioning
app.get("/api/users?version=v2", (req, res) => {
// Handle version
});exports.getAllUsers = async (req, res) => {
try {
const page = parseInt(req.query.page) || 1;
const limit = parseInt(req.query.limit) || 10;
const skip = (page - 1) * limit;
// Validate
if (page < 1 || limit < 1) {
return res.status(400).json({ error: "Invalid page or limit" });
}
const users = await User.find().skip(skip).limit(limit);
const total = await User.countDocuments();
res.json({
data: users,
pagination: {
page,
limit,
total,
pages: Math.ceil(total / limit),
hasNext: page * limit < total,
hasPrev: page > 1
}
});
} catch (error) {
res.status(500).json({ error: error.message });
}
};const cors = require("cors");
// Allow all origins (development only)
app.use(cors());
// Allow specific origins (production)
const whitelist = [
"http://localhost:3000",
"https://yourdomain.com",
"https://app.yourdomain.com"
];
app.use(cors({
origin: (origin, callback) => {
if (!origin || whitelist.includes(origin)) {
callback(null, true);
} else {
callback(new Error("CORS not allowed"));
}
},
credentials: true, // Allow cookies
methods: ["GET", "POST", "PUT", "DELETE", "PATCH"],
allowedHeaders: ["Content-Type", "Authorization"]
}));Create utils/logger.js:
const fs = require("fs");
const path = require("path");
const logDir = path.join(__dirname, "../logs");
// Create logs directory if it doesn't exist
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
class Logger {
formatMessage(level, message, data = {}) {
return JSON.stringify({
timestamp: new Date().toISOString(),
level,
message,
...data
});
}
info(message, data) {
const formatted = this.formatMessage("INFO", message, data);
console.log(formatted);
this.writeToFile("info.log", formatted);
}
error(message, error, data) {
const formatted = this.formatMessage("ERROR", message, {
...data,
error: error.message,
stack: error.stack
});
console.error(formatted);
this.writeToFile("error.log", formatted);
}
writeToFile(filename, message) {
const filepath = path.join(logDir, filename);
fs.appendFileSync(filepath, message + "\n");
}
}
module.exports = new Logger();Create docs/swagger.yaml:
openapi: 3.0.0
info:
title: API Integration Guide
version: 1.0.0
description: Complete REST API documentation
servers:
- url: http://localhost:3000/api/v1
description: Development server
paths:
/users:
get:
summary: Get all users
parameters:
- name: page
in: query
schema:
type: integer
default: 1
- name: limit
in: query
schema:
type: integer
default: 10
responses:
200:
description: List of users
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
post:
summary: Create new user
security:
- bearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateUserInput'
responses:
201:
description: User created
401:
description: Unauthorized
/users/{id}:
get:
summary: Get user by ID
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
200:
description: User data
404:
description: User not found
components:
schemas:
User:
type: object
properties:
id:
type: string
name:
type: string
email:
type: string
createdAt:
type: string
format: date-time
CreateUserInput:
type: object
required:
- name
- email
properties:
name:
type: string
email:
type: string
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWTInstall and use Swagger UI:
npm install swagger-ui-express swagger-jsdocconst swaggerUi = require("swagger-ui-express");
const YAML = require("yaml");
const fs = require("fs");
const swaggerDoc = YAML.parse(fs.readFileSync("./docs/swagger.yaml", "utf8"));
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerDoc));const rateLimit = require("express-rate-limit");
// Global rate limiter
const globalLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
message: "Too many requests",
standardHeaders: true,
legacyHeaders: false
});
// Strict limiter for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // Only 5 login attempts
skipSuccessfulRequests: true // Don't count successful requests
});
app.use(globalLimiter);
app.post("/api/v1/auth/login", authLimiter, authController.login);┌─────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
│ (Browser: HTML, CSS, JavaScript, React, Vue, Angular) │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Makes HTTP requests to API endpoints │ │
│ │ Displays data to user │ │
│ │ Handles user interactions │ │
│ └─────────────────────────────────────────────────────┘ │
└────────────────────────┬─────────────────────────────────────┘
│ HTTP/REST
Fetch API
XMLHttpRequest
┌────────────────────────┐
│ Internet / Network │
└────────────────────────┘
│
┌────────────────────────┴─────────────────────────────────────┐
│ SERVER LAYER │
│ (Node.js, Express, Python, Java, etc.) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Route Handler: │ │
│ │ - Parse request │ │
│ │ - Validate input │ │
│ │ - Apply middleware │ │
│ │ - Call business logic │ │
│ │ - Return response │ │
│ └──────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Business Logic Layer: │ │
│ │ - Controllers │ │
│ │ - Services │ │
│ │ - Validation │ │
│ └──────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Data Access Layer: │ │
│ │ - Models │ │
│ │ - Database queries │ │
│ │ - Data transformation │ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────────────┬─────────────────────────────────────┘
│
┌────────────────────────┴─────────────────────────────────────┐
│ DATABASE LAYER │
│ (MongoDB, PostgreSQL, MySQL, etc.) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Stores and retrieves data │ │
│ │ Enforces schema/structure │ │
│ │ Performs transactions │ │
│ └──────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
1. CLIENT INITIATES REQUEST
└─→ fetch("http://localhost:3000/api/v1/users", {
method: "POST",
headers: { "Authorization": "Bearer token" },
body: { name: "John", email: "john@example.com" }
})
2. HTTP REQUEST TRAVELS TO SERVER
└─→ POST /api/v1/users HTTP/1.1
Host: localhost:3000
Authorization: Bearer token
Content-Type: application/json
Body: { name: "John", email: "john@example.com" }
3. SERVER RECEIVES REQUEST
└─→ Express receives on port 3000
4. MIDDLEWARE PROCESSING
└─→ app.use(express.json()) → Parse JSON body
└─→ app.use(cors()) → Check CORS
└─→ app.use(authMiddleware) → Verify JWT token
5. ROUTE MATCHING
└─→ Router matches POST /api/v1/users
└─→ Calls userController.createUser
6. CONTROLLER LOGIC
└─→ Validate input data
└─→ Check if email exists
└─→ Prepare data
7. DATABASE OPERATION
└─→ User.create({ name, email })
└─→ Database stores data
└─→ Returns created document
8. RESPONSE PREPARATION
└─→ Format response
└─→ Set HTTP status 201 (Created)
└─→ Add headers
9. SERVER SENDS RESPONSE
└─→ HTTP/1.1 201 Created
Content-Type: application/json
Body: { message: "...", data: {...} }
10. FRONTEND RECEIVES RESPONSE
└─→ response.json() parses JSON
└─→ .then() receives data
└─→ Update DOM
└─→ Display to user
Total Time: Usually 50-500ms depending on:
- Network latency
- Server processing time
- Database query time
- Response size
┌─────────────────────────────────────────────────────────────┐
│ FRONTEND │
│ User Interface │
│ Input: name, email → Create User Form │
└────────────────────┬────────────────────────────────────────┘
│
│ fetch POST request
↓
┌─────────────────────────────────────────────────────────────┐
│ BACKEND SERVER │
│ │
│ 1. Route Handler (/api/v1/users) │
│ └─→ Pass to controller │
│ │
│ 2. Authentication Middleware │
│ └─→ Verify JWT token │
│ └─→ Attach user to request │
│ │
│ 3. Controller (userController.createUser) │
│ └─→ Extract name, email from request │
│ └─→ Validate data │
│ └─→ Check if email exists │
│ │
│ 4. Model/Service Layer │
│ └─→ Prepare data object │
│ └─→ Call database method │
│ │
│ 5. Error Handling Middleware │
│ └─→ Catch any errors │
│ └─→ Format error response │
└────────────────────┬────────────────────────────────────────┘
│
│ Database query
↓
┌─────────────────────────────────────────────────────────────┐
│ DATABASE (MongoDB) │
│ │
│ INSERT INTO users (name, email, createdAt) │
│ VALUES ("John", "john@example.com", NOW()) │
│ │
│ RETURNS: { _id: "...", name: "John", ... } │
└────────────────────┬────────────────────────────────────────┘
│
│ Database result
↓
┌─────────────────────────────────────────────────────────────┐
│ BACKEND SERVER │
│ │
│ 6. Format Response │
│ └─→ Status: 201 Created │
│ └─→ Body: { message: "...", data: {...} } │
│ └─→ Headers: Content-Type: application/json │
└────────────────────┬────────────────────────────────────────┘
│
│ HTTP response
↓
┌─────────────────────────────────────────────────────────────┐
│ FRONTEND │
│ │
│ 1. Receive Response │
│ └─→ response.json() │
│ │
│ 2. Update State │
│ └─→ Add new user to users array │
│ │
│ 3. Update DOM │
│ └─→ Display new user in table │
│ └─→ Show success message │
│ └─→ Clear form │
│ │
│ 4. User Sees Result │
│ └─→ New user appears on screen │
└─────────────────────────────────────────────────────────────┘
// 1. Input Validation
const validateEmail = (email) => {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return regex.test(email);
};
// 2. Sanitize Input
const sanitizeInput = (input) => {
return input.trim().replace(/[<>]/g, "");
};
// 3. Secure Password Hashing
const bcrypt = require("bcrypt");
const hashedPassword = await bcrypt.hash(password, 10);
// 4. Use HTTPS (always in production)
// 5. Secure JWT Secret
const JWT_SECRET = process.env.JWT_SECRET; // Store in env
// 6. Rate Limiting
const rateLimit = require("express-rate-limit");
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }));
// 7. CORS Whitelist
const whitelist = ["https://yourdomain.com"];
app.use(cors({ origin: whitelist }));
// 8. SQL Injection Prevention (if using SQL)
// Use parameterized queries
db.query("SELECT * FROM users WHERE id = ?", [userId]);
// 9. XSS Prevention
// Sanitize output in frontend
const sanitizeOutput = (text) => {
const div = document.createElement("div");
div.textContent = text;
return div.innerHTML;
};
// 10. CSRF Protection (if using cookies)
const csrf = require("csurf");
app.use(csrf());For Small Projects (< 5 endpoints):
app/
├── server.js
├── routes.js
└── .env
For Medium Projects (5-20 endpoints):
app/
├── server.js
├── routes/
│ ├── users.js
│ └── posts.js
├── controllers/
│ ├── userController.js
│ └── postController.js
├── middleware/
│ └── auth.js
├── models/
│ ├── User.js
│ └── Post.js
└── .env
For Large Projects (20+ endpoints):
app/
├── server.js
├── config/
│ ├── db.js
│ └── env.js
├── api/
│ ├── v1/
│ │ ├── routes/
│ │ ├── controllers/
│ │ ├── services/
│ │ └── middleware/
│ └── v2/
│ ├── routes/
│ ├── controllers/
│ └── services/
├── models/
├── utils/
├── middleware/
├── tests/
└── .env
// GOOD: Clear separation of concerns
// routes/users.js
const router = express.Router();
router.post("/", auth, validateUserInput, userController.createUser);
// controllers/userController.js
exports.createUser = async (req, res, next) => {
try {
const user = await userService.createUser(req.body);
res.status(201).json({ data: user });
} catch (error) {
next(error);
}
};
// services/userService.js
exports.createUser = async (userData) => {
validateUserData(userData);
return await User.create(userData);
};
// BAD: Everything in one place
app.post("/users", (req, res) => {
const { name, email } = req.body;
// validation, database, response all mixed
});// User Model - Handles data structure
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
}
// User Service - Handles business logic
class UserService {
create(userData) {
// Validate
// Transform
// Save to DB
}
}
// User Controller - Handles HTTP requests
class UserController {
async createUser(req, res) {
// Extract from request
// Call service
// Send response
}
}
// User Router - Handles routing
router.post("/users", userController.createUser);// config/config.js
const config = {
development: {
port: 3000,
database: "mongodb://localhost:27017/api_dev",
jwtExpire: "7d",
logLevel: "debug"
},
production: {
port: process.env.PORT,
database: process.env.DATABASE_URL,
jwtExpire: "1d",
logLevel: "error"
},
test: {
port: 3001,
database: "mongodb://localhost:27017/api_test",
jwtExpire: "1h",
logLevel: "warn"
}
};
const env = process.env.NODE_ENV || "development";
module.exports = config[env];Prerequisites:
- Node.js (v14+)
- npm or yarn
- MongoDB or PostgreSQL (for database examples)
Steps:
# 1. Clone repository
git clone https://github.com/yourusername/API-Integration.git
cd API-Integration
# 2. Install dependencies
npm install
# 3. Create .env file
cp .env.example .env
# 4. Update .env with your values
# PORT=3000
# MONGODB_URI=mongodb://localhost:27017/api_db
# JWT_SECRET=your_secret_key
# 5. Start MongoDB (if using)
mongod
# 6. Start server
npm start
# OR for development with auto-reload
npm run dev
# 7. Server running
# ✓ Server running on http://localhost:3000-
Create request in Postman:
Method: POST URL: http://localhost:3000/api/v1/users Headers: - Content-Type: application/json - Authorization: Bearer {token} Body (JSON): { "name": "John Doe", "email": "john@example.com" } -
Test flow:
- POST /api/v1/auth/login → Get token
- Copy token from response
- POST /api/v1/users with Authorization header
- Should get 201 Created response
# 1. Open frontend/index.html in browser
# OR use local server
npx http-server frontend/
# 2. Update API_BASE_URL in frontend/app.js
const API_BASE_URL = "http://localhost:3000/api/v1";
# 3. Test on http://localhost:8080{
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"test": "jest --coverage",
"lint": "eslint .",
"format": "prettier --write .",
"deploy": "git push heroku main"
}
}# 1. Create Heroku app
heroku create your-app-name
# 2. Set environment variables
heroku config:set JWT_SECRET=your_secret
heroku config:set MONGODB_URI=your_mongodb_uri
# 3. Deploy
git push heroku main
# 4. View logs
heroku logs --tailCreate Dockerfile:
FROM node:16
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
ENV PORT=3000
EXPOSE 3000
CMD ["node", "server.js"]# Build and run
docker build -t api-integration .
docker run -p 3000:3000 -e JWT_SECRET=secret api-integration# 1. SSH into EC2 instance
ssh -i key.pem ubuntu@your-instance-ip
# 2. Install Node
curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install -y nodejs
# 3. Clone and setup
git clone your-repo
cd API-Integration
npm install
# 4. Install PM2 for process management
npm install -g pm2
pm2 start server.js
pm2 save
pm2 startupThis API Integration Guide demonstrates:
✅ Backend Development
- REST API design
- Express.js framework
- CRUD operations
- Error handling
- Authentication (JWT, bcrypt)
- Database integration (MongoDB/PostgreSQL)
- Middleware implementation
✅ Frontend Development
- Fetch API
- Async/await
- DOM manipulation
- Form handling
- Loading states
- Error handling
✅ Software Architecture
- Client-server architecture
- Separation of concerns
- MVC pattern
- Design patterns
- Scalable folder structure
✅ Security
- Password hashing
- JWT authentication
- Input validation
- CORS configuration
- Rate limiting
- HTTPS best practices
✅ Database
- Data modeling
- Schema validation
- Query optimization
- Pagination
- Indexing
✅ DevOps & Deployment
- Environment configuration
- Docker containerization
- Heroku deployment
- CI/CD concepts
- Logging and monitoring
✅ Professional Development
- Clean code principles
- Error handling
- API documentation (Swagger)
- Testing (Postman)
- Version control (Git)
This comprehensive guide covers everything needed to understand, build, and deploy professional REST APIs. By following this guide, you'll develop:
- Strong Foundation - Understanding of APIs, HTTP, and web architecture
- Practical Skills - Building real production-grade APIs
- Security Awareness - Best practices for securing applications
- Problem-Solving - Handling errors, authentication, and system integration
- Professional Standards - Clean code, documentation, deployment
- Practice - Build your own API using this guide
- Expand - Add more features (caching, websockets, queuing)
- Learn - Explore GraphQL, microservices, testing frameworks
- Deploy - Put your API live using Heroku, AWS, or DigitalOcean
- Contribute - Share your learning with the community
- Node.js Docs: https://nodejs.org/docs/
- Express.js Guide: https://expressjs.com/
- MongoDB Documentation: https://docs.mongodb.com/
- JWT.io: https://jwt.io/
- REST API Design: https://restfulapi.net/
-
Clone the repository
git clone <your-repo-url> cd API-Integration
-
Read the code
- Start with
server.jsto understand structure - Review controllers for business logic
- Check middleware for security implementation
- Examine models for data structure
- Start with
-
Run the application
- Follow "How to Run Locally" section
- Test endpoints using provided frontend
- Review console logs for debugging
-
Assess your skills
- Code organization and structure
- Error handling implementation
- Security practices
- Documentation quality
- Comments explaining complex logic
This project demonstrates:
- ✅ Full stack capabilities
- ✅ Professional code quality
- ✅ Security awareness
- ✅ Ability to build scalable systems
- ✅ Communication through documentation
- ✅ Best practices implementation
Last Updated: February 2024 Version: 1.0.0 License: MIT
Made with ❤️ for developers learning API integration