Skip to content

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

  1. Error Handling: Always wrap API calls in try-catch blocks
  2. Rate Limiting: Implement delays between bulk operations
  3. Caching: Cache frequently accessed data to reduce API calls
  4. Retries: Implement retry logic for transient failures
  5. Monitoring: Track API performance and error rates
  6. Type Safety: Consider using TypeScript for better type safety
  7. Connection Pooling: Use HTTP agents for connection reuse
  8. Timeout Handling: Set appropriate timeouts for requests

Fast Fake REST API powered by Bun + Elysia.js | Documentation site source: github.com/ckissi/jsonplaceholder