Skip to content

Commit

Permalink
Merge pull request #54 from anton-k/fix-doc-typos
Browse files Browse the repository at this point in the history
Fix typos in docs
  • Loading branch information
anton-k authored Oct 24, 2023
2 parents a99891d + 5fb068d commit 9a8092b
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 144 deletions.
33 changes: 17 additions & 16 deletions docs/src/00-foreword.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The main features are:

* type-safe route handlers and conversions

* handlers are encoded with generic haskell functions
* handlers are encoded with generic Haskell functions

* built on top of WAI and warp server libraries.

Expand Down Expand Up @@ -47,7 +47,7 @@ hello = Send $ pure $ ok "Hello World"
## How to install library

We can install it from hackage.
we need to use the library [mig-server](https://hackage.haskell.org/package/mig-server)
We need to use the library [mig-server](https://hackage.haskell.org/package/mig-server)

With cabal we can install it from Hackage:

Expand All @@ -61,15 +61,15 @@ With stack we can link to the repo in extra-deps
TODO: example here
```

## Sturcuture of the library
## Structure of the library

There are several libraries:

- `mig` - core library which defines DSL to build servers with API-schemas and functions to render it to low-level representation.
- `mig-extra` - extra addons to the core library
- `mig-extra` - extra add-ons to the core library
- `mig-server` - mig core with batteries and functions to run servers on top of warp.
- `mig-client` - HTTP-clients from the server code
- `mig-wai` - connvert mig servers to WAI-applications
- `mig-wai` - convert mig servers to WAI-applications
- `mig-swagger-ui` - serve swagger for you app.

## Source code for examples
Expand All @@ -90,24 +90,25 @@ But it is akin to servant in usage of type-safe conversions and type-level safet
### servant

The mig uses the same ideas of type-safe handlers which a re based on generic Haskell functions.
The main difference is that in servant th whole server is described as type.
Which leads to type-safety and ability to derive API sche, from the type.
The main difference is that in servant the whole server is described as type.
Which leads to type-safety and ability to derive API schema, from the type.

But downside of it is fancy big types and whery advanced concepts that user needs to know
But downside of it is fancy big types and very advanced concepts that user needs to know
in order to use the library. Also one drawback to me is when things go wrong and you get
several pages long error messages. If your server is really big it can be very hard to spot
the origin of the error as type mismatch is going to be with the whole type which describes
the full server.

The mig borrows idea of type-safe functions to represent route handlers.
But types represent only individual handlers. It does not describe the full server.
But we have typesafety on the level of the single route. And error messages are going
to be localised and dedeicated to a single route.
But we have type safety on the level of the single route. And error messages are going
to be localised and dedicated to a single route.

Using type-level description of the routes provide the same benefits as in serbvant case:
Using type-level description of the routes provide the same benefits as in servant case:

* safe type check of the conversions of low level request and response elements
* usage of generic haskell functions as handlers
* usage of generic Haskell functions as handlers

* declarative design of the servers

In the mig API is a value that is derived from the server at run-time.
Expand All @@ -119,9 +120,9 @@ something more simple.
### scotty

The scotty is also in domain of simple, easy to use solutions.
so why did I wrote mig and havn't used the scotty instead?
Scotty features more imperative approach where you write handlert as
so why did I wrote mig and haven't used the scotty instead?
Scotty features more imperative approach where you write handlers as
expression for Scotty library monad. But it does not looks so well as in servant's case to me.
It is harder to assemble servers from parts. and I really like the idea of type-safe
convertions of various parts of request and response.
It is harder to assemble servers from parts. And I really like the idea of type-safe
conversions of various parts of request and response.

20 changes: 10 additions & 10 deletions docs/src/01-hello-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ newtype Server m = Server (Api (Route m))
```

The `Api` type is a value to describe the API schema and `Route` contains
useful info on the type of the route (method, decription of the inputs and outputs).
useful info on the type of the route (method, description of the inputs and outputs).
The server is parametrized by some monad type. For this example we use `IO`-monad.
It means that all our hadnlers are going to return `IO`-values.
It means that all our handlers are going to return `IO`-values.

### How to link paths to handlers

Expand All @@ -49,11 +49,11 @@ To bind path "api/v1/hello" to handler `hello` we use function `(/.)`. Let's loo
```

It expects the `Path` which has instance of class `IsString` that is why we can
use plain strings for it. the second argument is something that is convertible to `Server`.
Here we use trick to be able to use arbitrary haskell functions as handlers.
use plain strings for it. The second argument is something that is convertible to `Server`.
Here we use trick to be able to use arbitrary Haskell functions as handlers.
We have special class called `ToServer` which can convert many different types to `Server`.

The output type is abit tricky: `Server (MonadOf a)`.
The output type is a bit tricky: `Server (MonadOf a)`.
The `MonadOf` is a type function which can extract `m` from `(Server m)`.
Or for example it can extract `m` from the function `request -> m response`.
So the `MonadOf` is a way to get underlying server monad from any value.
Expand All @@ -70,7 +70,7 @@ The type-level function `MonadOf` knows how to extract `IO` from `Get IO (Resp T
### The type of response
Let's stydy the signature of the `hello` handler:
Let's study the signature of the `hello` handler:
```
hello :: Get IO (Resp Json Text)
Expand Down Expand Up @@ -100,7 +100,7 @@ newtype Send method m a = Send (m a)
```

It encodes HTTP-method on type level. This is useful to aggregate value for API-schema of our server.
We have type synonyms for all HTTP-nethods (`Get`, `Post`, `Put` etc).
We have type synonyms for all HTTP-methods (`Get`, `Post`, `Put` etc).

It's interesting to know that library mig does not use any custom monads for operation.
Instead it runs on top of monad provided by the user. Usually it would be `IO` or `Reader` over `IO`.
Expand Down Expand Up @@ -259,7 +259,7 @@ Servers on the same path are also distinguished by:

### Subtle nuance on Monoid instance for Server

Yuo may ask: why not to write the previous example like this:
You may ask: why not to write the previous example like this:

```haskell
server =
Expand Down Expand Up @@ -290,13 +290,13 @@ server =
]
```

Regarding the previous example we could not use `mconcat` even if we wnated to.
Regarding the previous example we could not use `mconcat` even if we wanted to.
Because `handelGet` and `handlePost` have different types. They can not
be even put in the same list. But here lies the beauty of the library.
We can use arbitrary types as handlers but in the end they all get converted
to the value `Server m`. So we have the flexibility on DSL level but
on the level of implementation to build the tree of handlers we use the same type.
which makes type very simple.
Which makes type very simple.

### List instance for Servers
Because of the `ToServer a => ToServer [a]` instance we can omit the `mconcat`
Expand Down
66 changes: 33 additions & 33 deletions docs/src/02-request-anatomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,16 @@ type Get a = Send GET IO a
type Post a = Send POST IO a
```

also it provides more specific response type:
Also it provides more specific response type:

```haskell
newtype Resp a = Resp (Core.Resp Json a)
```

For the next example we are going to build JSON-application again.
So instea of more general `Mig` we will use `Mig.Json.IO`.
So instead of more general `Mig` we will use `Mig.Json.IO`.

Also there are similiar modules for:
Also there are similar modules for:

* `IO`-based servers
* `Html` servers with generic monad
Expand All @@ -49,10 +49,10 @@ There is one reason why we do not do that for JSON. But we will study it later.
## Http request
In previous example we could query by static path. Let's do something more funcy
In previous example we could query by static path. Let's do something more fancy
and provide the input for the handler.
we have several types of inputs in HTTP:
We have several types of inputs in HTTP:
* query parameters. We can see them in the path `"api/get/route?queryName=queryValue"`
Expand All @@ -62,10 +62,10 @@ we have several types of inputs in HTTP:
* header parameters. They are in HTTP-request headers. For example header that
reports media-type of the request body: "Content-Type: application/json"
* request body. It is avalue packed into HTTP-request. It can be JSON or text or raw string
* request body. It is a value packed into HTTP-request. It can be JSON or text or raw string
or XML. All sorts of things can be used as request bodies.
To use any of HTTP inputs in the handler we use special newtype wrappers
To use any of HTTP inputs in the handler we use special `newtype` wrappers
as arguments to the handler functions.
### Query parameter example
Expand All @@ -83,9 +83,9 @@ Note that we have imported `Mig.IO.Json` and our types are more
specific and have fewer arguments. All types are dedicated to `IO` and `Json`.
So we can write `Get (Resp Text)` instead of `Get IO (Resp Json Text)`.

Interesting part of the handler is that qrgument: `Query "who" Text`.
Interesting part of the handler is that argument: `Query "who" Text`.
On the API level it creates expectation for a required query parameter in the path.
The `Query` is a simple newtype wrapper:
The `Query` is a simple `newtype` wrapper:

```haskell
newtype Query name value = Query value
Expand All @@ -99,12 +99,12 @@ server = "api/v1/hello" /. hello
```

There is no change because function `(/.)` is overloaded by second argument.
and it accepts all sorts of inputs. One of them states:
And it accepts all sorts of inputs. One of them states:

> if value `a` is convertible to server
> then `Query name value -> a` is also convertible to server
and by this magic as all haskell functions are curried we can use any number of
And by this magic as all Haskell functions are curried we can use any number of
queries in the handler. For example if we want to greet two persons we can write:

```haskell
Expand All @@ -124,7 +124,7 @@ add (Query a) (Query b) = Send $

### The rest of the inputs

All other input parameters work in the same way as a `Query`. we have a newtype wrapper
All other input parameters work in the same way as a `Query`. We have a `newtype` wrapper
for the value and type denotes all useful info for API description of the handler.

Let's for example query numbers for addition as capture parameters:
Expand All @@ -136,13 +136,13 @@ add (Query a) (Query b) = Send $
```

It will expect the path to be `"api/v1/add/2/4"`.
Other wrappers look very similiar:
Other wrappers look very similar:

* `Header name value` - for required headers
* `OptionalHeader name value` - for optional headers
* `Capture name value` - for path captures
* `Optional name value` - for optional queries
* `QueryFlag` - for booleab query that can be missing in the path (and then it is `false`)
* `QueryFlag` - for boolean query that can be missing in the path (and then it is `false`)
* `Body media value` - for request body


Expand All @@ -163,7 +163,7 @@ newtype AuthToken = AuthToken Text
deriving newtype (FromHttpApiData, Eq, Ord, ToParamSchema)
```

We can derive them for `newtype` wrappers. Aftr that we can use `AuthToken` as value
We can derive them for `newtype` wrappers. After that we can use `AuthToken` as value
to get from query parameter. For more info on how to derive those instances see the docs for the libraries.
It's easy to do. We can derive `Generic` for the data type and derive `ToParamSchema` with it.

Expand All @@ -172,7 +172,7 @@ The same instances we need for all parameters-like inputs: queries, headers, cap
### Nuances for Capture

The capture is interesting because it can be anywhere in the path.
for the example we havn't altered the server and our example:
For the example we haven't altered the server and our example:

```haskell
add :: Query "a" Int -> Query "b" Int -> Get (Resp Int)
Expand Down Expand Up @@ -231,15 +231,15 @@ In the core mig library the type `Body` has two type arguments. But as we use Js
the first argument for `Mig.Json.IO` as for `Mig.Json` is always `Json`-tag.
So those modules provide special case alternative for type `Body`. But in the `mig`
library it uses the same idea as we saw in the query parameter. It is just a
newtype wrapper for the value.
`newtype` wrapper for the value.

To be able to use it as input for the handler we have to provide instances for
several types:

* `FromJSON` from `aeson` library to parse value as JSON from byte string
* `ToSchema` from `openapi3` library to describe it in the API-schema

both of the types can be easily derived with `Generic` instance (from the module GHC.Generics).
Both of the types can be easily derived with `Generic` instance (from the module GHC.Generics).
First we derive instance of the `Generic` and then we can derive both `FromJSON` and `ToSchema`:

```haskell
Expand All @@ -250,8 +250,8 @@ data AddInput = AddInput
deriving (Generic, FromJSON, ToSchema)
```

also there are many libraries on hackage to
create custom derivings for those classes: `deriving-aeson`, `aeson-deriving` and many others.
Also there are many libraries on Hackage to
create custom drivings for those classes: `deriving-aeson`, `aeson-deriving` and many others.

So to use JSON request body we can define our own type, derive proper classes and
we are done.
Expand Down Expand Up @@ -368,7 +368,7 @@ curl -X 'GET' \

## Adding some goodies to the servers

There are some useful addons that make development of the servers
There are some useful add-ons that make development of the servers
much more pleasant. Let's discuss couple of them.

### Add swagger
Expand All @@ -377,7 +377,7 @@ Making `curl` request can quickly become hard to manage as
our servers become more complicated. There is OpenAPI standard
that defines how to describe HTTP-server API. Also it provides
Swagger. It is a tool to make it easy to check how server behaves.
It pprovides an HTTP-client for the server which allows us to
It provides an HTTP-client for the server which allows us to
query server routes.

Let's add a swagger to our server. Just add this line:
Expand All @@ -401,12 +401,12 @@ withSwagger :: SwaggerConfig m -> Server m -> Server m
```

We will study the `ServerConfig` in details in one of the next chapters
but for now the default value whcih is set with `def` from library `data-default`
but for now the default value which is set with `def` from library `data-default`
is fine.

### Add simple logs to the server

We can look at the request and trsponse data with tracing functions
We can look at the request and response data with tracing functions
which come from library `mig-extra` from the module `Mig.Extra.Plugin.Trace`:

```haskell
Expand All @@ -432,7 +432,7 @@ applyPlugin :: Plugin m -> Server m -> Server m
```

We show simplified signatures here. The real ones are overloaded by the first argument.
but we will dicuss plugins in depth in the separate chapter. For now it's
But we will discuss plugins in depth in the separate chapter. For now it's
ok to assume that those functions are defined in that simplified way.

So let's look at the data that goes through our server:
Expand Down Expand Up @@ -473,13 +473,13 @@ log:
type: http-response
```
This isan easy way to add addhock logs to the application.
This is an easy way to add add hock logs to the application.
Note that those logs are not aware of concurrency and will
report intermingled messages on concurrent queries.
We can add real loggs with more generic versions of the functions
We can add real logs with more generic versions of the functions
which accept callback and we can pass the logger function defined in terms
of one of the standard haskell logging libraries, say `katip` or `fast-logger`:
of one of the standard Haskell logging libraries, say `katip` or `fast-logger`:

```haskell
import Data.Aeson as Json
Expand All @@ -490,22 +490,22 @@ logHttpBy :: (Json.Value -> m ()) -> Verbosity -> Plugin m
## Summary

We have learned how various parts of the requests can be queries
with newtype wrappers. There are only handful of them.
we can query
with `newtype` wrappers. There are only handful of them.
We can query

* `Query name value` - for required queries
* `Body media value` - for request body
* `Optional name value` - for optional queries
* `Header name value` - for required headers
* `OptionalHeader name value` - for optional headers
* `Capture name value` - for path captures
* `QueryFlag` - for booleab query that can be missing in the path (and then it is `false`)
* `QueryFlag` - for boolean query that can be missing in the path (and then it is `false`)

We have learned to use specialized versions for servers which operate
only in terms of `IO` or `Json`. We can import the module `Mig.Json.IO`
and our signatures would bcome more simple and specific.
and our signatures would become more simple and specific.

we have learned how by ony-liners we can add to the server some useful features:
We have learned how by one-liners we can add to the server some useful features:

* swagger: `(withSwagger def server)`
For calls to the server in the UI
Expand Down
Loading

0 comments on commit 9a8092b

Please sign in to comment.