Step 10 — Middleware
Ada’s phone says “order failed.” Support opens the logs and sees fifty lines from the same second. Without a shared ID on the HTTP response and the log line, nobody can match “her call” to “your error.”
Middleware is how you fix that kind of cross-cutting problem once — instead of pasting the same code into every service.
What is middleware? (in general)
Think of every API request as a journey:
- The request arrives (headers, body, cookies).
- Your business code runs (
product.list,order.place, …). - A response goes back to the client.
Middleware is optional code that sits around step 2. It can:
- Run before your action (read headers, start a timer, reject empty bodies early).
- Run after your action (add headers, write metrics, clean up).
- Apply to many routes at once — that is the point.
Real-world examples you already use without noticing:
| Everyday need | Typical middleware job |
|---|---|
| Support tickets | Attach a unique X-Request-Id |
| CORS browser rules | Allow https://shop.example.com to call your API |
| Rate limits | Cap “100 requests / minute” per IP |
| Compression | Gzip large JSON responses |
| Maintenance | Return 503 while you deploy |
Middleware is not the place for “is Ada allowed to cancel this order?” That is authorization on the action (Step 9). Middleware is for shared plumbing on the wire.
Request
│
▼
┌─────────────────┐
│ Middleware A │ e.g. request ID
│ Middleware B │ e.g. CORS
└────────┬────────┘
▼
Your service action
│
▼
┌─────────────────┐
│ Middleware B │ (on the way out)
│ Middleware A │
└────────┬────────┘
▼
ResponseHow Pionia Shop uses it
Pionia runs a middleware chain on each HTTP request. You register classes in settings.ini (or a provider). Each class can touch the request and the response.
Today you add one useful piece: Request ID.
What you will learn
- Why middleware exists (shared steps, not business rules)
- Scaffold
RequestIdMiddleware - See
X-Request-Idon a realproduct.listresponse
- Step 9 — you already have a working API
Add RequestId middleware
Step 1 Step
1. Scaffold
php pionia make:middleware RequestIdThat creates a class under middlewares/ (folder appears on first use).
Step 1 Step
2. Register it
In environment/settings.ini:
[app_middlewares]
request_id = "Application\Middlewares\RequestIdMiddleware"(Exact class namespace follows your app’s Application\ layout — check the generated file.)
Step 1 Step
3. Call the catalog and read headers
curl -si -X POST http://127.0.0.1:8000/api/v1/ \
-H "Content-Type: application/json" \
-d '{"service":"product","action":"list"}'Look for a response header like X-Request-Id: …. Copy that value into a log search later when you debug checkout.
Auth vs middleware (do not mix them up)
| Concern | Where it lives | Example |
|---|---|---|
| “Is someone logged in?” | Authentication + #[Authenticated] | Step 9 |
| “May this user refund?” | #[Can] or a check in the action | Step 9 |
| “Stamp every response with an ID” | Middleware | This step |
| “Allow the Vite app’s origin” | Middleware / HTTP config | Frontend step |
If you put login checks only in middleware, every new action is easy to forget. Attributes on the service keep the rule next to the method.
Try it yourself
- Hit
product.listtwice — confirm two different request IDs. - Log
X-Request-Idfrom the client (or fromcurl -si) and search yourstorage/logs/for the same string after you addlogger()->info(...)in an action.
Common mistakes
- Using middleware for “must be logged in on this one action” — prefer
#[Authenticated] - Forgetting to register the class in
[app_middlewares]— scaffold alone does nothing - Expecting middleware to run for CLI commands — this chain is for HTTP
What’s next
Full guide: Middleware. Next tutorial step keeps checkout fast with background work.