Routing
A routes zone maps requests to controller actions, Rails style. It generates the router, a route table and a typed helper for every named route.
Setting up
Put the zone in the module your polar.toml points main at, usually src/routes.px. It needs Std.Http, Std.Option, Std.Id and Ticket.Conn in uses, plus every controller it names.
module Routes
uses
Std.Http
Std.Id
Std.Option
Ticket.Conn
Admin.StatsController
Controllers.ArticlesController
Controllers.PagesController
hosts
Node
routes
root -> PagesController.home
get /about -> PagesController.about as about
resources articles -> ArticlesController only index show new create
namespace admin
get /stats -> StatsController.show as stats
exports
Node
A controller is named by its local name: the last segment of its module path, or its as alias.
Verb lines
The verbs are get, post, put, patch and delete. A :name segment is a path param. Add as name to get a name_path helper. A verb line without as gets no helper.
routes
root -> PagesController.home
get /about -> PagesController.about as about
post /webhooks/:source -> PagesController.webhook as webhook
Resources
resources posts -> PostsController expands to the seven Rails actions, in this order:
| Method | Path | Action | Helper |
|---|---|---|---|
| GET | /posts | index | posts_path() |
| GET | /posts/new | new | new_post_path() |
| POST | /posts | create | |
| GET | /posts/:id | show | post_path(id) |
| GET | /posts/:id/edit | edit | edit_post_path(id) |
| PATCH, PUT | /posts/:id | update | |
| DELETE | /posts/:id | destroy |
Use only or except (not both) to pick actions. Matching is first-match in this order, which is why /posts/new isn't taken by /posts/:id.
Nesting, member and collection
Indented lines belong to the entry above. Nested resources go under the singular of the parent, member routes under /posts/:id/…, and collection routes under /posts/….
routes
resources posts -> PostsController
resources comments -> CommentsController only create destroy
member
post publish -> PostsController.publish
collection
get drafts -> PostsController.drafts
namespace admin
resources users -> UsersController except destroy
That gives you post_comments_path(post_id), post_comment_path(post_id, id), publish_post_path(id), drafts_posts_path() and admin_users_path(). A namespace prefixes paths with /admin and helper names with admin_.
When a table name has no simple singular, name it with as: resources people -> PeopleController as person.
Path helpers
A param named id or ending in _id takes an Id. Any other param takes a String, which is URL-encoded.
post_path(Id(3)) // "/posts/3"
post_comment_path(Id(2), Id(9)) // "/posts/2/comments/9"
webhook_path("a b") // "/webhooks/a%20b"
Controllers and views get helpers from Paths. The routes module imports every controller, so a controller can't import it back. The zone also generates a sibling module Paths with the same helpers and no controller imports. Write uses Paths and call Paths.article_path(id).
How requests match
- Trailing and doubled slashes don't matter:
/posts/3/matches/posts/:id. - A
POSTwith a form field_methodofpatch,putordeleteis routed as that method. HEADruns theGETaction and sends no body.- No path matches: the launcher answers 404. A path matches but the method doesn't: 405, with an
allowheader. - Path params are strings.
/posts/abcstill reachesshow, and the action decides what to do with it.
Mistakes are compile errors
routes
resources posts -> PostController
error[POLAR0901]: no module `PostController` in `uses`
--> src/unknown_controller.px:12:23
|
12 | resources posts -> PostController
| ^^^^^^^^^^^^^^
|
= help: a controller is a module in `uses`, named by its last segment or its `as` alias
Duplicate routes, duplicate helpers, unknown verbs and unknown actions in only are reported the same way. polar fmt lines up the -> column in each block.