Routes
API endpoints for your module, with permissions, rate limits and signed responses.
A route is an endpoint on the API port. Put each one in its own folder under src/routes/ as route.js.
import { Routes } from "#prism/modules";
export class ItemsRoute extends Routes {
constructor(prism) {
super(prism, {
name: "items",
path: "/items",
permissions: { GET: "example.view", POST: "example.edit" }
});
}
async GET(request) {
const items = this.prism.modules.models.get(this.module, "example_items");
return items.page(items.query(request.query));
}
async POST(request) {
const created = await this.prism.modules.handlers.get(this.module, "items").create(request.body.identifier, request.body.name);
await this.audit(request, "items.create", created.id);
return created;
}
}This answers GET and POST on /api/modules/example/items.
Add a method for each HTTP verb you need: GET, POST, PUT, PATCH or DELETE. Whatever it returns is sent as JSON. Returning nothing sends 204. A thrown error with a status, such as from fail(), is sent as that status with { error }.
Options
| Key | Default | What it does |
|---|---|---|
name | Required | The route name. |
path | Required | The path. Supports Express parameters such as /items/:id. |
absolute | false | Mount path as is instead of under /api/modules/<module>. Use it for public /api/v1/ endpoints. |
aliases | [] | Extra full paths for the same route, such as an /api/v1/ path for API tokens. |
access | private | public lets anyone call it. private needs a signed-in session or an API token. |
permissions | {} | One permission for every method, or one per method. |
limit | None | Rate limit per IP, such as { window: 60, max: 30 } for 30 a minute. |
raw | false | Keep the raw body on request.rawBody, for webhooks that check a signature. |
When the module is turned off, its routes answer 404. Routes with fewer :params are mounted first, so /items/key wins over /items/:id.
Public Endpoints
Endpoints that products, the store or an SDK read are public, versioned and signed:
export class CatalogRoute extends Routes {
constructor(prism) {
super(prism, { name: "catalog", path: "/api/v1/example/items", absolute: true, access: "public", limit: { window: 60, max: 120 } });
}
async GET(request, response) {
const items = this.prism.modules.models.get(this.module, "example_items");
this.signed(response, this.listing(request, "items", await items.page(items.query(request.query))));
}
}The site on the web port forwards every /api/v1/ request to the API, so these work on the store's own address.
Helpers
| Helper | What it does |
|---|---|
this.can(request, permission) | Whether the caller has the permission. |
this.require(request, permission) | Throws 403 if they do not. |
this.actor(request) | Who is calling. See Actors. |
this.ip(request) | The caller's IP, behind proxies too. |
this.audit(request, action, target, detail) | Adds an entry to the audit log. |
this.signed(response, data, status) | Sends data with an x-prism-signature header. |
this.listing(request, name, page) | Wraps a page() result as { success, total, [name] } on /api/v1/ paths. |
this.versioned(request) | Whether the request came in on an /api/v1/ path. |
Signed Responses
async GET(request, response) {
this.signed(response, { success: true, item });
}signed sends the response itself, so return nothing after it. Clients check the header against GET /api/v1/public-key. See Signed Responses.