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

@transformgovsg/zing

v0.4.0

Published

A lightweight web framework.

Readme

Zing

A lightweight HTTP framework for Node.js.

🚧 Roadmap

  • [x] ~~Cookies~~
  • [x] ~~Middleware~~
  • [ ] Server-sent events
  • [ ] Header, query, and body schema validation

📦 Install

pnpm add @transformgovsg/zing

Alternatively, using yarn or npm:

yarn add @transformgovsg/zing
npm install @transformgovsg/zing

🚀 Usage

import Zing, { HTTPStatusCode } from '@transformgovsg/zing';

const app = new Zing();

// Simplest route.
app.get('/', (_, res) => {
  res.ok();
});

// Route with a dynamic parameter.
app.get('/hello/:name', (req, res) => {
  res.text(HTTPStatusCode.OK, `Dynamic parameter: ${req.param('name')}!`);
});

// Route with a catch-all parameter.
app.get('/hello/*name', (req, res) => {
  res.text(HTTPStatusCode.OK, `Catch-all parameter: ${req.param('name')}!`);
});

await app.listen(8080);

process.on('SIGTERM', async () => {
  await app.shutdown();
});

process.on('SIGINT', async () => {
  await app.shutdown();
});

🔌 API

Zing.constructor()

Creates a new Zing application.

Type

constructor(options?: Partial<Options>);

Parameters

  • options - The options for the Zing application.
    • maxBodySize - The maximum size of the body of a request. Default: 1_048_576.

Example

const app = new Zing();

With custom options:

const app = new Zing({ maxBodySize: 4 * 1024 }); // Limit to 4KB.

⬆️ Back to top

Zing.listen()

Starts the HTTP server and listens on the given port for incoming requests.

Type

listen(port?: number): Promise<void>;

Parameters

  • port - The port to listen on. Default: 8080.

Example

await app.listen(); // Listen on port 8080.
await app.listen(8123); // Listen on port 8123.

⬆️ Back to top

Zing.shutdown()

Shuts down the HTTP server.

Type

shutdown(timeout?: number): Promise<void>;

Parameters

  • timeout - The time in milliseconds to wait for active requests to finish before forcefully shutting down the HTTP server. Default: 10000.

Example

await app.shutdown(); // Shutdown the HTTP server after 10 seconds.
await app.shutdown(5000); // Shutdown the HTTP server after 5 seconds.

⬆️ Back to top

Zing.route()

Adds a route to the application.

Type

route(method: 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS', pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • method - The HTTP method to match against the request method.
  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.route('GET', '/', (req, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.route('GET', '/', logger, (req, res) => {
  res.ok();
});

⬆️ Back to top

Zing.get()

Adds a GET route to the application.

Type

get(pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.get('/', (_, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.get('/', logger, (_, res) => {
  res.ok();
});

⬆️ Back to top

Zing.head()

Adds a HEAD route to the application.

Type

head(pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.head('/', (_, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.head('/', logger, (_, res) => {
  res.ok();
});

⬆️ Back to top

Zing.patch()

Adds a PATCH route to the application.

Type

patch(pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.patch('/', (_, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.patch('/', logger, (_, res) => {
  res.ok();
});

⬆️ Back to top

Zing.post()

Adds a POST route to the application.

Type

post(pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.post('/', (_, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.post('/', logger, (_, res) => {
  res.ok();
});

⬆️ Back to top

Zing.put()

Adds a PUT route to the application.

Type

put(pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.put('/', (_, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.put('/', logger, (_, res) => {
  res.ok();
});

⬆️ Back to top

Zing.delete()

Adds a DELETE route to the application.

Type

delete(pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.delete('/', (_, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.delete('/', logger, (_, res) => {
  res.ok();
});

⬆️ Back to top

Zing.options()

Adds a OPTIONS route to the application.

Type

options(pattern: string, ...args: [...Middleware[], Handler]): void;

Parameters

  • pattern - The pattern to match against the request pathname.
  • args - The middleware and handler to call when the route is matched.

Throws

  • Error - If the pattern is invalid.

Example

app.options('/', (_, res) => {
  res.ok();
});

With middleware:

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.options('/', logger, (_, res) => {
  res.ok();
});

⬆️ Back to top

Zing.use()

Adds an application-level middleware to be called for each incoming request regardless of whether it matches a route or not.

Type

use(...middleware: Middleware[]): void;

Parameters

  • middleware - The middleware to be called for each request.

Example

function logger(next: Handler) {
  return async (req, res) => {
    console.log(`${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.use(logger);

Middleware with configuration:

function logger(options: { prefix: string }): Middleware {
  return (next) => async (req, res) => {
    console.log(`${options.prefix} ${req.method} ${req.pathname}`);
    await next(req, res);
  };
}

app.use(logger({ prefix: '[Zing] ' }));

⬆️ Back to top

Zing.set404Handler()

Sets the handler to call when no route is matched.

By default, Zing has a default 404 handler that sends a 404 Not Found response. If you want to customize the response, you can set your own 404 handler.

Type

set404Handler(handler: Handler): void;

Parameters

  • handler - The handler to call when no route is matched.

Example

app.set404Handler((_, res) => {
  res.json(404, { message: 'Not Found' });
});

⬆️ Back to top

Zing.setErrorHandler()

Sets the handler to call when an error occurs.

By default, Zing has a default error handler that sends a 500 Internal Server Error response. The default error handler also handles a few Zing specific errors:

  • ContentTooLargeError
    • When the request body is too large.
    • Sends a 413 Content Too Large response.
  • UnsupportedContentTypeError
    • When trying to parse a request body with an unsupported content type.
    • Sends a 415 Unsupported Media Type response.
  • MalformedJSONError
    • When trying to parse a request body as JSON but the body is not valid JSON.
    • Sends a 422 Unprocessable Content response.
  • InternalServerError
    • When an unknown error occurs.
    • Sends a 500 Internal Server Error response.

If you want to customize the response, you can set your own error handler.

Type

setErrorHandler(handler: ErrorHandler): void;

Parameters

  • handler - The handler to call when an error occurs.

Example

app.setErrorHandler((_err, _req, res) => {
  res.json(500, { message: 'Internal Server Error' });
});

If you just want to handle a few specific errors, you can do something like the following:

app.setErrorHandler((err, _req, res) => {
  if (err instanceof XXXError) {
    res.json(422, { message: 'Unprocessable Content' });
    return;
  }

  // Rethrow the error so that it can be handled by the default error handler.
  throw err;
});

⬆️ Back to top

Request

Request.node

Returns the underlying Node.js request object.

Type

node: IncomingMessage;

⬆️ Back to top

Request.protocol

Returns the protocol of the request.

Type

protocol: 'http' | 'https';

Example

app.get('/', (req, res) => {
  const protocol = req.protocol;

  res.ok();
});

⬆️ Back to top

Request.pathname

Returns the pathname of the request.

Type

pathname: string;

Example

app.get('/', (req, res) => {
  const pathname = req.pathname;
});

⬆️ Back to top

Request.method

Returns the method of the request.

Type

method: 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT' | 'DELETE' | 'OPTIONS';

Example

app.get('/', (req, res) => {
  const method = req.method;
});

⬆️ Back to top

Request.get()

Returns the value of the given key from the request-scoped key-value store. If the key is not found and no default value is provided, null is returned.

Type

get<T = unknown>(key: string, defaultValue?: T): T | null;

Parameters

  • key - The key to get the value of.
  • defaultValue - The default value to return if the key is not found.

Example

app.get('/', (req, res) => {
  const value = req.get('key', 'defaultValue');
});

⬆️ Back to top

Request.set()

Stores a value in the request-scoped key-value store.

Type

set(key: string, value: unknown): void;

Parameters

  • key - The key to store the value under.
  • value - The value to store.

Example

app.get('/', (req, res) => {
  req.set('key', 'value');
});

⬆️ Back to top

Request.cookie()

Returns the value of the given cookie name from the request. If the cookie is not found and no default value is provided, null is returned.

Type

cookie(name: string, defaultValue?: string): string | null;

Parameters

  • name - The name of the cookie to get the value of.
  • defaultValue - The default value to return if the cookie is not found.

Example

app.get('/', (req, res) => {
  const value = req.cookie('name', 'defaultValue');
});

⬆️ Back to top

Request.param()

Returns the value of the given parameter name from the request. If the parameter is not found and no default value is provided, null is returned.

Type

param(name: string, defaultValue?: string): string | null;

Parameters

  • name - The name of the parameter to get the value of.
  • defaultValue - The default value to return if the parameter is not found.

Example

app.get('/', (req, res) => {
  const value = req.param('name', 'defaultValue');
});

⬆️ Back to top

Request.query()

Returns the value of the first occurrence of the given query name from the request. If the query is not found and no default value is provided, null is returned.

Type

query(name: string, defaultValue?: string): string | null;

Parameters

  • name - The name of the query to get the value of.
  • defaultValue - The default value to return if the query is not found.

Example

app.get('/', (req, res) => {
  const value = req.query('name', 'defaultValue');
});

⬆️ Back to top

Request.queries()

Returns the values of all occurrences of the given query name from the request. If the query is not found and no default value is provided, an empty array is returned.

Type

queries(name: string, defaultValue?: string[]): string[];

Parameters

  • name - The name of the query to get the values of.
  • defaultValue - The default value to return if the query is not found.

Example

app.get('/', (req, res) => {
  const values = req.queries('name', ['defaultValue1', 'defaultValue2']);
});

⬆️ Back to top

Request.header()

Returns the value of the first occurrence of the given header name from the request. If the header is not found and no default value is provided, null is returned.

Type

header(name: string, defaultValue?: string): string | null;

Parameters

  • name - The name of the header to get the value of.
  • defaultValue - The default value to return if the header is not found.

Example

app.get('/', (req, res) => {
  const value = req.header('name', 'defaultValue');
});

⬆️ Back to top

Request.body()

Returns the body of the request or null if the HTTP method is not PATCH, POST, or PUT.

Type

async body(): Promise<Result<Uint8Array | null, ContentTooLargeError | InternalServerError>>;

Example

app.get('/', async (req, res) => {
  const result = await req.body();

  if (result.isErr()) {
    res.json(500, { message: 'Internal Server Error' });
    return;
  }

  const body = result.value; // The body of the request.
});
app.get('/', async (req, res) => {
  const result = await req.body();

  const body = result.unwrap(); // The body of the request or throws an error if the result is an error.
  const body = result.unwrapOr(null); // The body of the request or `null` if the result is an error.

⬆️ Back to top

Request.text()

Returns the body of the request as a string or null if the HTTP method is not PATCH, POST, or PUT.

Type

async text(): Promise<Result<string | null, ContentTooLargeError | InternalServerError | UnsupportedContentTypeError>>;

Example

app.get('/', async (req, res) => {
  const result = await req.text();

  if (result.isErr()) {
    res.json(500, { message: 'Internal Server Error' });
    return;
  }

  const text = result.value; // The body of the request as a string.
});
app.get('/', async (req, res) => {
  const result = await req.text();

  const text = result.unwrap(); // The body of the request as a string or throws an error if the result is an error.
  const text = result.unwrapOr(null); // The body of the request as a string or `null` if the result is an error.

⬆️ Back to top

Request.json()

Returns the body of the request as a JSON object or null if the HTTP method is not PATCH, POST, or PUT.

Type

async json<T = unknown>(): Promise<Result<T | null, ContentTooLargeError | InternalServerError | UnsupportedContentTypeError | MalformedJSONError>>;

Example

app.get('/', async (req, res) => {
  const result = await req.json();

  if (result.isErr()) {
    res.json(500, { message: 'Internal Server Error' });
    return;
  }

  const json = result.value; // The body of the request as a JSON object.
});
app.get('/', async (req, res) => {
  const result = await req.json();

  const json = result.unwrap(); // The body of the request as a JSON object or throws an error if the result is an error.
  const json = result.unwrapOr(null); // The body of the request as a JSON object or `null` if the result is an error.

⬆️ Back to top

Response

Response.node

Returns the underlying Node.js response object.

Type

node: ServerResponse;

⬆️ Back to top

Response.finished

Returns true if the response has been sent, otherwise false.

Type

finished: boolean;

Example

app.get('/', (req, res) => {
  const finished = res.finished;
});

⬆️ Back to top

Response.status()

Sets the status code of the response.

Type

status(code: (typeof HTTPStatusCode)[keyof typeof HTTPStatusCode]): void;

Parameters

  • code - The status code to set.

Example

app.get('/', (req, res) => {
  res.status(200);
});

⬆️ Back to top

Response.ok()

Sets the status code of the response to 200.

Type

ok(): void;

Example

app.get('/', (req, res) => {
  res.ok();
});

⬆️ Back to top

Response.cookie()

Sets a cookie on the response.

Type

cookie(name: string, value: string, options?: CookieOptions): void;

Parameters

  • name - The name of the cookie to set.
  • value - The value of the cookie to set.
  • options - The options for the cookie.

Example

app.get('/', (req, res) => {
  res.cookie('name', 'value', {
    path: '/',
    maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days
  });
});

⬆️ Back to top

Response.json()

Sets the status code of the response to the given status code and the body to the given JSON object. It will automatically set the Content-Type and Content-Length headers.

Type

json(code: (typeof HTTPStatusCode)[keyof typeof HTTPStatusCode], data: JSONObject): void;

Parameters

  • code - The status code to set.
  • data - The JSON object to set the body to.

Example

app.get('/', (req, res) => {
  res.json(200, { message: 'Hello, world!' });
});

⬆️ Back to top

Response.text()

Sets the status code of the response to the given status code and the body to the given string. It will automatically set the Content-Type and Content-Length headers.

Type

text(code: (typeof HTTPStatusCode)[keyof typeof HTTPStatusCode], data: string): void;

Parameters

  • code - The status code to set.
  • data - The string to set the body to.

Example

app.get('/', (req, res) => {
  res.text(200, 'Hello, world!');
});

⬆️ Back to top

Response.header()

Sets the value of the given header key on the response.

Type

header<Key extends Exclude<HTTPHeaderKey, 'set-cookie'>>(key: Key, value: HTTPHeaderValue<Key>): void;

Parameters

  • key - The key of the header to set.
  • value - The value of the header to set.

Example

app.get('/', (req, res) => {
  res.header('Content-Type', 'application/json');
});

⬆️ Back to top

📜 License

This project is licensed under the MIT License.