> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aikeedo.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing and middleware

> How Aikeedo maps requests to handlers with attributes, assembles middleware pipelines, and turns exceptions into responses.

Routes aren't declared in a file. Each request handler carries attributes describing its path, method and middleware, and the router discovers them by scanning `src/` plus any directory a plugin or theme registers.

## Discovery

At bootstrap, an attribute mapper scans the source directory for classes with route attributes. The result is cached when `CACHE=true` and `DEBUG=false`.

```php theme={null}
use Easy\Router\Mapper\AttributeMapper;

$mapper->addPath(__DIR__);
```

That's also how plugins and themes add their own handlers. See [Routes and request handlers](/development/plugins/routes-and-request-handlers).

<Warning>
  A new route on a cached installation isn't visible until the cache is cleared. Development should run with `CACHE=false`.
</Warning>

## Attributes

```php theme={null}
#[Path('/notes')]
#[Route(path: '/', method: RequestMethod::GET)]
#[Route(path: '/[uuid:id]', method: RequestMethod::PATCH, priority: Priority::HIGH)]
#[Middleware(SomeMiddleware::class)]
```

| Attribute | Effect |
| - | - |
| `Easy\Router\Attributes\Route` | Declares a path, an HTTP method and an optional priority. Repeatable. |
| `Easy\Router\Attributes\Path` | A prefix for the class and everything extending it. |
| `Easy\Router\Attributes\Middleware` | Adds PSR-15 middleware, outermost first. Repeatable. |

### Path syntax

| Pattern | Matches |
| - | - |
| `/notes` | Exactly that path |
| `/[:slug]` | One segment, exposed as the `slug` attribute |
| `/[uuid:id]` | A UUID segment |
| `/[images\|videos:type]` | One of the listed values |
| `/[locale:locale]?/pricing` | An optional locale prefix, such as `/de-DE` |

`locale` is a custom match type registered at bootstrap, matching language codes like `en-US`.

Route parameters arrive as request attributes:

```php theme={null}
$id = $request->getAttribute('id');
```

Higher priorities are matched first, which is how a specific route beats a catch-all.

## Groups

Handlers inherit their prefix and middleware from the class they extend:

| Base class | Prefix | Middleware added |
| - | - | - |
| `Presentation\RequestHandlers\AbstractRequestHandler` | none | Exception, Install, RequestBodyParser, User, Locale |
| `…\Api\Api` | `/api` | + Authorization, EmailVerification, Byok |
| `…\App\AppView` | `/app` | + Authorization, EmailVerification, View |
| `…\App\Billing\BillingView` | `/app/billing` | inherits `AppView` |
| `…\Admin\AbstractAdminRequestHandler` | `/admin` | + Authorization, admin role required |
| `…\Admin\AbstractAdminViewRequestHandler` | `/admin` | + DemoEnvironment, License, View |
| `…\Admin\Api\AdminApi` | `/admin/api` | + DemoEnvironment |

Handlers that extend none of these get a root path and no middleware at all, which is what webhook and embed endpoints use deliberately.

## The middleware pipeline

| Middleware | Responsibility |
| - | - |
| `ExceptionMiddleware` | Converts exceptions into responses, logs unhandled ones |
| `InstallMiddleware` | Routes everything to the installer while `ENVIRONMENT=install` |
| `RequestBodyParserMiddleware` | Parses JSON and form bodies into an object |
| `UserMiddleware` | Resolves the user from a token, cookie or API key, and the workspace |
| `LocaleMiddleware` | Resolves the language and loads translation catalogs |
| `AuthorizationMiddleware` | Requires an active user, and the admin role under `/admin` |
| `EmailVerificationMiddleware` | Enforces a strict verification policy |
| `ByokMiddleware` | Applies workspace-provided provider keys |
| `ViewMiddleware` | Renders a `ViewResponse` and injects template globals |
| `DemoEnvironmentMiddleware` | Blocks writes in demo mode |
| `LicenseMiddleware` | Gates admin views behind license verification |
| `CaptchaMiddleware` | Validates captcha tokens where enabled |
| `ClientIpMiddleware` | Resolves the client IP behind proxies |

Middleware from a parent class runs before a child's, so the outermost layer is always error handling.

## A request end to end

`POST /admin/api/categories`:

```mermaid theme={null}
sequenceDiagram
    participant C as Client
    participant R as Router
    participant M as Middleware
    participant H as CreateCategoryRequestHandler
    participant B as Command bus
    participant D as Domain

    C->>R: POST /admin/api/categories
    R->>M: matched handler + middleware
    M->>M: exception, install, body parser, user, locale, authorization, demo
    M->>H: handle(request)
    H->>H: validate payload
    H->>B: CreateCategoryCommand
    B->>D: handler creates entity, dispatches event
    D-->>H: CategoryEntity
    H-->>C: 201 with CategoryResource
```

After the response is emitted, the entity manager is flushed.

## Responses

| Class | Use |
| - | - |
| `Presentation\Response\JsonResponse` | JSON payloads |
| `Presentation\Response\ViewResponse` | A Twig template plus data |
| `Presentation\Response\RedirectResponse` | Redirects |
| `Presentation\Response\HtmlResponse` | Raw HTML |
| `Presentation\Response\EmptyResponse` | No body |
| `Presentation\Response\Response` | A PSR-7 response you build yourself, including streams |

## Errors

`ExceptionMiddleware` maps exceptions to responses:

| Exception | Response |
| - | - |
| `ValidationException` | `400` with `code`, `message` and `param` |
| `UnauthorizedException` | `401` JSON on API paths, a redirect to `/login` elsewhere |
| `UsageLimitException`, `InsufficientCreditsException` | `403` |
| `HttpException` and its subclasses, such as `NotFoundException` | The status code it carries, defaulting to `422` |
| `InvalidValueException` | `400` |
| Anything else | Logged with a correlation ID, `500` JSON on API paths, an empty `500` elsewhere |

With `DEBUG` on, unhandled exceptions are rethrown instead, so you see the stack trace.

## Related

* [Authentication and authorization](/development/core/authentication-and-authorization)
* [Views and frontend](/development/core/views-and-frontend)
* [Plugin routes and handlers](/development/plugins/routes-and-request-handlers)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.