mirror of
https://github.com/monadicsystems/okapi.git
synced 2024-11-23 09:54:24 +03:00
Update README.md
This commit is contained in:
parent
be1b653d52
commit
d014c5adab
210
README.md
210
README.md
@ -107,213 +107,3 @@ with Endpoints, but not a server implemented with Matchpoints.
|
||||
|
||||
On the flip side, a server implemented with Matchpoints will be more
|
||||
concise than a server implemented with Endpoints.
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Use the command `stack new <project-name>` to create a new Haskell project
|
||||
2. Add the `okapi` library to your project's dependencies
|
||||
|
||||
## Endpoint
|
||||
|
||||
An *Endpoint* is an *executable specification* that represents a single endpoint of
|
||||
your API.
|
||||
|
||||
An Endpoint has 6 fields:
|
||||
|
||||
```haskell
|
||||
data Endpoint p q h b r = Endpoint
|
||||
{ method :: StdMethod, -- (1)
|
||||
pathScript :: Path.Script p, -- (2)
|
||||
queryScript :: Query.Script q, -- (3)
|
||||
headersScript :: Headers.Script h, -- (4)
|
||||
bodyScript :: Body.Script b, -- (5)
|
||||
responderScript :: Responder.Script r -- (6)
|
||||
}
|
||||
```
|
||||
|
||||
The `method` field is a simple value, but the other fields point to a `Script` that represents their respective HTTP request parts.
|
||||
|
||||
The type parameter of a Script represents the type of value it returns.
|
||||
|
||||
Therefore, the concrete type of an Endpoint is determined by the return types of
|
||||
the Scripts that are used to construct the Endpoint.
|
||||
|
||||
All the different Script types are Applicatives, but not all are *lawful applicatives*.
|
||||
|
||||
Since all Script types are Applicatives, we can use the Applicative typeclass methods to write our scripts. Here's an example of a query Script:
|
||||
|
||||
```haskell
|
||||
data Filter = Filter
|
||||
{ color :: Text
|
||||
, categoryID :: Int
|
||||
, isOnSale :: Maybe ()
|
||||
}
|
||||
|
||||
myQueryScript :: Query.Script Filter
|
||||
myQueryScript = Filter
|
||||
<$> Query.param "color"
|
||||
<*> Query.param "category"
|
||||
<*> (Query.optional $ Query.flag "sale")
|
||||
```
|
||||
|
||||
If you have the `-XApplicativeDo` language extension turned on, you can also write your Scripts using `do` syntax.
|
||||
|
||||
We recommend using `-XApplicativeDo` in conjuction with the `-XRecordWildCards` language extension if you're not comfortable with using the Applicative operators. Here's the same query script we defined above, but with these language extensions turned on:
|
||||
|
||||
```haskell
|
||||
{-# LANGUAGE ApplicativeDo #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
|
||||
myQueryScript :: Query.Script Filter
|
||||
myQueryScript = do
|
||||
color <- Query.param "color"
|
||||
categoryID <- Query.param "category"
|
||||
isOnSale <- Query.optional $ Query.flag "sale"
|
||||
pure Filter {..}
|
||||
```
|
||||
|
||||
Each Script type has its' own operations suited to parsing its' respective part of the
|
||||
request. These are defined in more detail below.
|
||||
|
||||
1. ### Method
|
||||
|
||||
The `method` field represents the HTTP method that the Endpoint accepts.
|
||||
|
||||
Its' type is `StdMethod` from the `http-types` library.
|
||||
|
||||
Only the standard methods mentioned in RFC-1234 are allowed.
|
||||
|
||||
2. ### Path Script
|
||||
|
||||
The `pathScript` field defines the request path that the Endpoint accepts, including *path parameters*.
|
||||
|
||||
Path Scripts have two operations: `static` and `param`.
|
||||
|
||||
```haskell
|
||||
myPathScript = Path.static "person" *> Path.param @Int "personID"
|
||||
```
|
||||
|
||||
3. ### Query Script
|
||||
|
||||
The `queryScript` field defines the query that the Endpoint accepts.
|
||||
|
||||
There are two operations for Query Scripts: `param` and `flag`.
|
||||
|
||||
There are two modifiers for Query Scripts: `optional` and `option`.
|
||||
|
||||
`optional` and `option` are specialized versions of the `optional` and `option`
|
||||
parser combinators found in the `parser-combinators` library.
|
||||
|
||||
```haskell
|
||||
myQueryScript :: Query.Script (Text, Maybe Float, Int)
|
||||
myQueryScript = do
|
||||
lastName <- Query.param "last_name"
|
||||
optSalary <- Query.optional $ Query.param "salary"
|
||||
minAge <- Query.option 21 $ Query.param "min_age"
|
||||
pure (lastName, optSalary, minAge)
|
||||
```
|
||||
|
||||
4. ### Body Script
|
||||
|
||||
The `bodyScript` field defines the request body and it's content type that the Endpoint accepts.
|
||||
|
||||
There are four operations for Body Scripts: `json`, `form`, `formParam`, `file`.
|
||||
|
||||
There are two modifiers for Body Scripts: `optional` and `option`.
|
||||
|
||||
```haskell
|
||||
myBodyScript :: Body.Script (Maybe Value)
|
||||
myBodyScript = Body.optional $ Body.json @Value
|
||||
```
|
||||
|
||||
5. ### Headers Script
|
||||
|
||||
The `headersScript` field defines the request headers that the Endpoint accepts.
|
||||
|
||||
There are two operations for Headers Scripts: `param` and `cookie`.
|
||||
|
||||
There are two modifiers for Headers Scripts: `optional` and `option`.
|
||||
|
||||
```haskell
|
||||
myHeadersScript :: Headers.Script _
|
||||
myHeadersScript = undefined
|
||||
```
|
||||
|
||||
6. ### Responder Script
|
||||
|
||||
The `responderScript` field defines the responses that the Endpoint's handler MUST return.
|
||||
|
||||
Responder Scripts have to be more complex than the other Script types in order for the Endpoint to have a contract with the handler that the handler will respond with the responses defined in the Responder Script.
|
||||
|
||||
This is done using a combination of higher order functions, linear types, and smart constructors.
|
||||
|
||||
Responder Script operations have to take a *Responder Headers* Script as an argument to define what headers will be attached to the Response.
|
||||
|
||||
For now, there is only one operation for Responder Scripts: `json`.
|
||||
|
||||
Responder Header Scripts also only have one operation: `has`.
|
||||
|
||||
```haskell
|
||||
{-# LANGUAGE ApplicativeDo #-}
|
||||
{-# LANGUAGE RecordWildCards #-}
|
||||
{-# LANGUAGE BlockArguments #-}
|
||||
|
||||
data SecretHeaders = SecretHeaders
|
||||
{ firstSecret :: Int -> Response -> Response
|
||||
, secondSecret :: Int -> Response -> Response
|
||||
}
|
||||
|
||||
data MyResponders = MyResponders
|
||||
{ allGood :: (SecretHeaders %1 -> Response -> Response) -> Text -> Response
|
||||
, notGood :: (() %1 -> Response -> Response) -> Text -> Response
|
||||
}
|
||||
|
||||
myResponderScript = do
|
||||
allGood <- Responder.json @Text status200 do
|
||||
addSecret <- ResponderHeaders.has @Int "IntSecret"
|
||||
addAnotherSecret <- ResponderHeaders.has @Int "X-Another-Secret"
|
||||
pure SecretHeaders {..}
|
||||
notGood <- Responder.json @Text status501 $ pure ()
|
||||
pure MyResponders {..}
|
||||
|
||||
myHandler someNumber _ _ _ _ (MyResponders allGood notGood) = do
|
||||
if someNumber < 100
|
||||
then do
|
||||
return $ allGood
|
||||
(\(SecretHeaders firstSecret secondSecret) response -> secondSecret 0 $ firstSecret 7 response)
|
||||
"All Good!"
|
||||
else do
|
||||
return $ notGood
|
||||
(\() response -> response)
|
||||
"Not Good!"
|
||||
```
|
||||
|
||||
More information about *Responders* and *ResponderHeaders* is available in the Handler section.
|
||||
|
||||
## Handler
|
||||
|
||||
Handlers are simple: they are contextful functions from the arguments provided by an Endpoint, to a Response.
|
||||
|
||||
The type synonym `Handler` represents the type of Handlers in Okapi:
|
||||
|
||||
```haskell
|
||||
type Handler m p q b h r = p -> q -> b -> h -> r -> m Response
|
||||
```
|
||||
|
||||
The type parameter `m` represents the context in which the Handler creates the Response.
|
||||
|
||||
The type parameters `p`, `q`, `b`, `h` and `r` represent the types of the values returned by the Endpoint's Path, Query, Body, Headers and Responder Scripts respectively.
|
||||
|
||||
## Plan
|
||||
|
||||
A Plan
|
||||
|
||||
## Server
|
||||
|
||||
### Lifter
|
||||
|
||||
### Execution
|
||||
|
||||
## Matchpoint
|
||||
|
||||
## TLDR
|
||||
|
Loading…
Reference in New Issue
Block a user