SDK
A lightweight, typed HTTP client for consuming the dnax REST API from any JavaScript or TypeScript environment (browser, Node.js, Bun, etc.).
Installation
bun add @dnax/[email protected]
Quick Start
import { Rest } from '@dnax/sdk';
const api = new Rest({
server: 'http://localhost:5000',
tenant: 'v1',
});
const users = await api.find('users');
Constructor
const api = new Rest(options: RestClientOptions);
| Option | Type | Default | Description |
|---|---|---|---|
server | string | — | Base URL of the dnax server (required) |
tenant | string | — | Tenant ID (required) |
headers | Record<string, string> | {} | Custom headers sent with every request |
token | { persist?: boolean; storageKey?: string } | — | JWT persistence in localStorage |
token.persist | boolean | true | Save the JWT token in localStorage after login |
token.storageKey | string | "dnax_token" | Key used in localStorage for the token |
const api = new Rest({
server: 'https://api.example.com',
tenant: 'v1',
headers: {
'X-Custom-Header': 'value',
},
token: {
persist: true,
storageKey: 'my_app_token',
},
});
token.persist is true (default) and a token is found in localStorage at construction, it is automatically loaded into the Authorization header.Authentication
login
Authenticate a user and store the JWT token. The token is automatically attached to all subsequent requests.
const { token, data } = await api.login('users', {
email: '[email protected]',
password: 'secret',
});
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection with auth enabled |
payload | Record<string, unknown> | Login credentials |
options? | RestRequestOptions | Request options |
Returns: { token: string, data: any }
logout
await api.logout('users');
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection with auth enabled |
payload? | Record<string, unknown> | Optional payload |
options? | RestRequestOptions | Request options |
getToken
Retrieve the current JWT token:
const token = api.getToken(); // string | undefined
clearToken
Remove the token from memory and localStorage:
api.clearToken();
setHeader
Set or remove a custom header:
api.setHeader('X-Custom', 'value');
api.setHeader('X-Custom', undefined); // removes it
CRUD Methods
All CRUD methods target a collection and send a POST request to the dnax unified API.
find
Retrieve multiple documents.
const users = await api.find('users', {
$match: { activated: true },
$sort: { createdAt: -1 },
$limit: 20,
});
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
params | Record<string, unknown> | Query parameters ($match, $sort, $limit, $skip, $include, etc.) |
options? | RestRequestOptions | Request options |
Returns: T[]
findOne
Retrieve a single document by ID.
const user = await api.findOne('users', '64f1a2b3c4d5e6f7a8b9c0d1');
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
id | string | Document ID |
params | Record<string, unknown> | Query parameters |
options? | RestRequestOptions | Request options |
Returns: T | null
insertOne
Create a single document.
const newUser = await api.insertOne('users', {
name: 'Jane Doe',
email: '[email protected]',
});
// newUser._id is available
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
data | TBody | Document data |
options? | RestRequestOptions | Request options |
Returns: T & { _id: string }
insertMany
Create multiple documents at once.
const products = await api.insertMany('products', [
{ name: 'Widget A', price: 9.99 },
{ name: 'Widget B', price: 14.99 },
]);
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
data | TBody[] | Array of documents |
options? | RestRequestOptions | Request options |
Returns: (T & { _id: string })[]
updateOne
Update a single document by ID.
await api.updateOne('users', '64f1a2b3c4d5e6f7a8b9c0d1', {
$set: { name: 'Jane Smith' },
});
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
id | string | Document ID |
update | TUpdate | Update payload |
options? | RestRequestOptions | Request options |
updateMany
Update multiple documents by IDs.
await api.updateMany('users', ['id1', 'id2', 'id3'], {
$set: { activated: true },
});
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
ids | string[] | Document IDs |
update | TUpdate | Update payload |
options? | RestRequestOptions | Request options |
deleteOne
Delete a single document by ID.
await api.deleteOne('users', '64f1a2b3c4d5e6f7a8b9c0d1');
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
id | string | Document ID |
options? | RestRequestOptions | Request options |
deleteMany
Delete multiple documents by IDs.
await api.deleteMany('users', ['id1', 'id2', 'id3']);
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
ids | string[] | Document IDs |
options? | RestRequestOptions | Request options |
Advanced Methods
aggregate
Run a MongoDB aggregation pipeline.
const stats = await api.aggregate('orders', [
{ $match: { status: 'completed' } },
{ $group: { _id: '$customerId', total: { $sum: '$amount' } } },
{ $sort: { total: -1 } },
]);
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
pipeline | unknown[] | Aggregation pipeline stages |
options? | RestRequestOptions | Request options |
Returns: T[]
runAction
Call a custom action defined on a collection.
const result = await api.runAction('orders', 'processOrder', {
orderId: '64f1a2b3c4d5e6f7a8b9c0d1',
});
| Parameter | Type | Description |
|---|---|---|
collection | string | Collection name |
action | string | Custom action name |
data? | unknown | Payload passed to the action |
options? | RestRequestOptions | Request options |
runService
Appelle un service enregistré dans la config serveur (cfg.services), sur la route POST /services/:tenant/:service/:action (pas sur /api/...).
const report = await api.runService('analytics', 'generateReport', {
from: '2025-01-01',
to: '2025-12-31',
});
| Parameter | Type | Description |
|---|---|---|
service | string | Nom du service (ex. analytics, comme Service.name côté config) |
action | string | Nom de l’action exposée dans service.actions |
data? | unknown | Corps envoyé dans { data: … } (accessible côté handler via body.data) |
options? | RestRequestOptions | En-têtes, signal, query, cleanDeep, etc. |
Tenant : celui passé au constructeur new Rest({ server, tenant, ... }) ; il apparaît dans l’URL après /services/.
Request Options
Every method accepts an optional RestRequestOptions object as its last parameter.
type RestRequestOptions = {
headers?: Record<string, string>;
signal?: AbortSignal;
query?: RestQueryOptions;
cleanDeep?: boolean;
};
| Option | Type | Description |
|---|---|---|
headers | Record<string, string> | Extra headers for this request only |
signal | AbortSignal | Abort signal to cancel the request |
query | RestQueryOptions | URL query parameters |
cleanDeep | boolean | Strip null, undefined, empty arrays, and empty objects from the body before sending |
AbortSignal
Cancel a long-running request:
const controller = new AbortController();
api.find('users', {}, { signal: controller.signal });
setTimeout(() => controller.abort(), 5000);
cleanDeep
Automatically remove empty values from the request body:
await api.insertOne('users', {
name: 'Jane',
nickname: null,
tags: [],
meta: {},
}, { cleanDeep: true });
// Body sent: { name: 'Jane' }
Error Handling
When the server returns a non-2xx response, the SDK throws an Error with extra properties:
try {
await api.findOne('users', 'invalid-id');
} catch (err) {
console.log(err.message); // "Document not found"
console.log(err.status); // 404
console.log(err.code); // "DOCUMENT_NOT_FOUND"
console.log(err.meta); // additional info from server
}
| Property | Type | Description |
|---|---|---|
message | string | Error message from the server |
status | number | HTTP status code |
code | string | Application error code |
meta | any | Optional metadata |
Full Example
import { Rest } from '@dnax/sdk';
const api = new Rest({
server: 'http://localhost:5000',
tenant: 'v1',
});
// Login
const { token } = await api.login('users', {
email: '[email protected]',
password: 'secret',
});
// Create
const product = await api.insertOne('products', {
name: 'T-Shirt',
price: 29.99,
category: 'clothing',
});
// Read
const products = await api.find('products', {
$match: { category: 'clothing' },
$sort: { price: 1 },
$limit: 10,
});
// Update
await api.updateOne('products', product._id, {
$set: { price: 24.99 },
});
// Delete
await api.deleteOne('products', product._id);
// Custom action
await api.runAction('orders', 'processOrder', { orderId: '...' });
// Aggregate
const stats = await api.aggregate('orders', [
{ $group: { _id: '$status', count: { $sum: 1 } } },
]);
// Logout
await api.logout('users');