Routes & APIs
Four routing patterns. Pick the one that fits your use case.
You will learn
- Four ways to register URLs: implicit public, ZealAPI, explicit routes, namespaced
- Dynamic path parameters like /users/{id}
- How ZealPHP injects parameters by name via reflection
- Return value conventions: int, array, string, Generator
The problem
So far, you've used implicit routing: drop a file in public/, get a URL. That
covers pages. You've also used ZealAPI files in api/ for REST endpoints. But what
about URLs like /users/42? Or restricting a route to POST only? Or versioning
an API?
ZealPHP has four routing patterns. You've already used two — here are all four.
1. Implicit public routes
You learned this in Lesson 3. Files in public/ become URLs automatically:
public/index.php → GET /
public/about.php → GET /about
public/blog/post.php → GET /blog/post
Use for: Pages the user visits. Simple, zero config.
2. Implicit API routes (ZealAPI)
You used this in Lesson 8. Files in api/ become REST endpoints:
// api/learn/notes.php → GET/POST /api/learn/notes
$notes = function () {
$u = Auth::currentUser();
// ... handle GET (list) and POST (create)
};
The closure variable name ($notes) must match the filename. Inside, $this is the ZealAPI instance with helpers like response(), json().
Use for: REST endpoints. One file per resource, auto-routed.
3. Explicit routes
When you need path parameters or method restrictions, register explicit routes in a file under route/:
// route/users.php
$app->route('/users/{id}', ['methods' => ['GET']], function($id) {
return ['id' => (int)$id, 'name' => 'User ' . $id];
});
$app->route('/users', ['methods' => ['POST']], function($request) {
return ['created' => true];
});
Use for: Dynamic URLs, method-specific routes, WebSocket handlers, Store table registration.
4. Namespaced routes
nsRoute and nsPathRoute add a URL prefix:
$app->nsRoute('api/v2', '/health', function() {
return ['ok' => true];
});
// → GET /api/v2/health
$app->nsPathRoute('files', function($path) {
return ['path' => $path];
});
// → GET /files/foo/bar/baz.txt → $path = 'foo/bar/baz.txt'
Use for: API versioning, catch-all paths (file serving, proxy).
Parameter injection
ZealPHP injects route handler arguments by name via reflection. The reflection result is cached at route registration — zero per-request overhead:
| Parameter name | Injected value |
| -------------- | --------------------------------- |
| $request | ZealPHP\HTTP\Request |
| $response | ZealPHP\HTTP\Response |
| $app | ResponseMiddleware instance |
| {param} names | Matched URL segments |
| any other | null or the parameter's default |
This means parameter order doesn't matter. function($id, $request) and function($request, $id) both work.
Return value conventions
| Return type | Behavior |
| -------------- | ------------------------------------ |
| int | HTTP status code (e.g. return 404) |
| array / object | JSON-serialized, Content-Type set |
| string | HTML body |
| Generator | SSR streaming (each yield sent live) |
| void + echo | Output buffer captured |
This site uses all four routing patterns. Explore them:
- /api/learn/chat_status — ZealAPI endpoint (implicit API)
- /api/learn/demo/greeting?name=World — Explicit route
- /api/learn/demo/timing?mode=parallel — Explicit route returning JSON
You need a REST endpoint at /api/products that handles GET and POST. Which routing pattern should you use?
Key Takeaways
- Four routing patterns: implicit public, ZealAPI, explicit, namespaced — each for a different use case
- Path parameters (
{id}) are injected by name — order doesn't matter - Reflection is cached at registration — zero per-request cost
- Return type determines response format: array → JSON, string → HTML, Generator → streaming