Update readme with differences to httpure
This commit is contained in:
parent
fcf532bd71
commit
d2ce890a92
158
Readme.md
158
Readme.md
@ -4,6 +4,16 @@
|
||||
|
||||
A 🎨 colourful fork of the amazing [HTTPure](https://github.com/citizennet/purescript-httpure) http server framework.
|
||||
|
||||
Coming from HTTPure? You might want to have a look at the [differences to HTTPure](#differences-to-httpure).
|
||||
|
||||
## ToC
|
||||
1. [Installation](#installation)
|
||||
1. [Quick start](#quick-start)
|
||||
1. [Documenation](#documentation)
|
||||
1. [Examples](#examples)
|
||||
1. [Testing](#testing)
|
||||
1. [Differences to HTTPure](#differences-to-httpure)
|
||||
1. [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
@ -11,7 +21,7 @@ A 🎨 colourful fork of the amazing [HTTPure](https://github.com/citizennet/pur
|
||||
spago install httpurple
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
## Quick start
|
||||
|
||||
```purescript
|
||||
module Main where
|
||||
@ -66,7 +76,7 @@ hello 🗺
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [docs folder](./docs).
|
||||
See the [docs folder](./docs) for more in-depth guides.
|
||||
|
||||
## Examples
|
||||
|
||||
@ -88,6 +98,150 @@ To run the test suite, in the project root run:
|
||||
spago -x test.dhall test
|
||||
```
|
||||
|
||||
## Differences to HTTPure
|
||||
|
||||
HTTPurple 🪁 is a fork of [HTTPure](https://github.com/citizennet/purescript-httpure) that I started to freely experiment with some ideas I have on improving the usage experience. Currently I have no intentions on back-porting any of it to HTTPure, as I don't have the time for it and also don't want to restrict myself.
|
||||
|
||||
If you have used HTTPure before, you'll probably want to go through the following changes to get started using HTTPurple 🪁.
|
||||
|
||||
### Routing-duplex
|
||||
|
||||
The most notable difference to HTTPure is that HTTPurple 🪁 uses the amazing [`routing-duplex`](https://github.com/natefaubion/purescript-routing-duplex) library for routing. I found the previous lookup-based routing tedious to work with, especially when having more complex routes, and quite error-prone, especially if you need reverse-routing for redirects.
|
||||
|
||||
[`routing-duplex`](https://github.com/natefaubion/purescript-routing-duplex) offers an elegant bidirectional routing which was initially designed for SPAs. Have a look at the really extensive [`documentation`](https://github.com/natefaubion/purescript-routing-duplex). The benefits of using routing-duplex are
|
||||
* Much simpler and less tedious definition of routes
|
||||
* Roundtrip printing/parsing of routes, so no more invalid redirects
|
||||
* Exhaustive pattern matching so you are sure to match all defined routes
|
||||
* Option to separate routes into logical groups
|
||||
|
||||
Here is a bit more elaborated examples:
|
||||
|
||||
```purescript
|
||||
module Main where
|
||||
|
||||
import Prelude hiding ((/))
|
||||
|
||||
import Data.Either (Either(..))
|
||||
import Data.Generic.Rep (class Generic)
|
||||
import Data.Maybe (Maybe(..))
|
||||
import Data.Tuple (Tuple(..))
|
||||
import Effect.Console (log)
|
||||
import HTTPurple (type (<+>), Request, ResponseM, ServerM, found', headers, ok, notFound, orElse, serve, (<+>))
|
||||
import Record as Record
|
||||
import Routing.Duplex (RouteDuplex', as, optional, print, root, segment, string)
|
||||
import Routing.Duplex.Generic as G
|
||||
import Routing.Duplex.Generic.Syntax ((/), (?))
|
||||
import Type.Prelude (Proxy(..))
|
||||
|
||||
-- define the ADT representing your Api
|
||||
data Route
|
||||
= Home
|
||||
| Profile String
|
||||
| Account String
|
||||
| Search { q :: String, sorting :: Maybe Sort }
|
||||
|
||||
derive instance Generic Route _
|
||||
|
||||
data Sort = Asc | Desc
|
||||
|
||||
derive instance Generic Sort _
|
||||
|
||||
sortToString :: Sort -> String
|
||||
sortToString = case _ of
|
||||
Asc -> "asc"
|
||||
Desc -> "desc"
|
||||
|
||||
sortFromString :: String -> Either String Sort
|
||||
sortFromString = case _ of
|
||||
"asc" -> Right Asc
|
||||
"desc" -> Right Desc
|
||||
val -> Left $ "Not a sort: " <> val
|
||||
|
||||
sort :: RouteDuplex' String -> RouteDuplex' Sort
|
||||
sort = as sortToString sortFromString
|
||||
|
||||
-- define the api routes
|
||||
api :: RouteDuplex' Route
|
||||
api = root $ G.sum
|
||||
{ "Home": G.noArgs
|
||||
, "Profile": "profile" / string segment
|
||||
, "Account": "account" / string segment
|
||||
, "Search": "search" ? { q: string, sorting: optional <<< sort }
|
||||
}
|
||||
|
||||
-- example of a second api for meta information
|
||||
data Route2 = Health
|
||||
|
||||
derive instance Generic Route2 _
|
||||
|
||||
meta :: RouteDuplex' Route2
|
||||
meta = root $ G.sum { "Health": "health" / G.noArgs }
|
||||
|
||||
-- optionally define a custom notFoundHandler
|
||||
notFoundHandler :: Request Unit -> ResponseM
|
||||
notFoundHandler = const $ ok "Nothing to see here"
|
||||
|
||||
-- combine routes using `<+>`, combine routes using `orElse`
|
||||
main :: ServerM
|
||||
main = serve { port: 8080, notFoundHandler } { route: api <+> meta, router: apiRouter `orElse` metaRouter }
|
||||
where
|
||||
|
||||
apiRouter { route: Home } = ok "hello world!"
|
||||
apiRouter { route: (Profile profile) } = ok $ "hello " <> profile <> "!"
|
||||
apiRouter { route: (Account account) } = found' redirect ""
|
||||
where
|
||||
-- to create a redirect just print the data constructor
|
||||
redirect = headers [ Tuple "Location" $ print api $ Profile account ]
|
||||
|
||||
apiRouter { route: (Search { q, sorting }) } = ok $ "searching for query " <> q <> " " <> case sorting of
|
||||
Just Asc -> "ascending"
|
||||
Just Desc -> "descending"
|
||||
Nothing -> "defaulting to ascending"
|
||||
|
||||
metaRouter { route: Health } = ok """{"status":"ok"}"""
|
||||
```
|
||||
|
||||
### Startup options
|
||||
|
||||
HTTPurple 🪁 greatly simplifies the startup options and functions. The `serve`, `serve'`, `serveSecure` and `serveSecure'` have been merged into a single function `serve` that accepts listen options as the first parameter and uses sane defaults if you don't provide any.
|
||||
|
||||
The easiest way to start a server is to provide just the route and a router:
|
||||
|
||||
```purescript
|
||||
main :: ServerM
|
||||
main =
|
||||
serve {} { route, router }
|
||||
```
|
||||
|
||||
This will spin up the http server with sane defaults.
|
||||
```bash
|
||||
HTTPurple 🪁 up and running on http://0.0.0.0:8080
|
||||
```
|
||||
|
||||
But you can overwrite any of the optional properties like this
|
||||
|
||||
```purescript
|
||||
main :: ServerM
|
||||
main =
|
||||
serve {
|
||||
hostname: "localhost"
|
||||
, port: 9000
|
||||
, certFile: "./Certificate.cer"
|
||||
, keyFile: "./Key.key"
|
||||
, notFoundHandler
|
||||
, onStarted: log "Server started 🚀"
|
||||
, closingHandler: NoClosingHandler
|
||||
} { route, router }
|
||||
where
|
||||
notFoundHandler :: Request Unit -> ResponseM
|
||||
notFoundHandler = const $ ok "Nothing to see here"
|
||||
```
|
||||
|
||||
### Minor other improvmenets
|
||||
|
||||
* Default closing handler - A default closing handler is provided so you can just stop your server using `ctrl+x` without having to worry about anything. You can deactivate it by setting `closingHandler: NoClosingHandler` in the listen options.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This is a fork of [HTTPure](https://github.com/citizennet/purescript-httpure), which is licensed under MIT. See the [original license](./LICENSES/httpure.LICENSE). This work is similarly licensed under [MIT](./License).
|
||||
|
Loading…
Reference in New Issue
Block a user