Node.js Examples
This page provides comprehensive Node.js examples for interacting with the JSONPlaceholder API using modern JavaScript features.
Setup
First, install the required dependencies:
bash
npm install axios
# or
pnpm add axios
Basic Setup
Choose the appropriate base URL for your use case:
javascript
const axios = require('axios');
// or with ES modules:
// import axios from 'axios';
const BASE_URL = 'https://api.jsonplaceholder.dev';
class JSONPlaceholderAPI {
constructor(baseURL = BASE_URL) {
this.api = axios.create({
baseURL,
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'JSONPlaceholder-NodeJS-Client/1.0'
}
});
// Response interceptor
this.api.interceptors.response.use(
(response) => response.data,
(error) => {
console.error('API Error:', error.message);
throw error;
}
);
}
async get(endpoint, params = {}) {
return this.api.get(endpoint, { params });
}
async post(endpoint, data) {
return this.api.post(endpoint, data);
}
async put(endpoint, data) {
return this.api.put(endpoint, data);
}
async patch(endpoint, data) {
return this.api.patch(endpoint, data);
}
async delete(endpoint) {
return this.api.delete(endpoint);
}
}
// Initialize API client
const api = new JSONPlaceholderAPI();
module.exports = { api, JSONPlaceholderAPI };
javascript
const axios = require('axios');
// or with ES modules:
// import axios from 'axios';
const BASE_URL = 'http://localhost:3000';
class JSONPlaceholderAPI {
constructor(baseURL = BASE_URL) {
this.api = axios.create({
baseURL,
timeout: 5000,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'JSONPlaceholder-NodeJS-Client/1.0'
}
});
// Response interceptor
this.api.interceptors.response.use(
(response) => response.data,
(error) => {
console.error('API Error:', error.message);
throw error;
}
);
}
async get(endpoint, params = {}) {
return this.api.get(endpoint, { params });
}
async post(endpoint, data) {
return this.api.post(endpoint, data);
}
async put(endpoint, data) {
return this.api.put(endpoint, data);
}
async patch(endpoint, data) {
return this.api.patch(endpoint, data);
}
async delete(endpoint) {
return this.api.delete(endpoint);
}
}
// Initialize API client
const api = new JSONPlaceholderAPI();
module.exports = { api, JSONPlaceholderAPI };
Users Examples
Basic User Operations
javascript
// Get all users
async function getAllUsers() {
try {
const users = await api.get('/users');
console.log(`Found ${users.length} users`);
return users;
} catch (error) {
console.error('Error fetching users:', error.message);
return [];
}
}
// Get single user
async function getUserById(userId) {
try {
const user = await api.get(`/users/${userId}`);
console.log(`User: ${user.name} (${user.email})`);
return user;
} catch (error) {
console.error(`Error fetching user ${userId}:`, error.message);
return null;
}
}
// Create new user
async function createUser(userData) {
const defaultData = {
address: {
street: '123 Main St',
city: 'New York',
zipcode: '10001'
},
phone: '1-555-123-4567',
website: 'example.com'
};
const user = { ...defaultData, ...userData };
try {
const newUser = await api.post('/users', user);
console.log(`Created user: ${newUser.name} (ID: ${newUser.id})`);
return newUser;
} catch (error) {
console.error('Error creating user:', error.message);
return null;
}
}
// Usage examples
async function userExamples() {
const users = await getAllUsers();
const user = await getUserById(1);
const newUser = await createUser({
name: 'John Doe',
username: 'johndoe',
email: '[email protected]'
});
}
Advanced User Operations
javascript
// Update user with partial data
async function updateUser(userId, updates) {
try {
const updatedUser = await api.patch(`/users/${userId}`, updates);
console.log(`Updated user ${userId}`);
return updatedUser;
} catch (error) {
console.error(`Error updating user ${userId}:`, error.message);
return null;
}
}
// Get comprehensive user statistics
async function getUserStats(userId) {
try {
const [user, posts, albums, todos] = await Promise.all([
api.get(`/users/${userId}`),
api.get(`/users/${userId}/posts`),
api.get(`/users/${userId}/albums`),
api.get(`/users/${userId}/todos`)
]);
const completedTodos = todos.filter(todo => todo.completed);
const completionRate = todos.length > 0 ? (completedTodos.length / todos.length) * 100 : 0;
const stats = {
user,
postsCount: posts.length,
albumsCount: albums.length,
todosCount: todos.length,
completedTodos: completedTodos.length,
completionRate
};
console.log(`Stats for ${user.name}:`);
console.log(` Posts: ${stats.postsCount}`);
console.log(` Albums: ${stats.albumsCount}`);
console.log(` Todos: ${stats.todosCount} (${stats.completionRate.toFixed(1)}% complete)`);
return stats;
} catch (error) {
console.error(`Error fetching stats for user ${userId}:`, error.message);
return null;
}
}
// Batch operations
async function batchCreateUsers(usersData) {
const results = await Promise.allSettled(
usersData.map(userData => createUser(userData))
);
const successful = results.filter(result => result.status === 'fulfilled');
const failed = results.filter(result => result.status === 'rejected');
console.log(`Successfully created ${successful.length} users`);
console.log(`Failed to create ${failed.length} users`);
return {
successful: successful.map(result => result.value),
failed: failed.map(result => result.reason)
};
}
// Usage
async function advancedUserExamples() {
await updateUser(1, { email: '[email protected]' });
const stats = await getUserStats(1);
const batchUsers = [
{ name: 'Alice Smith', username: 'alice', email: '[email protected]' },
{ name: 'Bob Johnson', username: 'bob', email: '[email protected]' }
];
const batchResults = await batchCreateUsers(batchUsers);
}
Posts Examples
Basic Post Operations
javascript
// Get all posts
async function getAllPosts() {
return api.get('/posts');
}
// Get posts by user
async function getPostsByUser(userId) {
return api.get('/posts', { userId });
}
// Get single post
async function getPostById(postId) {
return api.get(`/posts/${postId}`);
}
// Create new post
async function createPost(userId, title, body) {
const postData = { userId, title, body };
return api.post('/posts', postData);
}
// Update post
async function updatePost(postId, updates) {
return api.patch(`/posts/${postId}`, updates);
}
// Delete post
async function deletePost(postId) {
return api.delete(`/posts/${postId}`);
}
// Get post with comments
async function getPostWithComments(postId) {
try {
const [post, comments] = await Promise.all([
api.get(`/posts/${postId}`),
api.get(`/posts/${postId}/comments`)
]);
return { ...post, comments };
} catch (error) {
console.error(`Error fetching post ${postId} with comments:`, error.message);
return null;
}
}
Advanced Post Operations
javascript
// Search posts by content
function searchPosts(posts, searchTerm) {
const term = searchTerm.toLowerCase();
return posts.filter(post =>
post.title.toLowerCase().includes(term) ||
post.body.toLowerCase().includes(term)
);
}
// Get popular posts (by comment count)
async function getPopularPosts(minComments = 3) {
try {
const posts = await getAllPosts();
const popularPosts = [];
// Use Promise.all for concurrent requests
const postsWithComments = await Promise.all(
posts.map(async (post) => {
const comments = await api.get(`/posts/${post.id}/comments`);
return { ...post, commentCount: comments.length };
})
);
return postsWithComments
.filter(post => post.commentCount >= minComments)
.sort((a, b) => b.commentCount - a.commentCount);
} catch (error) {
console.error('Error fetching popular posts:', error.message);
return [];
}
}
// Bulk operations with rate limiting
async function bulkCreatePosts(postsData, concurrency = 3) {
const results = [];
for (let i = 0; i < postsData.length; i += concurrency) {
const batch = postsData.slice(i, i + concurrency);
const batchResults = await Promise.allSettled(
batch.map(postData => createPost(postData.userId, postData.title, postData.body))
);
results.push(...batchResults);
// Add delay between batches to avoid overwhelming the server
if (i + concurrency < postsData.length) {
await new Promise(resolve => setTimeout(resolve, 100));
}
}
return results;
}
// Post analytics
async function analyzeUserPosts(userId) {
try {
const posts = await getPostsByUser(userId);
if (posts.length === 0) {
return { message: 'No posts found for this user' };
}
const analysis = {
totalPosts: posts.length,
averageTitleLength: posts.reduce((sum, post) => sum + post.title.length, 0) / posts.length,
averageBodyLength: posts.reduce((sum, post) => sum + post.body.length, 0) / posts.length,
longestPost: posts.reduce((longest, post) =>
post.body.length > longest.body.length ? post : longest
),
shortestPost: posts.reduce((shortest, post) =>
post.body.length < shortest.body.length ? post : shortest
)
};
console.log(`Post analysis for user ${userId}:`);
console.log(`Total posts: ${analysis.totalPosts}`);
console.log(`Average title length: ${analysis.averageTitleLength.toFixed(1)} chars`);
console.log(`Average body length: ${analysis.averageBodyLength.toFixed(1)} chars`);
return analysis;
} catch (error) {
console.error(`Error analyzing posts for user ${userId}:`, error.message);
return null;
}
}
Comments Examples
javascript
// Get all comments
async function getAllComments() {
return api.get('/comments');
}
// Get comments by post
async function getCommentsByPost(postId) {
return api.get('/comments', { postId });
}
// Create new comment
async function createComment(postId, name, email, body) {
const commentData = { postId, name, email, body };
return api.post('/comments', commentData);
}
// Comment moderation
async function moderateComments(keywords = ['spam', 'inappropriate']) {
try {
const comments = await getAllComments();
const flaggedComments = [];
comments.forEach(comment => {
const body = comment.body.toLowerCase();
const flaggedFor = keywords.find(keyword =>
body.includes(keyword.toLowerCase())
);
if (flaggedFor) {
flaggedComments.push({
comment,
flaggedFor,
severity: keywords.indexOf(flaggedFor) + 1
});
}
});
return flaggedComments.sort((a, b) => b.severity - a.severity);
} catch (error) {
console.error('Error moderating comments:', error.message);
return [];
}
}
// Comment analytics
async function getCommentAnalytics() {
try {
const comments = await getAllComments();
const analytics = {
totalComments: comments.length,
averageLength: comments.reduce((sum, c) => sum + c.body.length, 0) / comments.length,
commenterEmails: [...new Set(comments.map(c => c.email))].length,
mostActiveCommenter: getMostActiveCommenter(comments)
};
return analytics;
} catch (error) {
console.error('Error getting comment analytics:', error.message);
return null;
}
}
function getMostActiveCommenter(comments) {
const emailCounts = comments.reduce((counts, comment) => {
counts[comment.email] = (counts[comment.email] || 0) + 1;
return counts;
}, {});
const mostActive = Object.entries(emailCounts)
.reduce((max, [email, count]) =>
count > max.count ? { email, count } : max,
{ email: '', count: 0 }
);
return mostActive;
}
Advanced Examples
Real-time Data Synchronization
javascript
class DataSynchronizer {
constructor(api, interval = 30000) {
this.api = api;
this.interval = interval;
this.cache = new Map();
this.subscribers = new Map();
this.syncInterval = null;
}
// Subscribe to data changes
subscribe(resource, callback) {
if (!this.subscribers.has(resource)) {
this.subscribers.set(resource, new Set());
}
this.subscribers.get(resource).add(callback);
// Start syncing if not already started
if (!this.syncInterval) {
this.startSync();
}
}
// Unsubscribe from data changes
unsubscribe(resource, callback) {
const callbacks = this.subscribers.get(resource);
if (callbacks) {
callbacks.delete(callback);
if (callbacks.size === 0) {
this.subscribers.delete(resource);
}
}
// Stop syncing if no more subscribers
if (this.subscribers.size === 0 && this.syncInterval) {
clearInterval(this.syncInterval);
this.syncInterval = null;
}
}
// Start periodic synchronization
startSync() {
this.syncInterval = setInterval(async () => {
for (const resource of this.subscribers.keys()) {
await this.syncResource(resource);
}
}, this.interval);
}
// Sync specific resource
async syncResource(resource) {
try {
const newData = await this.api.get(resource);
const oldData = this.cache.get(resource);
if (JSON.stringify(newData) !== JSON.stringify(oldData)) {
this.cache.set(resource, newData);
this.notifySubscribers(resource, newData, oldData);
}
} catch (error) {
console.error(`Error syncing ${resource}:`, error.message);
}
}
// Notify subscribers of changes
notifySubscribers(resource, newData, oldData) {
const callbacks = this.subscribers.get(resource);
if (callbacks) {
callbacks.forEach(callback => {
try {
callback(newData, oldData);
} catch (error) {
console.error('Error in subscriber callback:', error);
}
});
}
}
}
// Usage example
const synchronizer = new DataSynchronizer(api, 10000);
synchronizer.subscribe('/users', (newUsers, oldUsers) => {
console.log('Users updated:', newUsers.length);
});
synchronizer.subscribe('/posts', (newPosts, oldPosts) => {
console.log('Posts updated:', newPosts.length);
});
Caching with TTL
javascript
class APICache {
constructor(ttl = 300000) { // 5 minutes default TTL
this.cache = new Map();
this.ttl = ttl;
}
set(key, data) {
const expiresAt = Date.now() + this.ttl;
this.cache.set(key, { data, expiresAt });
}
get(key) {
const cached = this.cache.get(key);
if (!cached) return null;
if (Date.now() > cached.expiresAt) {
this.cache.delete(key);
return null;
}
return cached.data;
}
clear() {
this.cache.clear();
}
size() {
return this.cache.size;
}
}
class CachedAPIClient extends JSONPlaceholderAPI {
constructor(baseURL, cacheTTL = 300000) {
super(baseURL);
this.cache = new APICache(cacheTTL);
}
async get(endpoint, params = {}) {
const cacheKey = `${endpoint}:${JSON.stringify(params)}`;
const cached = this.cache.get(cacheKey);
if (cached) {
console.log(`Cache hit for ${endpoint}`);
return cached;
}
const data = await super.get(endpoint, params);
this.cache.set(cacheKey, data);
console.log(`Cache miss for ${endpoint}, data cached`);
return data;
}
}
// Usage
const cachedAPI = new CachedAPIClient(BASE_URL, 300000); // 5 minute cache
Performance Monitoring
javascript
class PerformanceMonitor {
constructor() {
this.metrics = [];
}
async measureRequest(requestFn, label) {
const start = process.hrtime.bigint();
let error = null;
let result = null;
try {
result = await requestFn();
} catch (err) {
error = err;
}
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1000000; // Convert to milliseconds
const metric = {
label,
duration,
timestamp: new Date(),
success: !error,
error: error?.message
};
this.metrics.push(metric);
return { result, metric };
}
getStats() {
const successful = this.metrics.filter(m => m.success);
const failed = this.metrics.filter(m => !m.success);
return {
total: this.metrics.length,
successful: successful.length,
failed: failed.length,
averageDuration: successful.reduce((sum, m) => sum + m.duration, 0) / successful.length || 0,
minDuration: Math.min(...successful.map(m => m.duration)),
maxDuration: Math.max(...successful.map(m => m.duration))
};
}
reset() {
this.metrics = [];
}
}
// Usage example
const monitor = new PerformanceMonitor();
async function benchmarkAPI() {
const tests = [
() => api.get('/users'),
() => api.get('/posts'),
() => api.get('/comments'),
() => api.get('/users/1'),
() => api.get('/posts/1/comments')
];
for (let i = 0; i < tests.length; i++) {
const { metric } = await monitor.measureRequest(tests[i], `Test ${i + 1}`);
console.log(`${metric.label}: ${metric.duration.toFixed(2)}ms - ${metric.success ? 'Success' : 'Failed'}`);
}
const stats = monitor.getStats();
console.log('\nBenchmark Results:');
console.log(`Total requests: ${stats.total}`);
console.log(`Success rate: ${(stats.successful / stats.total * 100).toFixed(1)}%`);
console.log(`Average duration: ${stats.averageDuration.toFixed(2)}ms`);
console.log(`Min/Max duration: ${stats.minDuration.toFixed(2)}ms / ${stats.maxDuration.toFixed(2)}ms`);
}
// Run benchmark
benchmarkAPI().catch(console.error);
Error Handling and Retry Logic
javascript
class RetryableAPI extends JSONPlaceholderAPI {
constructor(baseURL, maxRetries = 3, retryDelay = 1000) {
super(baseURL);
this.maxRetries = maxRetries;
this.retryDelay = retryDelay;
}
async makeRequestWithRetry(requestFn, retries = 0) {
try {
return await requestFn();
} catch (error) {
if (retries < this.maxRetries && this.isRetryableError(error)) {
console.log(`Request failed, retrying... (${retries + 1}/${this.maxRetries})`);
await this.delay(this.retryDelay * Math.pow(2, retries)); // Exponential backoff
return this.makeRequestWithRetry(requestFn, retries + 1);
}
throw error;
}
}
isRetryableError(error) {
const retryableCodes = ['ENOTFOUND', 'ECONNRESET', 'ETIMEDOUT'];
return retryableCodes.includes(error.code) ||
(error.response && error.response.status >= 500);
}
delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async get(endpoint, params = {}) {
return this.makeRequestWithRetry(() => super.get(endpoint, params));
}
async post(endpoint, data) {
return this.makeRequestWithRetry(() => super.post(endpoint, data));
}
}
// Usage
const retryableAPI = new RetryableAPI(BASE_URL, 3, 1000);
Testing Examples
Unit Testing with Jest
javascript
// tests/api.test.js
const { JSONPlaceholderAPI } = require('../api');
describe('JSONPlaceholder API', () => {
let api;
beforeEach(() => {
api = new JSONPlaceholderAPI('http://localhost:3000');
});
describe('Users', () => {
test('should fetch all users', async () => {
const users = await api.get('/users');
expect(Array.isArray(users)).toBe(true);
expect(users.length).toBeGreaterThan(0);
expect(users[0]).toHaveProperty('id');
expect(users[0]).toHaveProperty('name');
expect(users[0]).toHaveProperty('email');
});
test('should fetch single user by id', async () => {
const user = await api.get('/users/1');
expect(user).toHaveProperty('id', 1);
expect(user).toHaveProperty('name');
expect(user).toHaveProperty('email');
});
test('should create new user', async () => {
const userData = {
name: 'Test User',
username: 'testuser',
email: '[email protected]'
};
const newUser = await api.post('/users', userData);
expect(newUser).toHaveProperty('id');
expect(newUser.name).toBe(userData.name);
expect(newUser.email).toBe(userData.email);
});
});
describe('Posts', () => {
test('should fetch all posts', async () => {
const posts = await api.get('/posts');
expect(Array.isArray(posts)).toBe(true);
expect(posts.length).toBeGreaterThan(0);
});
test('should create new post', async () => {
const postData = {
userId: 1,
title: 'Test Post',
body: 'This is a test post'
};
const newPost = await api.post('/posts', postData);
expect(newPost).toHaveProperty('id');
expect(newPost.title).toBe(postData.title);
});
});
});
Best Practices
- Error Handling: Always wrap API calls in try-catch blocks
- Rate Limiting: Implement delays between bulk operations
- Caching: Cache frequently accessed data to reduce API calls
- Retries: Implement retry logic for transient failures
- Monitoring: Track API performance and error rates
- Type Safety: Consider using TypeScript for better type safety
- Connection Pooling: Use HTTP agents for connection reuse
- Timeout Handling: Set appropriate timeouts for requests