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
Maintainers
Readme
🚀 ezy-response
🎯 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 Createdres.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:
- Remove your existing response library
- Install ezy-response:
npm install ezy-response - Replace
app.use(yourOldLibrary())withapp.use(ezyResponse()) - 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
- Consistent Error Codes: Use standardized error codes across your API
- Meaningful Messages: Provide clear, user-friendly error messages
- Proper Status Codes: Use appropriate HTTP status codes for different scenarios
- Security: Don't expose sensitive information in error responses
- 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:advancedGuidelines
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Write tests for your changes
- Ensure all tests pass (
npm test) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - 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
- 📧 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
- 📖 Documentation: README.md
Made with ❤️ by Bittu-the-coder
⭐ Star this repository if it helped you!
