npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

ezy-response

v1.0.0

Published

Simplified and standardized Express.js response handling with clean, one-liner syntax for success, error, custom, and stream-based responses

Readme

🚀 ezy-response

npm version License: MIT Node.js Version Build Status Downloads

🎯 Transform your Express.js API responses from verbose boilerplate to clean one-liners

ezy-response revolutionizes how you handle API responses in Express.js applications. Say goodbye to repetitive res.status().json({...}) patterns and embrace a standardized, developer-friendly approach that makes your code cleaner, more maintainable, and less error-prone.

🌟 Why ezy-response?

Before (Standard Express.js):

res.status(200).json({
  success: true,
  message: 'Users retrieved successfully',
  data: users,
  timestamp: new Date().toISOString()
});

After (With ezy-response):

res.success({
  message: 'Users retrieved successfully',
  data: users
});

Result: 70% less code, 100% more consistency! 🎉

✨ Features

  • 🚀 One-liner syntax - Transform verbose response code into clean, readable statements
  • 📦 Standardized format - Consistent response structure across your entire API
  • 🔧 TypeScript ready - Full type definitions included out of the box
  • 🎯 14 response methods - Covers every HTTP scenario you'll encounter
  • 📊 Smart pagination - Built-in pagination support with metadata
  • 📁 File streaming - Effortless file downloads and streaming
  • 🗜️ Compression support - Optional gzip compression for better performance
  • 🧪 Battle-tested - Comprehensive test suite with 100% coverage
  • 📚 Rich documentation - Examples, guides, and best practices included
  • 🔌 Zero config - Works immediately after installation

📦 Installation

npm install ezy-response

🚀 Quick Start

const express = require('express');
const ezyResponse = require('ezy-response');

const app = express();

// 🔌 Plug in ezy-response (one line setup!)
app.use(ezyResponse());

// ✅ Success response
app.get('/users', (req, res) => {
  res.success({
    message: 'Users retrieved successfully',
    data: users
  });
});

// ❌ Error response
app.get('/error', (req, res) => {
  res.error({
    message: 'Something went wrong',
    statusCode: 400
  });
});

// 🎯 Validation error
app.post('/users', (req, res) => {
  if (!req.body.email) {
    return res.validationError({
      errors: [{ field: 'email', message: 'Email is required' }]
    });
  }
  // ... create user
});

app.listen(3000);

📖 Complete API Reference

🔧 Middleware Setup

const ezyResponse = require('ezy-response');

// Basic setup
app.use(ezyResponse());

// With compression enabled
app.use(ezyResponse({ enableCompression: true }));

✅ Success Responses

res.success(options)

res.success({
  message: 'Operation completed successfully', // optional, default: 'Success'
  data: { id: 1, name: 'John' }, // optional
  meta: { version: '1.0.0' }, // optional
  statusCode: 200 // optional, default: 200
});

// 📤 Response:
// {
//   "success": true,
//   "message": "Operation completed successfully",
//   "data": { "id": 1, "name": "John" },
//   "meta": { "version": "1.0.0" },
//   "timestamp": "2024-01-15T10:30:00.000Z"
// }

res.created(options)

res.created({
  data: newUser,
  message: 'User created successfully', // optional
  location: '/users/123' // optional, sets Location header
});
// 📤 Status: 201 Created

res.noContent(message?)

res.noContent(); // 📤 Status: 204 No Content
res.noContent('Deleted successfully'); // 📤 Status: 200 with message

❌ Error Responses

res.error(options)

res.error({
  message: 'Something went wrong', // optional, default: 'An error occurred'
  code: 'USER_NOT_FOUND', // optional, default: 'INTERNAL_ERROR'
  error: { details: 'Additional info' }, // optional
  statusCode: 400, // optional, default: 500
  meta: { requestId: 'abc123' } // optional
});

res.validationError(options)

res.validationError({
  errors: [
    { field: 'email', message: 'Email is required', code: 'REQUIRED' },
    { field: 'password', message: 'Password too short', code: 'MIN_LENGTH' }
  ],
  message: 'Validation failed' // optional
});
// 📤 Status: 422 Unprocessable Entity

🎯 HTTP Status Responses

// 401 Unauthorized
res.unauthorized('Invalid credentials');

// 403 Forbidden
res.forbidden('Access denied');

// 404 Not Found
res.notFound('User not found');

// 409 Conflict
res.conflict('Email already exists');

// 429 Rate Limited
res.rateLimitExceeded({
  message: 'Too many requests',
  retryAfter: 60 // seconds, sets Retry-After header
});

// 500 Server Error
res.serverError('Database connection failed', error);

📊 Pagination Response

res.paginated(options)

res.paginated({
  data: users,
  pagination: {
    page: 1,
    limit: 10,
    total: 100,
    totalPages: 10,
    hasNext: true,
    hasPrev: false
  },
  message: 'Users retrieved successfully' // optional
});

// 📤 Response includes pagination metadata

🔧 Custom Responses

res.custom(options)

res.custom({
  statusCode: 418,
  data: { message: "I'm a teapot" },
  headers: { 'X-Custom': 'value' } // optional
});

res.streamFile(options)

res.streamFile({
  filePath: '/path/to/file.pdf',
  filename: 'document.pdf', // optional
  mimeType: 'application/pdf', // optional
  inline: false // optional, default: false (attachment)
});

📊 Response Format

All responses (except custom and streamFile) follow this standardized format:

{
  success: boolean;           // true for 2xx, false for errors
  message: string;            // Human-readable message
  data?: any;                 // Response payload (when applicable)
  meta?: object;              // Additional metadata (when applicable)
  code?: string;              // Error code (for error responses)
  error?: any;                // Error details (for error responses)
  timestamp: string;          // ISO timestamp
  pagination?: {              // For paginated responses
    page: number;
    limit: number;
    total: number;
    totalPages: number;
    hasNext: boolean;
    hasPrev: boolean;
  }
}

🎯 TypeScript Support

Full TypeScript definitions included for the best developer experience:

import express from 'express';
import ezyResponse, { SuccessOptions, ErrorOptions } from 'ezy-response';

const app = express();
app.use(ezyResponse());

app.get('/users', (req, res) => {
  res.success({
    message: 'Users found',
    data: users,
    meta: { total: users.length }
  });
});

⚙️ Configuration

Compression

Enable gzip compression for better performance:

app.use(ezyResponse({ enableCompression: true }));

Environment-based Error Details

Error details are automatically included/excluded based on NODE_ENV:

// In development: includes error stack traces
// In production: excludes sensitive error information
res.serverError('Database error', error);

📚 Real-World Examples

Complete CRUD API

const express = require('express');
const ezyResponse = require('ezy-response');

const app = express();
app.use(express.json());
app.use(ezyResponse());

// 📋 List users with pagination
app.get('/users', async (req, res) => {
  try {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 10;

    const { users, total } = await User.findAndCountAll({
      limit,
      offset: (page - 1) * limit
    });

    res.paginated({
      data: users,
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
        hasNext: page < Math.ceil(total / limit),
        hasPrev: page > 1
      },
      message: 'Users retrieved successfully'
    });
  } catch (error) {
    res.serverError('Failed to fetch users', error);
  }
});

// 👤 Get user by ID
app.get('/users/:id', async (req, res) => {
  try {
    const user = await User.findById(req.params.id);
    if (!user) {
      return res.notFound('User not found');
    }
    res.success({ data: user, message: 'User found' });
  } catch (error) {
    res.serverError('Failed to fetch user', error);
  }
});

// ➕ Create new user
app.post('/users', async (req, res) => {
  const { name, email, age } = req.body;
  const errors = [];

  // Validation
  if (!name || name.length < 2) {
    errors.push({ field: 'name', message: 'Name must be at least 2 characters' });
  }
  if (!email || !/\S+@\S+\.\S+/.test(email)) {
    errors.push({ field: 'email', message: 'Valid email is required' });
  }
  if (age && (age < 0 || age > 150)) {
    errors.push({ field: 'age', message: 'Age must be between 0 and 150' });
  }

  if (errors.length > 0) {
    return res.validationError({ errors });
  }

  try {
    const user = await User.create({ name, email, age });
    res.created({
      data: user,
      location: `/users/${user.id}`,
      message: 'User created successfully'
    });
  } catch (error) {
    if (error.code === 'UNIQUE_VIOLATION') {
      res.conflict('Email already exists');
    } else {
      res.serverError('Failed to create user', error);
    }
  }
});

// ✏️ Update user
app.put('/users/:id', async (req, res) => {
  try {
    const [updated] = await User.update(req.body, {
      where: { id: req.params.id },
      returning: true
    });

    if (!updated) {
      return res.notFound('User not found');
    }

    res.success({
      data: updated,
      message: 'User updated successfully'
    });
  } catch (error) {
    res.serverError('Failed to update user', error);
  }
});

// 🗑️ Delete user
app.delete('/users/:id', async (req, res) => {
  try {
    const deleted = await User.destroy({
      where: { id: req.params.id }
    });

    if (!deleted) {
      return res.notFound('User not found');
    }

    res.noContent('User deleted successfully');
  } catch (error) {
    res.serverError('Failed to delete user', error);
  }
});

Authentication & Authorization

// 🔐 Login endpoint
app.post('/auth/login', async (req, res) => {
  const { email, password } = req.body;

  if (!email || !password) {
    return res.validationError({
      errors: [
        { field: 'email', message: 'Email is required' },
        { field: 'password', message: 'Password is required' }
      ]
    });
  }

  try {
    const user = await User.findOne({ where: { email } });

    if (!user || !(await bcrypt.compare(password, user.password))) {
      return res.unauthorized('Invalid credentials');
    }

    const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET);

    res.success({
      data: { token, user: { id: user.id, name: user.name, email: user.email } },
      message: 'Login successful'
    });
  } catch (error) {
    res.serverError('Login failed', error);
  }
});

// 🚫 Protected route
app.get('/profile', authenticateToken, async (req, res) => {
  try {
    const user = await User.findById(req.user.id);
    res.success({ data: user, message: 'Profile retrieved' });
  } catch (error) {
    res.serverError('Failed to fetch profile', error);
  }
});

// Middleware
function authenticateToken(req, res, next) {
  const authHeader = req.headers.authorization;
  const token = authHeader && authHeader.split(' ')[1];

  if (!token) {
    return res.unauthorized('Access token is required');
  }

  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) {
      return res.forbidden('Invalid or expired token');
    }
    req.user = user;
    next();
  });
}

File Upload & Download

const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

// 📁 File upload
app.post('/upload', upload.single('file'), (req, res) => {
  if (!req.file) {
    return res.validationError({
      errors: [{ field: 'file', message: 'File is required' }]
    });
  }

  res.created({
    data: {
      filename: req.file.filename,
      originalName: req.file.originalname,
      size: req.file.size,
      path: `/files/${req.file.filename}`
    },
    message: 'File uploaded successfully'
  });
});

// 📥 File download
app.get('/files/:filename', (req, res) => {
  const filePath = path.join(__dirname, 'uploads', req.params.filename);

  if (!fs.existsSync(filePath)) {
    return res.notFound('File not found');
  }

  res.streamFile({
    filePath,
    filename: req.params.filename,
    inline: false
  });
});

🧪 Testing

The package includes comprehensive tests. Run them with:

# Run tests once
npm test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage report
npm run test:coverage

# Lint code
npm run lint

# Format code
npm run format

📈 Migration Guide

From Standard Express.js

Before:

// Success response
res.status(200).json({
  success: true,
  message: 'Data retrieved',
  data: users,
  timestamp: new Date().toISOString()
});

// Error response
res.status(400).json({
  success: false,
  error: 'Validation failed',
  message: 'Email is required',
  timestamp: new Date().toISOString()
});

// Pagination
res.status(200).json({
  success: true,
  data: items,
  pagination: { page, limit, total, totalPages },
  timestamp: new Date().toISOString()
});

After:

// Success response
res.success({
  message: 'Data retrieved',
  data: users
});

// Error response
res.validationError({
  errors: [{ field: 'email', message: 'Email is required' }]
});

// Pagination
res.paginated({
  data: items,
  pagination: { page, limit, total, totalPages }
});

From Other Response Libraries

ezy-response is designed to be a drop-in replacement for most response helper libraries. Simply:

  1. Remove your existing response library
  2. Install ezy-response: npm install ezy-response
  3. Replace app.use(yourOldLibrary()) with app.use(ezyResponse())
  4. Update your response calls to use ezy-response methods

🚀 Performance

  • Lightweight: Only ~5KB gzipped
  • Fast: Minimal overhead over standard Express.js responses
  • Memory efficient: No memory leaks, optimized for high-traffic applications
  • Compression: Optional gzip compression reduces response size by up to 80%

🔧 Advanced Usage

Custom Error Codes

res.error({
  message: 'User not found',
  code: 'USER_NOT_FOUND',
  statusCode: 404,
  meta: { userId: req.params.id }
});

Response Interceptors

// Add custom metadata to all responses
app.use((req, res, next) => {
  const originalSuccess = res.success;
  res.success = function (options = {}) {
    return originalSuccess.call(this, {
      ...options,
      meta: {
        ...options.meta,
        requestId: req.headers['x-request-id'],
        apiVersion: '1.0.0'
      }
    });
  };
  next();
});

Environment-specific Configurations

const isDevelopment = process.env.NODE_ENV === 'development';

app.use(
  ezyResponse({
    enableCompression: !isDevelopment // Only in production
    // Add custom options based on environment
  })
);

🔍 Debugging

Enable debug logging:

DEBUG=ezy-response* npm start

📋 Best Practices

  1. Consistent Error Codes: Use standardized error codes across your API
  2. Meaningful Messages: Provide clear, user-friendly error messages
  3. Proper Status Codes: Use appropriate HTTP status codes for different scenarios
  4. Security: Don't expose sensitive information in error responses
  5. Logging: Always log errors on the server side for debugging
// ✅ Good
res.error({
  message: 'Unable to process your request',
  code: 'INVALID_INPUT',
  statusCode: 400
});

// ❌ Avoid
res.error({
  message: 'Database connection failed: Connection refused at localhost:5432',
  error: sqlError,
  statusCode: 500
});

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

# Clone the repository
git clone https://github.com/Bittu-the-coder/ezy-response.git

# Install dependencies
npm install

# Run tests
npm test

# Run examples
npm run example:basic
npm run example:advanced

Guidelines

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Write tests for your changes
  4. Ensure all tests pass (npm test)
  5. Commit your changes (git commit -m 'Add some AmazingFeature')
  6. Push to the branch (git push origin feature/AmazingFeature)
  7. Open a Pull Request

📄 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • Inspired by the need for standardized API responses in Express.js applications
  • Built with ❤️ for the Node.js community
  • Thanks to all contributors and users who help improve this package

📞 Support


Made with ❤️ by Bittu-the-coder

Star this repository if it helped you!