Open CrowdHailer opened 4 years ago
I don't have a lot of experiences with functional programming, but the router makes me think to pattern matching:
// users.gleam
import midas
fn list() -> midas.Response {
// TODO return a midas.Response with the list of users
}
fn get_post(user_id: String, post_id: String) -> midas.Response {
let published = midas.QueryParam(req, "published")
// TODO return a midas.Response with the given post
pub fn router(req: midas.Request, path: List(String)) -> midas.Response {
let is_user_id = midas.String([midas.MinLength(14), midas.MaxLength(14)])
let is_post_id = midas.Integer([midas.Max(999)])
case [req.Verb, path] {
[midas.Get, []] -> list(req, res)
[midas.Get, [user_id, "posts", post_id]] if is_user_id(user_id) && is_post_id(post_id) -> get_post(user_id, post_id)
_ -> midas.error404(req)
}
}
// router.gleam
import midas
import my_app/users
pub fn router(req: midas.Request) -> midas.Response {
case req.SplittedPath {
["users" | rest] -> users.router(req, rest)
_ -> midas.error404(req)
}
}
A limitation is that I don't know how to make post_id
an Int
(and not just a String
to parse).
Maybe it can give you some ideas.
Update: I've read again the README, and I feel stupid to suggest what is already there. Let's just say that I prefer what is in the README to the other suggestions.
The pattern matching solution is nice, it's certainly the most simple. You hit the nail on the head about the integers though. The DSL suggested here gives you more type safety but potentially not worth the overhead.
As mentioned, it can exist as an experimental API for a while because the option of a simple case statement and matching will always exist.
I think there is value in always having a simple interface to the server which consists of a single request-> response function.
However there is a lot of boiler plate in writing API's so what would a DSL look like.
Routing Tree suggestion
Key feature of this one is every new extraction, e.g. Param/QueryParam takes a list of next steps, so branching can be introduced at any point, this reduces duplication.
List of endpoints suggestion.
Formatter makes mess of above but assuming you reuse user_id declaration and handlers are defined elsewhere, routing can be simplified
Need to use an explicit
Choice
type when more than one route is available. Has more duplication but the behaviour of reach route is clearer.Notes
curry2
curry3
etc.I think it's possible to have controller files.
In summary the DSL is a bunch of error composition the second option (particularly if grouped by controller) might as well be the clear fn call approach.
Can have a Response(serializer: fn(x) -> r, Get(return x)) // Could serialize just OK value
The best thing to do is to practise a parsing API on a smaller section of the problem, e.g. form/query params, where all raw values are strings, entries come as a list not map, etc. And then expand it to requests if working well