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

# Middleware

> The --middleware flag, scope rules, redirects, and the host seam.

Middleware is a template that decides whether a request should reach a page at
all. It renders nothing. Its whole output is a signal: redirect somewhere else,
or carry on.

```slurp middleware/auth.slurp theme={null}
---
middleware "auth"
---
{if !request.cookies.session}
  {redirect "/login"}
{/if}
```

A page never has to ask whether it is allowed to be rendered, so a page never
has to read the request.

## Selecting middleware files

Nothing about the filename or the directory makes a file middleware. The compile
flag does:

```bash theme={null}
slurp validate --dir . --middleware middleware
slurp build --input . --output dist --middleware middleware
```

The flag takes a directory relative to the scanned tree, and is repeatable:

```bash theme={null}
slurp validate --dir . --middleware middleware --middleware auth/guards
```

A host embedding Slurp may keep its middleware anywhere, and the compiler has to
be told which of two **inverted** rule sets to check a file under. It cannot
guess from a path.

<Warning>
  **Without the flag, a correct middleware file fails to validate.**

  ```
  error[MiddlewareScopeViolation]: request.* is only accessible in middleware files (.\middleware\auth.slurp:4:6)
  Validation fail: 4 file(s), 1 error(s), 0 warning(s).
  ```

  The same run with `--middleware middleware` is clean:

  ```
  Validation ok: 4 file(s), 0 error(s), 0 warning(s).
  ```

  The failure direction is the safe one. The dangerous direction is a **typo**:
  `--middleware midleware` names a directory that does not exist, selects
  nothing, and reports the same scope violations with no hint that the flag
  missed. If middleware that should be checked reports `request.*` errors, check
  the spelling of the flag before the template.
</Warning>

### What the flag selects

|                                 | Selected by `--middleware middleware`           |
| ------------------------------- | ----------------------------------------------- |
| `middleware/auth.slurp`         | yes                                             |
| `middleware/nested/deep.slurp`  | yes, it is recursive                            |
| `middleware-helpers/util.slurp` | no, the match is on the directory, not a prefix |
| `guards/d.slurp`                | no, name it with a second flag                  |

<Note>
  The `middleware "auth"` line in frontmatter only RECORDS a name for tooling. It
  does not make the file middleware, and a file with no such line is middleware
  if the flag selects it. Like every other frontmatter directive, a misspelling
  of it is skipped silently.
</Note>

## Checked, never emitted

Middleware produces no output file. `slurp build` compiles it, runs the security
walk over it, and moves on:

```bash theme={null}
slurp build --input . --output dist --middleware middleware --verbose
```

```
  .\components\Product.slurp (component, checked only)
  .\layouts\base.slurp (layout, checked only)
  .\middleware\auth.slurp (middleware, not emitted)
  .\pages\index.slurp → dist\pages\index.html
Build complete. 1 page(s) emitted, 2 component/layout file(s) checked, 1 middleware file(s) checked.
```

Emitting it would be meaningless. It runs per request in the host, and writing
a `.html` for it would put a file in `dist/` that no route ever serves.

Its scope rules are inverted, so the ordinary page walk reports every correct
middleware file as a violation and lets a broken one through.

## The scope rules are inverted

Inside middleware, exactly three path roots are readable: `request`, `env` and
`loop`. There is no store, no cart, no product, no page data.

```slurp theme={null}
{if !request.cookies.session}{redirect "/login"}{/if}   ok
{if env.dev}{redirect "/dev"}{/if}                       ok
{if !user.signed_in}{redirect "/login"}{/if}             error
```

```
error[MiddlewareScopeViolation]: 'user' is not accessible in middleware; only request.* is allowed
```

Outside middleware, the mirror image applies. `request.*` is unreadable, and
both signals are compile errors:

```
error[MiddlewareScopeViolation]: request.* is only accessible in middleware files (.\pages\a.slurp:1:5)
error[RedirectOutsideMiddleware]: {redirect} is only allowed inside middleware files (.\pages\a.slurp:1:21)
error[RedirectOutsideMiddleware]: {next} is only allowed inside middleware files (.\pages\bad.slurp:0:0)
```

A page that wants to know something about the request gets it the same way it
gets everything else: the server puts it in the render context. A page reading
cookies directly is a page whose output depends on data the host did not choose
to give it.

## Available language features

Middleware has the whole template language available, minus the data. In
practice that means `{if}`, `{match}` and comparisons over `request.*`, and then
a `{redirect "/somewhere"}` or a `{next}`.

The language's general limits apply. Two matter here:

<Warning>
  **There is no clock, and there are no callable functions.** `Date.now()` parses
  and evaluates to null, and a comparison against null compares Equal, so a
  session-expiry check written that way never fires while looking like it works.
  Expire the cookie where it is minted and let a missing cookie be the check.
</Warning>

**Markup in a middleware file goes nowhere.** Writing `<p>Hello</p>` there is
legal and validates clean, but the file is never emitted and `{redirect}` and
`{next}` themselves render to the empty string. Middleware is a decision, not a
page.

## Acting on the signal

`slurp build` produces a static site, and a static site has no request to gate,
so middleware there is checked and nothing more. The signal matters when Slurp
is embedded in a server.

In 0.1.0 the compiler validates the signal but does not hand it back: rendering a
`{redirect}` yields the empty string, and there is no API that returns the target.
A host that wants to act on it parses the file with `parse()` and walks the
document for a `Node::Redirect`, deciding for itself in the context of a real
request. Expect this seam to move before 1.0.

## Next

<CardGroup cols={2}>
  <Card title="Writing pages" icon="file-code" href="/slurp/guides/pages">
    File-based routing, page data, and output paths.
  </Card>

  <Card title="Project structure" icon="folder-tree" href="/slurp/project-structure">
    Special directories, output paths, and static assets.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/slurp/reference/errors">
    Every diagnostic code, including the two on this page.
  </Card>

  <Card title="CLI" icon="terminal" href="/slurp/tooling/cli">
    Every subcommand and flag.
  </Card>
</CardGroup>
