# Routes

URL: https://zerodev.ca/docs/kora/developer-documentation/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`.

```js title="modules/example/src/routes/items/route.js"
import { Routes } from "#kora/modules";

export class ItemsRoute extends Routes {
    constructor(kora) {
        super(kora, {
            name: "items",
            path: "/items",
            permissions: { GET: "example.view", POST: "example.edit" }
        });
    }

    async GET(request) {
        const items = this.kora.modules.models.get(this.module, "example_items");
        return items.page(items.query(request.query));
    }

    async POST(request) {
        const created = await this.kora.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.                                                                 |
| `access`      | `private` | `public` lets anyone call it. `private` needs a dashboard session or an API token.                   |
| `permissions` | `{}`      | A 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`   | Skip JSON body parsing, for webhooks that need the raw body.                                         |

When the module is turned off, its routes answer `404`.

## 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. `kind` is `staff`, `token`, `public` or `system`.            |
| `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-kora-signature` header.                              |
| `this.listing(request, name, page)`           | Wraps a `page()` result as `{ success, total, [name] }` on `/api/v1/` paths. |

## Signed Responses

Anything your product reads to make a licensing decision should be signed, so it cannot be faked:

```js
async POST(request, response) {
    this.signed(response, { valid: true, timestamp: Date.now() });
}
```

`signed` sends the response itself, so return nothing after it. The SDKs check the header with the public key. See [Signed Responses](/docs/kora/extensions/core#signed-responses).
