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

api-core-backend

v1.0.20

Published

Simple REST API response, pagination, filtering, search, HTTP status, and Express middleware helpers for Node.js and TypeScript backends.

Readme

api-core-backend

npm version license npm downloads dependencies types node source

REST API helpers for Node.js, Express, and TypeScript.

api-core-backend is a small utility package for building clean and consistent REST APIs. It gives backend projects a standard way to send JSON responses, handle errors, read common query parameters, paginate data with max and offset, log requests, and generate optional Swagger/OpenAPI documentation.

Use it when you want your API controllers to stay focused on business logic instead of repeating the same response, pagination, error, and middleware code in every project.

Why Use It

  • Keeps API responses consistent across endpoints and projects
  • Reduces repeated controller, pagination, error, and logging code
  • Provides safe defaults for common REST API patterns
  • Works well in JavaScript and TypeScript projects
  • Keeps runtime dependencies at zero

Use With

Languages And Runtimes

  • JavaScript
  • TypeScript
  • Node.js
  • Bun runtime
  • Deno runtime with npm package support

Frameworks

  • Express
  • NestJS with Express adapter
  • Fastify
  • Koa
  • Hono
  • Restify
  • AdonisJS
  • FeathersJS
  • Serverless API routes
  • Any Node.js framework that can use plain helper functions

Express middleware helpers such as logger(), asyncHandler(), notFoundMiddleware, and errorMiddleware are designed for Express-compatible req, res, and next handlers. Response, pagination, filter, search, sorting, status code, error class, and Swagger helpers can be used in any Node.js backend.

Features

  • Consistent success, error, validation, and paginated JSON responses
  • Simple max and offset pagination with automatic total
  • Query helpers for filters, search, and sorting
  • HTTP status constants and typed HTTP error classes
  • Express helpers for async handlers, not found routes, and error handling
  • Request logger with project, file, method, status, duration, user, and safe JSON output
  • Optional Swagger/OpenAPI helpers for docs
  • TypeScript types, ESM and CommonJS builds, and no runtime dependencies

Installation

npm install api-core-backend

Optional Swagger dependencies:

npm install swagger-ui-express swagger-jsdoc

Requirements

  • Node.js >=18
  • Express or an Express-compatible framework for middleware helpers
  • TypeScript supported out of the box

Quick Start

import express from "express";
import {
  asyncHandler,
  configureLogger,
  errorMiddleware,
  getFilters,
  getPagination,
  getSearch,
  getSorting,
  logger,
  notFoundMiddleware,
  paginate,
  response,
  statusCode,
} from "api-core-backend";

const app = express();

app.use(express.json());

configureLogger({
  projectName: "my-api",
  getUser: (req) => req.user?.username,
});

app.use(logger());

const users = [
  { id: "1", name: "Sokha", status: "ACTIVE", role: "ADMIN" },
  { id: "2", name: "Dara", status: "ACTIVE", role: "USER" },
  { id: "3", name: "Sophea", status: "INACTIVE", role: "USER" },
];

async function findAllUsers(req, res) {
  const pagination = getPagination(req.query);
  const filters = getFilters(req.query, ["status", "role"]);
  const search = getSearch(req.query);
  const sorting = getSorting(req.query);

  const filteredUsers = users
    .filter((user) => (filters.status ? user.status === filters.status : true))
    .filter((user) => (filters.role ? user.role === filters.role : true))
    .filter((user) =>
      search.keyword
        ? user.name.toLowerCase().includes(search.keyword.toLowerCase())
        : true,
    )
    .sort((a, b) => {
      if (!sorting.sortBy) return 0;

      const left = String(a[sorting.sortBy] ?? "");
      const right = String(b[sorting.sortBy] ?? "");

      return sorting.sortOrder === "desc"
        ? right.localeCompare(left)
        : left.localeCompare(right);
    });

  const result = paginate(filteredUsers, pagination);

  return res.json(
    response(result, statusCode.OK, "Users fetched successfully"),
  );
}

app.get("/api/users", asyncHandler(findAllUsers));

app.use(notFoundMiddleware);
app.use(errorMiddleware);

app.listen(3000);

Pagination

Pagination uses max and offset.

GET /api/users?max=10&offset=0
import { getPagination, paginate, response } from "api-core-backend";

const pagination = getPagination(req.query);
const result = paginate(users, pagination);

return res.json(response(result));

Defaults:

{
  "max": 10,
  "offset": 0
}

Configure limits:

const pagination = getPagination(req.query, {
  defaultMax: 20,
  maxMax: 100,
});

Invalid values fall back to defaults. Requested max is capped by maxMax.

Logger

Configure once during application bootstrap:

import { configureLogger, logger } from "api-core-backend";

configureLogger({
  projectName: "my-api",
  getUser: (req) => req.user?.username,
});

app.use(logger());

Example output:

[my-api] | 2026-04-27 16:00:00 | INFO GET /api/users 200 6ms | file=index.js | method=findAllUsers | by=anonymous

Logger behavior:

  • Uses Cambodia local time in YYYY-MM-DD HH:mm:ss format
  • Uses the running entry file when available, such as index.js
  • Falls back to route-based file inference, such as users.controller.ts
  • Infers handler method names from named Express route handlers
  • Redacts sensitive request and response fields
  • Hides request source by default
  • Supports global and per-middleware options

Logger options:

configureLogger({
  projectName: "my-api",
  includeRequestFrom: false,
  includeUserAgent: false,
  includeRouteContext: true,
  includeRequestData: false,
  includeResponseData: true,
  maxRequestDataLength: 500,
  maxResponseDataLength: 2000,
  redactFields: ["password", "token", "authorization", "secret", "apiKey"],
  controllerFileSuffix: ".controller.ts",
  getUser: (req) => req.user?.username,
});

requestLogger() remains available as an alias for logger().

For a single-file app such as index.js, run your app with node index.js or nodemon index.js and use named route handlers for clean log output:

configureLogger({
  projectName: "my-api",
});

async function findAllUsers(req, res) {
  return res.json(response(users));
}

app.get("/api/users", asyncHandler(findAllUsers));

Example output:

[my-api] | 2026-04-28 08:55:59 | INFO GET /api/users 200 29ms | file=index.js | method=findAllUsers | by=anonymous
***response={"success":true,"statusCode":200,"message":"OK","data":[{"id":"1","name":"Sokha","status":"ACTIVE","role":"ADMIN"},{"id":"2","name":"Dara","status":"ACTIVE","role":"USER"},{"id":"3","name":"Sophea","status":"INACTIVE","role":"USER"}],"total":3,"timestamp":"2026-04-28 08:55:59"}***

If you want to force a file name, set sourceFile:

configureLogger({
  projectName: "my-api",
  sourceFile: "index.js",
});

Responses

Short response:

return res.json(response(users));

Full response with status code and message:

return res.json(response(users, statusCode.OK, "Users fetched successfully"));

Paginated response:

const pagination = getPagination(req.query);
const result = paginate(filteredUsers, pagination);

return res.json(response(result, statusCode.OK, "Users fetched successfully"));

Other response helpers:

import {
  errorResponse,
  paginatedResponse,
  response,
  successResponse,
  validationErrorResponse,
} from "api-core-backend";

successResponse({ message: "Done", data: { id: 1 } });
errorResponse({ message: "Something went wrong", code: "ERROR_CODE" });
validationErrorResponse({
  message: "Validation failed",
  errors: [{ field: "email", message: "Email is required" }],
});
paginatedResponse({
  message: "Users fetched successfully",
  data: users,
  max: 10,
  offset: 0,
  total: 100,
});

Success response shape:

{
  "success": true,
  "statusCode": 200,
  "message": "Request successful",
  "data": { "id": 1, "name": "Sokha" },
  "total": 1,
  "timestamp": "2026-04-27 21:59:03"
}

Query Helpers

import { getFilters, getSearch, getSorting } from "api-core-backend";

const filters = getFilters(req.query, ["status", "role"]);
const search = getSearch(req.query);
const sorting = getSorting(req.query);

Supported query parameters:

?max=10&offset=0
?status=ACTIVE&role=USER
?q=sokha
?search=sokha
?sortBy=name&sortOrder=desc

Error Handling

import {
  BadRequestError,
  ConflictError,
  ForbiddenError,
  NotFoundError,
  UnauthorizedError,
  ValidationError,
  asyncHandler,
  errorMiddleware,
  notFoundMiddleware,
} from "api-core-backend";

app.get("/api/users/:id", asyncHandler(findUserById));

throw new NotFoundError("User not found");
throw new BadRequestError("Name is required");
throw new UnauthorizedError("Please login first");
throw new ForbiddenError("Permission denied");
throw new ConflictError("Email already exists");
throw new ValidationError("Validation failed", [
  { field: "email", message: "Email is required" },
]);

app.use(notFoundMiddleware);
app.use(errorMiddleware);

Status Codes

import { HTTP_STATUS, statusCode } from "api-core-backend";

statusCode.OK; // 200
statusCode.CREATED; // 201
statusCode.BAD_REQUEST; // 400
statusCode.UNAUTHORIZED; // 401
statusCode.FORBIDDEN; // 403
statusCode.NOT_FOUND; // 404
statusCode.CONFLICT; // 409
statusCode.UNPROCESSABLE_ENTITY; // 422
statusCode.INTERNAL_SERVER_ERROR; // 500

HTTP_STATUS.OK; // 200

Swagger

import express from "express";
import { setupSwaggerDocs } from "api-core-backend/swagger";

const app = express();

await setupSwaggerDocs(app, {
  path: "/api-docs",
  title: "My API",
  version: "1.0.0",
  description: "REST API documentation",
  servers: [{ url: "http://localhost:3000" }],
  tags: [{ name: "Users" }],
});

Create a spec without mounting Express:

import { createSwaggerSpec } from "api-core-backend/swagger";

const spec = createSwaggerSpec({
  title: "My API",
  version: "1.0.0",
  servers: [{ url: "http://localhost:3000" }],
});

API Reference

Main Package

| Export | Purpose | | --------------------------- | ----------------------------------------- | | response() | Standard success response with total | | successResponse() | Success response helper | | errorResponse() | Error response helper | | validationErrorResponse() | Validation error response helper | | paginatedResponse() | Paginated response helper | | getPagination() | Reads max and offset | | paginate() | Slices arrays and returns full total | | getPaginationMeta() | Builds max/offset pagination metadata | | getFilters() | Keeps allowed filter fields | | getSearch() | Reads q or search | | getSorting() | Reads sortBy and sortOrder | | asyncHandler() | Wraps async route handlers | | notFoundMiddleware | Handles unmatched routes | | errorMiddleware | Sends standard JSON errors | | configureLogger() | Sets global logger options | | logger() | Request logger middleware | | requestLogger() | Alias for logger() | | resetLoggerConfig() | Clears global logger config | | statusCode | HTTP status constants | | HTTP_STATUS | HTTP status constants |

Swagger Package

Import from api-core-backend/swagger.

| Export | Purpose | | ------------------------ | ---------------------------- | | createSwaggerSpec() | Creates an OpenAPI spec | | setupSwaggerDocs() | Mounts Swagger UI | | swaggerRoute() | Creates one route definition | | swaggerRoutes() | Creates route definitions | | swaggerSchemas | Reusable OpenAPI schemas | | swaggerQueryParameters | Reusable query parameters |

License

MIT License.

Copyright (c) 2026 Choch Kimhour.