ZapLabs Logo
Backend Engineering

How to Test Node.js APIs Using Insomnia

Pavithra SandaminiOctober 10, 202511 min read
How to Test Node.js APIs Using Insomnia

Build a production-ready Node.js API with Express and learn how to test every endpoint in Insomnia while applying best practices for automation and collaboration.

Testing APIs is a crucial part of modern web development. In this guide, you’ll build a robust Node.js API and use Insomnia to validate every key scenario—from happy paths to edge cases—so your service stays reliable in production.

What you’ll learn

  • Set up a production-ready Node.js API with Express
  • Implement structured error handling and validation
  • Test all CRUD operations using Insomnia
  • Organize and automate your API tests
  • Follow best practices for API development and quality

Prerequisites

Before diving in, make sure you have:

  • Node.js (v14 or higher) installed
  • A basic understanding of JavaScript and REST APIs
  • A code editor (VS Code recommended)

Part 1: Build your Node.js API

1.1 Project setup

Start by creating a clean project structure and installing dependencies:

mkdir user-management-api
cd user-management-api
npm init -y
npm install express
npm install --save-dev nodemon

nodemon restarts the server automatically whenever you save changes, which speeds up local development.

user-management-api/
├── index.js
├── package.json
└── README.md

1.3 Configure package.json

Update the scripts section so you can run the API in both production and development modes:

{
  "name": "user-management-api",
  "version": "1.0.0",
  "description": "Simple REST API for user management",
  "main": "index.js",
  "scripts": {
    "start": "node index.js",
    "dev": "nodemon index.js"
  },
  "keywords": ["api", "express", "rest"],
  "author": "Your Name",
  "license": "MIT"
}

1.4 Build the Express API

Create index.js with a fully working set of CRUD endpoints, validation, and production-ready error handling:

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// In-memory database (replace with a real database in production)
let users = [
  { id: 1, name: 'John Doe', email: '[email protected]', role: 'admin' },
  { id: 2, name: 'Jane Smith', email: '[email protected]', role: 'user' }
];

// Helper function to generate new IDs
const generateId = () => {
  return users.length > 0 ? Math.max(...users.map((u) => u.id)) + 1 : 1;
};

// Validation middleware
const validateUser = (req, res, next) => {
  const { name, email } = req.body;

  if (!name || name.trim().length === 0) {
    return res.status(400).json({
      error: 'Validation failed',
      message: 'Name is required and cannot be empty'
    });
  }

  if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    return res.status(400).json({
      error: 'Validation failed',
      message: 'Invalid email format'
    });
  }

  next();
};

// Routes
app.get('/health', (req, res) => {
  res.json({ status: 'OK', timestamp: new Date().toISOString() });
});

app.get('/api/users', (req, res) => {
  res.json({
    success: true,
    count: users.length,
    data: users
  });
});

app.get('/api/users/:id', (req, res) => {
  const userId = parseInt(req.params.id, 10);
  const user = users.find((u) => u.id === userId);

  if (!user) {
    return res.status(404).json({
      success: false,
      error: 'User not found',
      message: `No user found with ID: ${userId}`
    });
  }

  res.json({
    success: true,
    data: user
  });
});

app.post('/api/users', validateUser, (req, res) => {
  const { name, email, role } = req.body;

  if (email && users.some((u) => u.email === email)) {
    return res.status(409).json({
      success: false,
      error: 'Conflict',
      message: 'User with this email already exists'
    });
  }

  const newUser = {
    id: generateId(),
    name: name.trim(),
    email: email || null,
    role: role || 'user'
  };

  users.push(newUser);

  res.status(201).json({
    success: true,
    message: 'User created successfully',
    data: newUser
  });
});

app.put('/api/users/:id', validateUser, (req, res) => {
  const userId = parseInt(req.params.id, 10);
  const user = users.find((u) => u.id === userId);

  if (!user) {
    return res.status(404).json({
      success: false,
      error: 'User not found',
      message: `No user found with ID: ${userId}`
    });
  }

  const { name, email, role } = req.body;

  if (email && email !== user.email && users.some((u) => u.email === email)) {
    return res.status(409).json({
      success: false,
      error: 'Conflict',
      message: 'Another user with this email already exists'
    });
  }

  user.name = name.trim();
  if (email) user.email = email;
  if (role) user.role = role;

  res.json({
    success: true,
    message: 'User updated successfully',
    data: user
  });
});

app.patch('/api/users/:id', (req, res) => {
  const userId = parseInt(req.params.id, 10);
  const user = users.find((u) => u.id === userId);

  if (!user) {
    return res.status(404).json({
      success: false,
      error: 'User not found',
      message: `No user found with ID: ${userId}`
    });
  }

  const { name, email, role } = req.body;

  if (name !== undefined) user.name = name.trim();
  if (email !== undefined) user.email = email;
  if (role !== undefined) user.role = role;

  res.json({
    success: true,
    message: 'User updated successfully',
    data: user
  });
});

app.delete('/api/users/:id', (req, res) => {
  const userId = parseInt(req.params.id, 10);
  const userIndex = users.findIndex((u) => u.id === userId);

  if (userIndex === -1) {
    return res.status(404).json({
      success: false,
      error: 'User not found',
      message: `No user found with ID: ${userId}`
    });
  }

  users.splice(userIndex, 1);
  res.status(204).send();
});

app.use((req, res) => {
  res.status(404).json({
    success: false,
    error: 'Not Found',
    message: `Cannot ${req.method} ${req.path}`
  });
});

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({
    success: false,
    error: 'Internal Server Error',
    message: 'Something went wrong on the server'
  });
});

app.listen(PORT, () => {
  console.log(`🚀 Server running on http://localhost:${PORT}`);
  console.log(`📝 API Documentation: http://localhost:${PORT}/api/users`);
});

1.5 Start the server

Run the development server and confirm it starts without errors:

npm run dev

Expected terminal output:

🚀 Server running on http://localhost:3000
📝 API Documentation: http://localhost:3000/api/users

Part 2: Set up Insomnia

2.1 Download and install

  1. Visit insomnia.rest.
  2. Download the installer for your operating system.
  3. Follow the prompts and launch Insomnia.

2.2 Create your first collection

  1. Open Insomnia and click Create → Request Collection.
  2. Name it User Management API.
  3. Click Create to save the collection.

Part 3: Test your API endpoints

3.1 Health check

  • Create a request named Health Check.
  • Method: GET
  • URL: http://localhost:3000/health
{
  "status": "OK",
  "timestamp": "2025-10-11T10:30:00.000Z"
}

3.2 Get all users

  • Request name: Get All Users
  • Method: GET
  • URL: http://localhost:3000/api/users
{
  "success": true,
  "count": 2,
  "data": [
    {
      "id": 1,
      "name": "John Doe",
      "email": "[email protected]",
      "role": "admin"
    },
    {
      "id": 2,
      "name": "Jane Smith",
      "email": "[email protected]",
      "role": "user"
    }
  ]
}

3.3 Get a single user

  • Request name: Get User by ID
  • Method: GET
  • URL: http://localhost:3000/api/users/1
{
  "success": true,
  "data": {
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]",
    "role": "admin"
  }
}

3.4 Create a user

  • Request name: Create User
  • Method: POST
  • URL: http://localhost:3000/api/users
  • Body (JSON):
{
  "name": "Alice Johnson",
  "email": "[email protected]",
  "role": "user"
}

Expected response (201 Created):

{
  "success": true,
  "message": "User created successfully",
  "data": {
    "id": 3,
    "name": "Alice Johnson",
    "email": "[email protected]",
    "role": "user"
  }
}

3.5 Update a user

  • Request name: Update User
  • Method: PUT
  • URL: http://localhost:3000/api/users/3
  • Body (JSON):
{
  "name": "Alice Williams",
  "email": "[email protected]",
  "role": "admin"
}
{
  "success": true,
  "message": "User updated successfully",
  "data": {
    "id": 3,
    "name": "Alice Williams",
    "email": "[email protected]",
    "role": "admin"
  }
}

3.6 Partial update

  • Request name: Partial Update User
  • Method: PATCH
  • URL: http://localhost:3000/api/users/3
  • Body (JSON):
{
  "role": "moderator"
}

This updates only the role field and leaves other attributes untouched.

3.7 Delete a user

  • Request name: Delete User
  • Method: DELETE
  • URL: http://localhost:3000/api/users/3

Expect a 204 No Content response with an empty body.

3.8 Exercise error scenarios

  • 404 — User Not Found
    Method: GET
    URL: http://localhost:3000/api/users/999
{
  "success": false,
  "error": "User not found",
  "message": "No user found with ID: 999"
}
  • 400 — Validation Error
    Method: POST
    URL: http://localhost:3000/api/users
{
  "name": "",
  "email": "invalid-email"
}

Expected response:

{
  "error": "Validation failed",
  "message": "Name is required and cannot be empty"
}
  • 409 — Duplicate Email
    Method: POST
    URL: http://localhost:3000/api/users
{
  "name": "Duplicate User",
  "email": "[email protected]"
}
{
  "success": false,
  "error": "Conflict",
  "message": "User with this email already exists"
}

Part 4: Advanced Insomnia features

4.1 Organize requests with folders

  1. Right-click the collection and select New Folder.
  2. Create folders such as User Operations, Error Testing, and Authentication.
  3. Drag and drop requests into the relevant folders to keep things tidy.

4.2 Use environment variables

  1. Click the environment dropdown and select Manage Environments.
  2. Create an environment named Development with values:
{
  "base_url": "http://localhost:3000",
  "api_version": "api"
}
  1. Update requests to use {{ _.base_url }}/{{ _.api_version }}/users so you can switch environments with one change.

4.3 Chain requests

Leverage one response inside another request:

  1. Send a POST request to create a user.
  2. Reference the returned ID in another request using {{ _.user_id }}.
  3. Right-click the field → Response → Body Attribute → select the previous response.

4.4 Export and share collections

Right-click the collection, choose Export, select Insomnia v4 (JSON), and share the file with teammates so everyone uses the same test suite.

4.5 Add automated tests (Insomnia Pro)

If you have Insomnia Pro, add lightweight JavaScript assertions:

const response = await insomnia.send();
expect(response.status).to.equal(200);
expect(response.data).to.have.property('success', true);
expect(response.data.data).to.be.an('array');

Part 5: Best practices

API development

  • Use accurate HTTP status codes such as 200, 201, 204, 400, 404, and 500.
  • Keep response payloads consistent so clients can parse them reliably.
  • Validate user input on every write endpoint.
  • Introduce API versioning (e.g., /api/v1/) to future-proof changes.
  • Handle errors gracefully and return actionable messages.
  • Add structured logging with libraries such as winston or morgan.

Testing strategy

  • Cover every endpoint, not just the happy path.
  • Stress-test edge cases like empty strings, invalid formats, and large payloads.
  • Validate negative scenarios: missing fields, invalid IDs, duplicate records.
  • Group related requests into folders for clarity.
  • Use environment variables to swap between dev, staging, and production.
  • Document each request so teammates know the intent.
  • Save frequently used requests as templates for new projects.

Part 6: Next steps

Enhance your API

  • Replace the in-memory store with MongoDB, PostgreSQL, or MySQL.
  • Implement authentication with JWT or OAuth.
  • Add rate limiting to prevent abuse.
  • Introduce pagination, filtering, and sorting for large datasets.
  • Write automated tests with Jest or Mocha.
  • Generate API documentation using Swagger/OpenAPI.
  • Deploy to platforms like Heroku, Railway, or Vercel.

Explore alternative tools

  • Postman for team collaboration features.
  • Thunder Client as a lightweight VS Code extension.
  • REST Client to run .http files directly in VS Code.
  • cURL for quick command-line checks.
  • HTTPie for a friendlier CLI experience.

Conclusion

You now have a hardened Node.js API and a fully exercised Insomnia collection. With consistent testing, thoughtful organization, and automation in place, you can deliver reliable APIs that stay maintainable as your product grows.

Resources

Related articles

Explore companion reads to keep momentum on your product roadmap.

View all posts
What Is Alpha Testing?
Quality EngineeringOct 26, 2025

What Is Alpha Testing?

ZapLabs Editorial Team6 min
Read article
Performance Testing
Quality EngineeringOct 24, 2025

Performance Testing

Pavithra Sandamini7 min
Read article
Acceptance Test-Driven Development
Quality EngineeringOct 20, 2025

Acceptance Test-Driven Development

ZapLabs Editorial Team7 min
Read article

Bring your next release to market faster

Partner with ZapLabs to align product strategy, design, and engineering around outcomes that matter.