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 🙏

© 2026 – Pkg Stats / Ryan Hefner

halify-response

v1.1.3

Published

Helper utilities for HAL-style API responses in Express.js

Downloads

19

Readme

halify-response

📦 A comprehensive utility library for building consistent, HAL (Hypertext Application Language)-compliant JSON responses in your Express.js or REST APIs.
Brought to you by Shivank Singh.


✨ Features

  • 🔗 HAL-compliant responses with _links, _embedded, and metadata
  • Comprehensive response types for all API operations
  • 🛡️ Advanced HTTP error handling with standardized error codes
  • 🎯 HTTP success responses with semantic status codes
  • 📊 Collection operations with pagination, search, and filtering
  • 📦 Resource operations with CRUD support
  • 🔍 Bulk operations support with detailed results
  • 🏥 Health check and status endpoints
  • 💬 Developer-friendly with TypeScript support
  • 🚀 Lightweight and fast with zero dependencies
  • 🎯 Easy integration into any Node.js project
  • 🔄 Functional API with direct imports

📦 Installation

npm install halify-response

Note: This library is written in TypeScript and provides full type definitions out of the box.


🚀 Quick Start

import { 
  // HTTP Success Responses
  ok, created, updated, deleted,
  
  // HTTP Error Responses  
  badRequest, notFound, validationError,
  
  // Resource Operations
  createResponse, updateResponse, detailResponse,
  
  // Collection Operations
  listResponse, paginatedResponse, searchResponse
} from 'halify-response';

// Success response - 200 OK
const successRes = ok({ endpoint: '/api/users/123' });

// Created response - 201 Created
const createdRes = created({ endpoint: '/api/users/123' });

// Validation error - 422
const validationRes = validationError(
  { email: 'Email is required', password: 'Password too short' },
  { endpoint: '/api/users' }
);

// Resource creation
const resourceRes = createResponse(
  'user', 
  { id: 123, name: 'John Doe' }, 
  '/api/users/123'
);

📚 HTTP Status Code Responses

🎉 Success Responses

The library provides semantic functions for HTTP success responses:

200 - OK

import { ok } from 'halify-response';

const response = ok({ 
  endpoint: '/api/users/123',
  customDetail: 'User retrieved successfully'
});

Output:

{
  "_links": { "self": { "href": "/api/users/123" } },
  "data": {
    "status": 200,
    "title": "OK",
    "detail": "User retrieved successfully",
    "code": "SUCCESS"
  }
}

200 - Updated

import { updated } from 'halify-response';

const response = updated({ endpoint: '/api/users/123' });

Output:

{
  "_links": { "self": { "href": "/api/users/123" } },
  "data": {
    "status": 200,
    "title": "Updated",
    "detail": "The resource has been updated successfully.",
    "code": "RESOURCE_UPDATED"
  }
}

201 - Created

import { created } from 'halify-response';

const response = created({ endpoint: '/api/users/123' });

Output:

{
  "_links": { 
    "self": { "href": "/api/users/123" },
    "location": { "href": "/api/resource", "title": "Resource Location" }
  },
  "data": {
    "status": 201,
    "title": "Created",
    "detail": "The resource has been created successfully.",
    "code": "RESOURCE_CREATED"
  }
}

202 - Accepted

import { accepted } from 'halify-response';

const response = accepted({ endpoint: '/api/jobs/123' });

204 - No Content

import { noContent } from 'halify-response';

const response = noContent({ endpoint: '/api/users/123' });

204 - Deleted

import { deleted } from 'halify-response';

const response = deleted({ endpoint: '/api/users/123' });

Output:

{
  "_links": { "self": { "href": "/api/users/123" } },
  "data": {
    "status": 204,
    "title": "Deleted",
    "detail": "The resource has been deleted successfully.",
    "code": "RESOURCE_DELETED"
  }
}

206 - Partial Content

import { partialContent } from 'halify-response';

const response = partialContent({ endpoint: '/api/files/123' });

🚨 Error Responses

The library provides comprehensive HTTP error responses:

400 - Bad Request

import { badRequest } from 'halify-response';

const response = badRequest({ 
  endpoint: '/api/users',
  customDetail: 'Missing required fields'
});

Output:

{
  "_links": { "self": { "href": "/api/users" } },
  "error": {
    "status": 400,
    "title": "Bad Request",
    "detail": "Missing required fields",
    "code": "BAD_REQUEST"
  }
}

401 - Unauthorized

import { unauthorized } from 'halify-response';

const response = unauthorized({ endpoint: '/api/protected' });

Output:

{
  "_links": {
    "self": { "href": "/api/protected" },
    "login": { "href": "/auth/login", "title": "Login" }
  },
  "error": {
    "status": 401,
    "title": "Unauthorized",
    "detail": "Authentication is required to access this resource.",
    "code": "UNAUTHORIZED"
  }
}

403 - Forbidden

import { forbidden } from 'halify-response';

const response = forbidden({ endpoint: '/api/admin' });

404 - Not Found

import { notFound } from 'halify-response';

const response = notFound({ endpoint: '/api/users/999' });

405 - Method Not Allowed

import { methodNotAllowed } from 'halify-response';

const response = methodNotAllowed({ endpoint: '/api/users' });

409 - Conflict

import { conflict } from 'halify-response';

const response = conflict({ endpoint: '/api/users' });

422 - Validation Error

import { validationError } from 'halify-response';

const response = validationError(
  {
    email: 'Email is required',
    password: 'Password must be at least 8 characters'
  },
  { endpoint: '/api/users' }
);

Output:

{
  "_links": { "self": { "href": "/api/users" } },
  "error": {
    "status": 422,
    "title": "Validation Error",
    "detail": "One or more validation errors occurred.",
    "code": "VALIDATION_ERROR",
    "fields": {
      "email": "Email is required",
      "password": "Password must be at least 8 characters"
    }
  }
}

429 - Too Many Requests

import { tooManyRequests } from 'halify-response';

const response = tooManyRequests({ endpoint: '/api/users' });

500 - Internal Server Error

import { internalServerError } from 'halify-response';

const response = internalServerError({ endpoint: '/api/users' });

Output:

{
  "_links": {
    "self": { "href": "/api/users" },
    "support": { "href": "/support", "title": "API Support" }
  },
  "error": {
    "status": 500,
    "title": "Internal Server Error",
    "detail": "An unexpected error occurred on the server. Please try again later.",
    "code": "SERVER_ERROR"
  }
}

503 - Service Unavailable

import { serviceUnavailable } from 'halify-response';

const response = serviceUnavailable({ endpoint: '/api/users' });

🏗️ Resource Operations

Generic Resource Response

import { resourceResponse } from 'halify-response';

const response = resourceResponse(
  'user', 
  { id: 123, name: 'John Doe' }, 
  '/api/users/123',
  {
    message: 'Custom operation completed',
    additionalLinks: {
      profile: { href: '/api/users/123/profile', title: 'User Profile' }
    }
  }
);

Create Response

import { createResponse } from 'halify-response';

const response = createResponse(
  'user',
  { id: 123, name: 'John Doe', email: '[email protected]' },
  '/api/users/123'
);

Output:

{
  "success": true,
  "_links": { "self": { "href": "/api/users/123" } },
  "name": "user",
  "message": "Resource created successfully",
  "attributes": {
    "id": 123,
    "name": "John Doe",
    "email": "[email protected]"
  }
}

Update Response

import { updateResponse } from 'halify-response';

const response = updateResponse(
  'user',
  { id: 123, name: 'John Updated', updatedAt: '2024-01-01' },
  '/api/users/123'
);

Detail Response

import { detailResponse } from 'halify-response';

const response = detailResponse(
  'user',
  { id: 123, name: 'John Doe', profile: { bio: 'Developer' } },
  '/api/users/123'
);

📦 Collection Operations

Generic Collection Response

import { collectionResponse } from 'halify-response';

const response = collectionResponse(
  'users',
  userList,
  '/api/users',
  {
    total: 100,
    metadata: { lastUpdated: '2024-01-01' },
    links: {
      next: { href: '/api/users?page=2', title: 'Next Page' }
    }
  }
);

List Response

import { listResponse } from 'halify-response';

const response = listResponse(
  'users',
  [
    { id: 1, name: 'John' },
    { id: 2, name: 'Jane' }
  ],
  '/api/users',
  { 
    total: 2,
    metadata: { 
      lastUpdated: '2024-01-01' 
    } 
  }
);

Output:

{
  "success": true,
  "_links": { "self": { "href": "/api/users" } },
  "name": "users",
  "_embedded": {
    "data": [
      { "id": 1, "name": "John" },
      { "id": 2, "name": "Jane" }
    ]
  },
  "total": 2,
  "metadata": { "lastUpdated": "2024-01-01" }
}

Paginated Response

import { paginatedResponse } from 'halify-response';

const response = paginatedResponse(
  'users',                    // Module name
  userList,                   // Data array
  '/api/users?page=2',        // Current page URL
  '/api/users?page=1',        // First page URL
  '/api/users?page=10',       // Last page URL
  2,                          // Current page
  10,                         // Total pages
  20,                         // Items per page
  200                         // Total items
);

Output:

{
  "success": true,
  "name": "users",
  "_embedded": { "data": [...] },
  "_links": {
    "self": { "href": "/api/users?page=2" },
    "first": { "href": "/api/users?page=1" },
    "last": { "href": "/api/users?page=10" }
  },
  "metadata": {
    "page": 2,
    "pageCount": 10,
    "pageSize": 20,
    "total": 200
  }
}

Search Response

import { searchResponse } from 'halify-response';

const response = searchResponse(
  'users',
  searchResults,
  '/api/users/search',
  { query: 'john', filters: { status: 'active' } },
  {
    total: 15,
    page: 1,
    pageSize: 10,
    query: 'john',
    filters: ['status:active']
  }
);

Output:

{
  "success": true,
  "_links": { "self": { "href": "/api/users/search" } },
  "name": "users",
  "_embedded": { "results": [...] },
  "metadata": {
    "searchParams": { "query": "john", "filters": { "status": "active" } },
    "query": "john",
    "filters": ["status:active"],
    "page": 1,
    "pageSize": 10
  }
}

🔧 Specialized Operations

Bulk Response

import { bulkResponse } from 'halify-response';

const response = bulkResponse(
  'users',
  'create',
  [
    { id: 1, status: 'success' },
    { id: 2, status: 'failed', error: 'Email exists' }
  ],
  '/api/users/bulk',
  {
    successCount: 1,
    failureCount: 1,
    errors: [
      { id: 2, field: 'email', message: 'Email already exists' }
    ]
  }
);

Output:

{
  "success": true,
  "_links": { "self": { "href": "/api/users/bulk" } },
  "name": "users",
  "operation": "create",
  "results": [
    { "id": 1, "status": "success" },
    { "id": 2, "status": "failed", "error": "Email exists" }
  ],
  "successCount": 1,
  "failureCount": 1,
  "errors": [
    { "id": 2, "field": "email", "message": "Email already exists" }
  ]
}

Status Response

import { statusResponse } from 'halify-response';

const response = statusResponse(
  'user-service',
  'healthy',
  '/api/health',
  {
    version: '1.0.0',
    uptime: 3600,
    dependencies: {
      database: 'connected',
      redis: 'connected'
    },
    details: {
      memory: '45%',
      cpu: '12%'
    }
  }
);

Output:

{
  "success": true,
  "_links": { "self": { "href": "/api/health" } },
  "name": "user-service",
  "status": "healthy",
  "timestamp": "2024-01-01T12:00:00.000Z",
  "version": "1.0.0",
  "uptime": 3600,
  "dependencies": {
    "database": "connected",
    "redis": "connected"
  },
  "details": {
    "memory": "45%",
    "cpu": "12%"
  }
}

🔧 Advanced Usage

Express.js Integration

import express from 'express';
import { 
  ok, created, notFound, internalServerError,
  detailResponse, createResponse, paginatedResponse 
} from 'halify-response';

const app = express();

// GET single user
app.get('/api/users/:id', async (req, res) => {
  try {
    const user = await getUserById(req.params.id);
    if (!user) {
      return res.status(404).json(notFound({ endpoint: req.originalUrl }));
    }
    
    const response = detailResponse('user', user, req.originalUrl);
    res.json(response);
  } catch (error) {
    const errorRes = internalServerError({ endpoint: req.originalUrl });
    res.status(500).json(errorRes);
  }
});

// POST create user
app.post('/api/users', async (req, res) => {
  try {
    const newUser = await createUser(req.body);
    const response = createResponse('user', newUser, `/api/users/${newUser.id}`);
    res.status(201).json(response);
  } catch (error) {
    const errorRes = internalServerError({ endpoint: req.originalUrl });
    res.status(500).json(errorRes);
  }
});

// GET users with pagination
app.get('/api/users', async (req, res) => {
  try {
    const { page = 1, limit = 20 } = req.query;
    const { users, total, totalPages } = await getUsers(page, limit);
    
    const response = paginatedResponse(
      'users',
      users,
      req.originalUrl,
      `/api/users?page=1&limit=${limit}`,
      `/api/users?page=${totalPages}&limit=${limit}`,
      parseInt(page),
      totalPages,
      parseInt(limit),
      total
    );
    
    res.json(response);
  } catch (error) {
    const errorRes = internalServerError({ endpoint: req.originalUrl });
    res.status(500).json(errorRes);
  }
});

Error Middleware

import { 
  badRequest, unauthorized, forbidden, notFound, 
  validationError, internalServerError 
} from 'halify-response';

// Global error handler
app.use((error, req, res, next) => {
  let response;
  let statusCode = 500;

  switch (error.name) {
    case 'ValidationError':
      response = validationError(error.fields, { endpoint: req.originalUrl });
      statusCode = 422;
      break;
    case 'UnauthorizedError':
      response = unauthorized({ endpoint: req.originalUrl });
      statusCode = 401;
      break;
    case 'ForbiddenError':
      response = forbidden({ endpoint: req.originalUrl });
      statusCode = 403;
      break;
    case 'NotFoundError':
      response = notFound({ endpoint: req.originalUrl });
      statusCode = 404;
      break;
    default:
      response = internalServerError({ 
        endpoint: req.originalUrl,
        customDetail: error.message 
      });
      statusCode = 500;
  }
  
  res.status(statusCode).json(response);
});

Custom Response Options

import { ok, created, resourceResponse } from 'halify-response';

// Custom success response with additional links
const response = ok({
  endpoint: '/api/users/123',
  customDetail: 'User profile updated successfully',
  additionalLinks: {
    profile: { href: '/api/users/123/profile', title: 'User Profile' },
    settings: { href: '/api/users/123/settings', title: 'User Settings' }
  }
});

// Custom resource response with metadata
const resourceRes = resourceResponse(
  'user',
  userData,
  '/api/users/123',
  {
    message: 'User data retrieved with preferences',
    additionalLinks: {
      preferences: { href: '/api/users/123/preferences' }
    }
  }
);

🔍 Utility Functions

Error Code Validation

import { 
  isSupportedStatusCode, 
  getErrorCodes,
  isSupportedSuccessStatusCode,
  getSuccessCodes 
} from 'halify-response';

// Check if error code is supported
if (isSupportedStatusCode(422)) {
  console.log('422 is supported for error responses');
}

// Get all available error codes
const errorCodes = getErrorCodes();
console.log(errorCodes); // { 400: { title: "Bad Request", ... }, ... }

// Check if success code is supported
if (isSupportedSuccessStatusCode(201)) {
  console.log('201 is supported for success responses');
}

// Get all available success codes
const successCodes = getSuccessCodes();
console.log(successCodes); // { 200: { title: "OK", ... }, ... }

Generic Response Creation

import { createErrorResponse, createSuccessResponse } from 'halify-response';

// Create custom error response
const customError = createErrorResponse(409, {
  endpoint: '/api/users',
  customDetail: 'User with this email already exists',
  additionalLinks: {
    'forgot-password': { href: '/auth/forgot-password', title: 'Forgot Password' }
  }
});

// Create custom success response
const customSuccess = createSuccessResponse(202, {
  endpoint: '/api/jobs',
  customDetail: 'Job queued successfully',
  additionalLinks: {
    status: { href: '/api/jobs/123/status', title: 'Job Status' }
  }
});

📝 TypeScript Support

This library is built with TypeScript and provides comprehensive type definitions:

import { 
  // Response interfaces
  ResourceResponse, CollectionResponse,
  
  // Option interfaces
  ResourceResponseOptions, CollectionResponseOptions,
  
  // HTTP response interfaces
  SuccessResponse, ErrorResponse,
  
  // Functions
  createResponse, listResponse, ok, badRequest
} from 'halify-response';

interface User {
  id: number;
  name: string;
  email: string;
}

// Fully typed responses
const userResponse: ResourceResponse = createResponse(
  'user', 
  { id: 1, name: 'John', email: '[email protected]' }, 
  '/api/users/1'
);

const usersResponse: CollectionResponse = listResponse(
  'users',
  [{ id: 1, name: 'John' }],
  '/api/users',
  { total: 1 }
);

const successResponse: SuccessResponse = ok({ endpoint: '/api/users' });
const errorResponse: ErrorResponse = badRequest({ endpoint: '/api/users' });

Available Types

Response Interfaces

  • ResourceResponse - Resource operation responses
  • CollectionResponse - Collection operation responses
  • SuccessResponse - HTTP success responses
  • ErrorResponse - HTTP error responses
  • BulkResponse - Bulk operation responses
  • StatusResponse - Status/health check responses

Option Interfaces

  • ResourceResponseOptions - Options for resource responses
  • CollectionResponseOptions - Options for collection responses
  • PaginatedCollectionOptions - Options for paginated collections
  • SearchCollectionOptions - Options for search collections
  • BulkResponseOptions - Options for bulk operations
  • StatusResponseOptions - Options for status responses

Validation Types

  • ValidationError - Validation error structure
  • ValidationErrorResponseOptions - Validation error options

🏗️ Build and Development

# Install dependencies
npm install

# Type check
npm run type-check

# Build the library
npm run build

# Publish to npm
npm publish

Requirements:

  • Node.js >= 12.0.0
  • TypeScript >= 4.9.5

📋 API Reference

HTTP Success Functions

  • ok(options?) - 200 OK
  • updated(options?) - 200 Updated
  • created(options?) - 201 Created
  • accepted(options?) - 202 Accepted
  • noContent(options?) - 204 No Content
  • deleted(options?) - 204 Deleted
  • partialContent(options?) - 206 Partial Content

HTTP Error Functions

  • badRequest(options?) - 400 Bad Request
  • unauthorized(options?) - 401 Unauthorized
  • forbidden(options?) - 403 Forbidden
  • notFound(options?) - 404 Not Found
  • methodNotAllowed(options?) - 405 Method Not Allowed
  • conflict(options?) - 409 Conflict
  • validationError(fields, options?) - 422 Validation Error
  • tooManyRequests(options?) - 429 Too Many Requests
  • internalServerError(options?) - 500 Internal Server Error
  • serviceUnavailable(options?) - 503 Service Unavailable

Resource Functions

  • resourceResponse(name, data, url, options?) - Generic resource response
  • createResponse(name, data, url, options?) - Create resource
  • updateResponse(name, data, url, options?) - Update resource
  • detailResponse(name, data, url, options?) - Get resource details

Collection Functions

  • collectionResponse(name, items, url, options?) - Generic collection
  • listResponse(name, items, url, options?) - Simple list
  • paginatedResponse(name, items, url, first, last, page, count, size, total) - Paginated list
  • searchResponse(name, results, url, params, options?) - Search results

Specialized Functions

  • bulkResponse(name, operation, results, url, options?) - Bulk operations
  • statusResponse(service, status, url, options?) - Health/status check

Utility Functions

  • createErrorResponse(code, options?) - Custom error response
  • createSuccessResponse(code, options?) - Custom success response
  • getErrorCodes() - Get all error code definitions
  • getSuccessCodes() - Get all success code definitions
  • isSupportedStatusCode(code) - Check if error code is supported
  • isSupportedSuccessStatusCode(code) - Check if success code is supported

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License

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


🙏 Acknowledgments

  • HAL Specification for the response format
  • Express.js community for inspiration
  • All contributors and users of this library

Made with ❤️ by Shivank Singh