# @janiscommerce/api

> A package for managing API from any origin

Latest version **8.3.0** (published 2026-10-22) · ISC license · 0 weekly downloads

## Install

```sh
npm install @janiscommerce/api
pnpm add @janiscommerce/api
yarn add @janiscommerce/api
bun add @janiscommerce/api
```

## Health

**Score 65/100 (B)** — status: active.

Positive: has types; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads; no esm support.

## Facts

| | |
|---|---|
| Version | 8.3.0 |
| Published | 2026-10-22 |
| First published | 2019-05-27 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | CommonJS |
| Node | >=18 |
| Dependencies | 4 |
| Unpacked size | 44.9 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 1 |
| Author | Janis |
| Maintainers | janiscommerce |

## Links

- npm: https://www.npmjs.com/package/@janiscommerce/api
- Repository: https://github.com/janis-commerce/api
- Homepage: https://github.com/janis-commerce/api#readme
- Issues: https://github.com/janis-commerce/api/issues
- npm.io page: https://npm.io/package/@janiscommerce/api

## Dependencies (4)

- [lodash.clonedeep](https://npm.io/package/lodash.clonedeep.md) ^4.5.0
- [@janiscommerce/log](https://npm.io/package/@janiscommerce/log.md) ^5.3.0
- [@janiscommerce/api-session](https://npm.io/package/@janiscommerce/api-session.md) ^3.4.0
- [@janiscommerce/superstruct](https://npm.io/package/@janiscommerce/superstruct.md) ^1.2.1

## Recent versions

- 8.3.0 (latest) — 2026-10-22
- 9.0.0-7 (next) — 2025-03-28
- 6.2.1-typed.0 (typed) — 2021-01-25
- 5.1.3 (mantainance-v5) — 2020-09-01
- 8.2.0 — 2026-07-03
- 8.1.1 — 2026-06-29
- 8.1.0 — 2025-08-20
- 9.0.0-6 — 2025-03-28
- 9.0.0-5 — 2025-03-27
- 9.0.0-4 — 2025-03-27
- 9.0.0-3 — 2025-03-27
- 9.0.0-2 — 2025-03-27
- 9.0.0-1 — 2025-03-27
- 9.0.0-0 — 2025-03-27
- 8.0.3 — 2024-06-10
- … 50 more at https://npm.io/package/@janiscommerce/api/versions

## README

# api

![Build Status](https://github.com/janis-commerce/api/workflows/Build%20Status/badge.svg)
[![Coverage Status](https://coveralls.io/repos/github/janis-commerce/api/badge.svg?branch=master)](https://coveralls.io/github/janis-commerce/api?branch=master)
[![npm version](https://badge.fury.io/js/%40janiscommerce%2Fapi.svg)](https://www.npmjs.com/package/@janiscommerce/api)

A package for managing API from any origin.


## Installation

```bash
npm install @janiscommerce/api
```

## ⚠️ Automatic logs & sensitive data

Since `8.2.0`, **API requests are logged even when there is no `clientCode` — or no session at all** (they are stored as _core logs_), instead of being discarded as before. Client-less non-`GET` endpoints (e.g. login, OAuth token, password recovery, internal/admin APIs) that were previously **not** logged now start emitting logs, which by default include the **request data, request headers and response body** — so they may start logging secrets, tokens or passwords into the trace service.

**Before upgrading, review your client-less non-`GET` endpoints** and protect them (`shouldCreateLog = false`, `excludeFieldsLogRequestData` / `shouldLogResponseBody = false`, or the `JANIS_TRACE_PRIVATE_FIELDS` env var). See the [Logging](#logging) section for the full details.

## API

This is the class you should extend to code your own APIs. You can customize them with the following methods and getters:

### get struct()
This optional getter should return a valid [struct](https://www.npmjs.com/package/superstruct). If it doesn't match the data, default http code is set to 400.

**IMPORTANT** In case you return an array, each element will be passed as an argument to struct validation (see examples below). To validate an array, use `struct.list()` instead.

### async validate()
This optional method should throw an Error in case of validation failure. It's message will be set in the response body. It's return value will be discarded.

### async process()
This method is required, and should have the logic of your API. At this point, request should be already validated. If you throw an error here, default http code is set to 500.

The following methods will be inherited from the base API Class:

### Getters

- **pathParameters**. *string array*.
Returns the path parameters of the request. For example: /store/10/schedules will generate the following path parameters: ['10']

- **headers**. *object*.
Returns the the headers of the request as a key-value object. Header keys are normalized to lowercase (HTTP header names are case-insensitive per RFC 7230), so you can always read them in lowercase regardless of the casing sent by the client (e.g. `this.headers['x-janis-page']`).

- **cookies**. *object*.
Returns the the cookies of the request as a key-value object.

- **sourceIp**. *string*.
Returns the source IP of the request, as received by the Dispatcher. It will be `undefined` if not received.

### Setters

All this setters are chainable!

* **setCode(code)**.
Set a response httpCode. `code` must be a integer. This will prevent default http codes to be set.

* **setHeader(headerName, headerValue)**.
Set an individual response header. `headerName` must be a string.

* **setHeaders(headers)**.
Set response headers. `headers` must be an object with "key-value" headers.

* **setCookie(cookieName, cookieValue)**.
Set an individual response cookie. `cookieName` must be a string.

* **setCookies(cookies)**.
Set response cookies. `cookies` must be an object with "key-value" cookies.

* **setBody(body)**.
Set the response body.

## Dispatcher

This is the class you should use to dispatch your APIs. It takes the request data as constructor arguments and then finds you API file based on the endpoint and executes it.

### constructor(request)
The request must be an object and can be setup using the following properties:

* endpoint {string} **required** The API endpoint called
* method {string} The HTTP Method used in the request. Default: `'get'`.
* data {mixed} The data received in the API (query string or request body). Default: `{}`.
* headers {object} A key-value object containing the request headers. Default: `{}`.
* cookies {object} A key-value object containing the request cookies. Default: `{}`.
* authenticationData {object} An object containing the request authentication data (see [Session injection](#session-injection)). Default: `{}`.
* sourceIp {string} The source IP of the request. Default: `undefined`.

### async dispatch()
This will dispatch the API. It resolves to an object with the API execution result, with the following properties:

* code {number} The return http code. Default: `200`.
* body {mixed} The response body
* headers {object} A key-value object containing the response headers
* cookies {object} A key-value object containing the response cookies


## Logging

APIs are automatically logged to the trace service using [`@janiscommerce/log`](https://www.npmjs.com/package/@janiscommerce/log). Every non-`GET` request is logged by default (`GET` requests are not). Each log is stored either as a **client log** (tied to the request's `clientCode`) or, when there is no `clientCode` — or no session at all — as a **core log** (a log not tied to any client).

> **⚠️ Sensitive data**
> Logs include the request data, request headers and response body by default. Since client-less requests are now logged (as core logs) instead of being discarded, review your client-less non-`GET` endpoints so they don't log secrets, tokens or passwords. Use the `excludeFieldsLog*` getters below and/or the `JANIS_TRACE_PRIVATE_FIELDS` env var.

The automatic logs can be customized with the following getters:

- **shouldCreateLog**. *boolean*.
Determines if the api execution should be logged or not.
When the HTTP Method is **GET** the *default* is `false` and `true` otherwise.

- **shouldLogRequestData**. *boolean*.
Determines if the api request data should be logged or not.

- **shouldLogRequestHeaders**. *boolean*.
Determines if the api request headers should be logged or not.

- **shouldLogResponseBody**. *boolean*.
Determines if the api response body should be logged or not.

- **shouldLogAsCore**. *boolean*.
Determines if the api request log should be saved as a **core log** (a log not tied to any client) instead of a client log. *Default* is `false`.
Independently of this getter, a request log is also saved as a core log when there is no `clientCode` (or no session at all), instead of being discarded as before.

- **excludeFieldsLogRequestData**. *string array*.
Returns the fields to exclude from the api request data passing simple fields or specific paths to such fields.

- **excludeFieldsLogResponseBody**. *string array*.
Returns the fields to exclude from the api response data passing simple fields or specific paths to such fields.

ℹ️ **Note**:
- The wildcard `*` in the field path of the `excludeFieldsLogRequestData` or `excludeFieldsLogResponseBody` static getter, is used to access properties inside arrays of one level or nested arrays.
- The wildcard `**` in the field path of the `excludeFieldsLogRequestData` or `excludeFieldsLogResponseBody` static getter, can be used when the intermediate field path is unknown between the root and the field to exclude.
- The `excludeFieldsLogRequestData` or `excludeFieldsLogResponseBody` static getter can have both field names and field paths.

⚠️ **Warning**:
- When using the wildcard `*` alone in the field path of the `excludeFieldsLogRequestData` or `excludeFieldsLogResponseBody` static getter, it will exclude all the fields in the log.
- In case the field path is incorrect, it will not exclude any field.

### Masking sensitive fields globally: `JANIS_TRACE_PRIVATE_FIELDS`

The per-API `excludeFieldsLog*` getters above **remove** the listed fields from a specific API's log. As a service-wide alternative, [`@janiscommerce/log`](https://www.npmjs.com/package/@janiscommerce/log#env-variables) reads the `JANIS_TRACE_PRIVATE_FIELDS` env var and **recursively masks** the listed fields with `***` in **every** log the service emits (comma-separated, e.g. `JANIS_TRACE_PRIVATE_FIELDS=password,token`). Use it as a service-wide safety net for sensitive fields.

## APIError

Every error handled by this package will be an instance of this class. You might find more information about the error source in the `previousError` property.

It also uses the following error codes:

| Name | Value | Description |
| --- | --- | --- |
| Invalid request data | 1 | The request parameters received are not an object |
| Invalid endpoint | 2 | The request endpoint received is not a string |
| Invalid method | 3 | The request method received is not a string |
| Invalid headers | 4 | The request headers received are not an object |
| Invalid cookies | 5 | The request cookies received are not an object |
| API not found | 6 | The endpoint does not correspond to an API file. This sets the default http code to 404 |
| Invalid API | 7 | The API does not inherit from API class or does not implement the `process` method |
| Invalid struct | 8 | The request data does not match the API struct |
| Invalid authentication data | 9 | The request authentication data received is not an object |
| Invalid source IP | 10 | The request source IP received is not a string |


## ErrorWithVariables

This is an error class to use to return easily a message and variables apply to it, for example to handle error translation properly.

Every variable should be a scalar (not an array or object). Otherwise it will be casted to a string.

```js
const { ErrorWithVariables } = require('@janiscommerce/api');

throw new ErrorWithVariables('some.error.to.translate', {
	name: 'Some name to replace in translation'
})
```

## Session injection
*Since 2.0.0*

This package implements [API Session](https://www.npmjs.com/package/@janiscommerce/api-session). In order to associate a request to a session, you must pass a valid authentication data in the `authenticationData` property of the Dispatcher constructor.

Session details and customization details can be found in api-session README.

## API Examples

### Basic API

```js
'use strict';

const { API } = require('@janiscommerce/api');

class MyApi extends API {

	async process() {
		this.setBody({
			message: 'Success'
		});
	}

}

module.exports = MyApi;
```

### API with struct and custom validation

```js
'use strict';

const { API } = require('@janiscommerce/api');

class MyApi extends API {

	get struct() {
		return {
			foo: 'string'
		};
	}

	async validate() {
		if(!this.data.foo.match(/(bar)+/))
			throw new Error('Foo must be one or more bars');
	}

	async process() {
		this.setBody({
			message: 'Success'
		});
	}

}

module.exports = MyApi;
```

### API with struct with defaults

```js
'use strict';

const { API } = require('@janiscommerce/api');

class MyApi extends API {

	get struct() {
		return [{
			foo: 'string?'
		}, {
			// Defaults
			foo: 'bar'
		}];
	}

	async process() {
		this.setBody({
			message: 'Success'
		});
	}

}

module.exports = MyApi;
```

### API with custom validation http status

```js
'use strict';

const { API } = require('@janiscommerce/api');

const UserValidator = require('user-validator');

class MyApi extends API {

	get struct() {
		return {
			userId: 'number'
		};
	}

	async validate() {
		if(!UserValidator.isValidId(this.data.userId)) {
			this.setCode(401);
			throw new Error('Unauthorized');
		}
	}

	async process() {
		this.setBody({
			message: 'Success'
		});
	}

}

module.exports = MyApi;
```

### API with custom response http code and headers

```js
'use strict';

const { API } = require('@janiscommerce/api');

class MyApi extends API {

	async process() {
		this
			.setHeader('x-foo', 'bar')
			.setCode(201)
			.setBody({
				message: 'Created'
			});
	}

}

module.exports = MyApi;
```

### API injected session

```js
'use strict';

const { API } = require('@janiscommerce/api');

const UserValidator = require('user-validator');

class MyApi extends API {

	get struct() {
		return {
			userId: 'number'
		};
	}

	async validate() {

		const userValidator = this.session.getSessionInstance(UserValidator);

		if(!userValidator.isValidId(this.data.userId)) {
			this.setCode(401);
			throw new Error('Unauthorized');
		}
	}

	async process() {
		this.setBody({
			message: 'Success'
		});
	}

}

module.exports = MyApi;
```

## Dispatcher Examples

### Full request dispatcher

```js
'use strict';

const { Dispatcher } = require('@janiscommerce/api');

const dispatcher = new Dispatcher({
	endpoint: 'store/10/schedules',
	method: 'get',
	data: { status: 'active' },
	headers: { 'Content-Type': 'application/json' },
	cookies: { 'my-cookie': 'cookie-value' },
	sourceIp: '203.0.113.10',
	authenticationData: { userId: 10, clientCode: 'janiscommerce' }
});

const response = await dispatcher.dispatch();
```

## Serverless, REST APIs and unit testing

To implement predictable REST APIs, there are a couple packages that extend this one:

* [List APIs](https://www.npmjs.com/package/@janiscommerce/api-list): To implement resource listing APIs, with standarized sorting, pagination, filtering and responses.
* [Get APIs](https://www.npmjs.com/package/@janiscommerce/api-get): To implement one-resource get APIs with ease.
* [Save APIs](https://www.npmjs.com/package/@janiscommerce/api-save): To implement one-resource create/update APIs with ease, with standarized validation and

To implement this on serverless, there is a REST API handler that can be used out-of-the-box:

* [Serverless REST API](https://www.npmjs.com/package/@janiscommerce/sls-api-rest): To implement API dispatching in AWS Api Gateway + Lambda with ease.

To implement unit tests in your APIs, there is a also a package:

* [API Test](https://www.npmjs.com/package/@janiscommerce/api-test): To run unit tests on your APIs like a boss.

---
_Source: https://npm.io/package/@janiscommerce/api · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
