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

better-endpoints

v1.4.0

Published

A TypeScript library to simplify API response handling and manage custom HTTP errors. It provides an `ApiResponse` decorator to format success and error responses consistently, along with specific error classes for each HTTP status code.

Downloads

69

Readme

Better Endpoints

A TypeScript library to simplify API response handling and manage custom HTTP errors. It provides an ApiResponse decorator to format success and error responses consistently, along with specific error classes for each HTTP status code.

Installation

To add the library to your project:

npm install better-endpoints

Usage

1. Decorator @ApiResponse

The ApiResponse decorator simplifies API response handling by defining a standard format for successful responses and automatically capturing errors, ensuring consistent responses.

import { ApiResponse } from 'better-endpoints';

class ExampleController {
  @ApiResponse()
  async getData() {
    // Logic for the method returning data
    return { id: 1, name: 'Example' };
  }
}

When the getData method is successfully executed, it will return an object in the following format:

{
  "success": true,
  "status": 200,
  "message": { "id": 1, "name": "Example" }
}

In case of an error, ApiResponse captures the error and returns a standardized response.


2. Handling HTTP Errors

The library provides specific error classes for HTTP statuses, allowing you to throw errors with predefined status codes and messages. These errors are automatically captured by the ApiResponse decorator.

Example:

import { Error404, Error500 } from 'better-endpoints';

class ExampleController {
  @ApiResponse()
  async fetchResource(id: number) {
    if (!this.resourceExists(id)) {
      throw new Error404('Resource not found');
    }

    try {
      // Logic to fetch resource
      return { id, name: 'Resource' };
    } catch (error) {
      throw new Error500('Internal server error');
    }
  }
}

Error example:

{
  "success": false,
  "status": 404,
  "message": "Resource not found"
}

3. Available Errors

The library includes the following error classes, which can be used to represent specific HTTP errors:

| Class | Code | Name | Description | | --- | --- | --- | --- | | Error400 | 400 | Bad Request | The request is invalid or malformed. | Error401 | 401 | Unauthorized | Authentication is required or has failed. | Error403 | 403 | Forbidden | The client does not have permission to access the resource. | Error404 | 404 | Not Found | The requested resource could not be found. | Error408 | 408 | Request Timeout | The server timed out waiting for the request. | Error409 | 409 | Conflict | The request conflicts with the current state of the resource. | Error410 | 410 | Gone | The resource requested is no longer available. | Error415 | 415 | Unsupported Media Type | The media type of the request is not supported by the server. | Error422 | 422 | Unprocessable Entity | The server understands the request but cannot process it. | Error426 | 426 | Upgrade Required | The client must upgrade to a different protocol. | Error429 | 429 | Too Many Requests | The user has sent too many requests in a given time. | Error451 | 451 | Unavailable For Legal Reasons | The resource is unavailable due to legal restrictions. | Error500 | 500 | Internal Server Error | A generic error occurred on the server. | Error502 | 502 | Bad Gateway | The server received an invalid response from the upstream server. | Error503 | 503 | Service Unavailable | The server is temporarily unavailable or overloaded. | Error504 | 504 | Gateway Timeout | The upstream server failed to send a request in time. | Error507 | 507 | Insufficient Storage | The server is unable to store the representation needed to complete the request.


4. Customizing options

The options parameter for the ApiResponse decorator allows you to customize both the success and error responses, including HTTP status codes and messages. You can adjust the onSuccess and onError properties to define more specific behavior.

import { ApiResponse } from 'better-endpoints';

class ExampleController {
  @ApiResponse({ 
    onSuccess: { status: 201, message: 'Resource created successfully' },
    onError: { message: 'Custom error' }
  })
  async createResource() {
    return { id: 1, name: 'New Resource' };
  }
}

In this example:

  • The success status is customized to 201 with the message 'Resource created successfully'.
  • The error message is set to 'Custom Error'.

These customizations take priority over any default error messages that would normally be captured by the decorator.


5. Debug Mode

The ApiResponse decorator supports an optional debug mode that can be enabled by setting the enableDebug property in the options parameter. When enabled, the decorator will log:

  • Any errors caught during the method execution.
  • The final response object that will be returned, whether it's a success or an error.
import { ApiResponse } from 'better-endpoints';

class ExampleController {
  @ApiResponse({ enableDebug: true })
  async getData() {
    throw new Error('Something went wrong');
  }
}

6. Manual Response Handling

In some cases, the @ApiResponse decorator might not fit all use cases. To provide more flexibility, better-endpoints allows you to manually create responses that follow the same standardized format.

6.1. ResponseDto Type

All responses, whether handled by the decorator or manually, follow the ResponseDto type:

type SuccessStatus = 200 | 201 | 202;

export type ResponseDto<T = any> = {
  success: true;
  message: T;
  status: SuccessStatus;
} | {
  success: false;
  message: string;
  status: number;
};

This ensures consistency across all responses in your API.

6.2. Creating Success Responses

To manually generate a success response, use the createSuccessResponse function:

import { createSuccessResponse } from 'better-endpoints';

const response = createSuccessResponse({ id: 1, name: 'Example' });
console.log(response);

Output:

{
  "success": true,
  "message": { "id": 1, "name": "Example" },
  "status": 200
}

You can also specify a different status code:

const response = createSuccessResponse("Created successfully", 201);

6.3. Creating Error Responses

To generate an error response, use createErrorResponse:

import { createErrorResponse } from 'better-endpoints';

const response = createErrorResponse("Something went wrong", 500);
console.log(response);

Output:

{
  "success": false,
  "message": "Something went wrong",
  "status": 500
}

6.4. Direct Response Objects vs Helper Functions

Instead of using helper functions, you can also return the response object directly:

import { ResponseDto } from 'better-endpoints';

const response: ResponseDto<string> = { 
  success: true, 
  message: "Request successful", 
  status: 200 
};

const errorResponse: ResponseDto = { 
  success: false, 
  message: "Something went wrong", 
  status: 500 
};

However, to simplify response creation and ensure consistency, use the built-in helper functions.

6.5. When to Use Manual Responses

Manual responses should be used when:

  • The @ApiResponse decorator does not fit a specific scenario.
  • You need to handle responses outside of a controller method.
  • You want to return formatted responses from middleware or services.

6.6. Example

import { createErrorResponse, createSuccessResponse, ResponseDto } from 'better-endpoints';

class UserService {

  async getUser(id: number): Promise<ResponseDto> {
    const user = await this.findUser(id);
    if (!user) {
      return createErrorResponse("User not found", 404)
    }
    
    return createSuccessResponse(user);
  }
}

Examples

Common use, with debug mode enabled:

import { ApiResponse, Error404, Error500 } from 'better-endpoints';

class ExampleController {

  @ApiResponse({ onSuccess: { status: 200 }, enableDebug: true })
  async getData(id: number) {
    if (!this.exists(id)) {
      throw new Error404('Resource not found');
    }

    return this.findData(id);
  }

  private findData(id: number) {
    try {
      return { id, name: 'Resource Name' };
    } catch (error) {
      // Errors can be thrown from anywhere
      throw new Error500();
    }
  }
}

Re-throwing errors:

import { Error404, Error500 } from 'better-endpoints';

class ExampleService {
  
  async saveData(id: number, data: any) {
    try {
      const resource = await this.findData(id);

      // ...Save data logic

      return 'Data saved successfully';
    } catch (error) {
      // Log the error for debugging purposes
      Database.registerLog(`Error while saving data: ${error.message}`);

      // Re-throw the error to be handled by @ApiResponse or other layers
      throw error;
    }
  }

  private async findData(id: number) {
    try {
      // Simulating a database fetch
      return { id, name: 'Resource Name' };
    } catch (error) {
      // Log the error and re-throw it as a specific HTTP error
      console.error('Unexpected error while fetching data:', error);
      throw new Error500('Database error occurred');
    }
  }
}

In this case, it is important to note that the saveData function is being called by a method decorated with @ApiResponse.

License

MIT