diff --git a/README.md b/README.md index f5d0a25..9fa53f8 100644 --- a/README.md +++ b/README.md @@ -1 +1,91 @@ # axon +**WIP** + +HTTP server library inspired by [`axum`](https://docs.rs/latest/axum), allowing best-in-class +expressive routing. + +```purs +root :: Get -> Path "/" _ -> Aff String +root _ _ = pure "Hello, world!" + +main :: Effect Unit +main = Axon.serve (root `Handle.or` Handle.Default.notFound) + +-- GET localhost:8000/ -> 200 OK "Hello, world!" +-- GET localhost:8000/foo -> 404 Not Found +``` + +## Request Handlers +Request handler functions have any number of parameters that are `RequestParts` and return an `Aff Response` (or any `MonadAff`). + +
+ + +`RequestParts` + + + - `Request` + - Always succeeds; provides the entire request + - **Combinators** + - `Unit` + - Always succeeds + - `a /\ b` + - Tuple of `a` and `b`, where `a` and `b` are `RequestParts`. + - `Maybe a` + - `a` must be `RequestParts`. If `a` can't be extracted, the handler will still succeed and this will be `Nothing`. If `a` was extracted, it's wrapped in `Just`. + - `Either a b` + - `a` and `b` must be `RequestParts`. Succeeds if either `a` or `b` succeeds (preferring `a`). Fails if both fail. + - **Body** + - `String` + - succeeds when request has a non-empty body that is valid UTF-8 + - `Json a` + - succeeds when request has a `String` body (see above) that can be parsed into `a` using `DecodeJson`. + - `Buffer` + - succeeds when request has a nonempty body. + - `Stream` + - succeeds when request has a nonempty body. + - **Headers** + - `Header a` + - `a` must be `TypedHeader` from `Axon.Header.Typed`. Allows statically (ex. `ContentType Type.MIME.Json`) or dynamically (ex. `ContentType String`) matching request headers. + - `HeaderMap` + - All headers provided in the request + - **Path** + - `Path a c` + - Statically match the path of the request, and extract parameters. See `Axon.Request.Parts.Path`. (TODO: this feels too magical, maybe follow axum's prior art of baking paths into the router declaration?) + - **Method** + - `Get` + - `Post` + - `Put` + - `Patch` + - `Delete` + - `Options` + - `Connect` + - `Trace` +
+ +Similarly to the structural extraction of request parts; handlers can use `Axon.Response.Construct.ToResponse` for easily constructing responses. + +
+ + +`ToResponse` + + +- **Combinators** + - `Status /\ a` + - Special case to make sure any `Status` in a tuple will take priority over any default statuses within. TODO: This case (overlapping with `a /\ b` requires the class to be "sealed" in an instance chain. Want a clean way around this so consumers can implement `ToResponse`.) + - `a /\ b` + - Merges `toResponse a` and `toResponse b`, using `b` on conflicts +- **Status** + - `Axon.Response.Status.Status` +- **Body** + - `Axon.Response.Body.Body` + - `String` + - `Node.Buffer.Buffer` + - `Node.Stream.Readable a` (for all `a`) + - `Axon.Response.Construct.Json a` + - `a` must be `EncodeJson`. This will set the body to `a` stringified, and set `Content-Type` to `application/json`. +- **Headers** + - `ToResponse` is implemented for all implementors of `TypedHeader` + - TODO: `Map String String` +