@@ -204,7 +204,7 @@ And that's it! You can run this example in the same way that we showed for
205Fine, we can write trivial webservices easily, but none of the two above use
205Fine, we can write trivial webservices easily, but none of the two above use
206any "fancy" combinator from servant. Let's address this and use `QueryParam`,
206any "fancy" combinator from servant. Let's address this and use `QueryParam`,
207−`Capture` and `ReqBody` right away. You'll see how each occurence of these
207+`Capture` and `ReqBody` right away. You'll see how each occurrence of these
208combinators in an endpoint makes the corresponding handler receive an
208combinators in an endpoint makes the corresponding handler receive an
209argument of the appropriate type automatically. You don't have to worry about
209argument of the appropriate type automatically. You don't have to worry about
210manually looking up URL captures or query string parameters, or
210manually looking up URL captures or query string parameters, or
@@ -1092,7 +1092,7 @@ We can write some simple webservice with the handlers running in `Reader String`
1093``` haskell
1093``` haskell
1094type ReaderAPI = "a" :> Get '[JSON] Int
1094type ReaderAPI = "a" :> Get '[JSON] Int
1095− :<|> "b" :> ReqBody '[JSON] Double :> Get '[JSON] Bool
1095+ :<|> "b" :> ReqBody '[JSON] Double :> Get '[JSON] Bool
1097readerAPI :: Proxy ReaderAPI
1097readerAPI :: Proxy ReaderAPI
1098readerAPI = Proxy
1098readerAPI = Proxy
@@ -1114,7 +1114,7 @@ We unfortunately can't use `readerServerT` as an argument of `serve`, because
1114That's right. We have just written `readerToHandler`, which is exactly what we
1114That's right. We have just written `readerToHandler`, which is exactly what we
1115would need to apply to all handlers to make the handlers have the
1115would need to apply to all handlers to make the handlers have the
1116right type for `serve`. Being cumbersome to do by hand, we provide a function
1116right type for `serve`. Being cumbersome to do by hand, we provide a function
1117−`hoistServer` which takes a natural transformation between two parametrized types `m`
1117+`hoistServer` which takes a natural transformation between two parameterized types `m`
1118and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1118and `n` and a `ServerT someapi m`, and returns a `ServerT someapi n`.
1120In our case, we can wrap up our little webservice by using
1120In our case, we can wrap up our little webservice by using
@@ -1164,6 +1164,27 @@ app5 :: Application
1164app5 = serve readerAPI (hoistServer readerAPI funToHandler funServerT)
1164app5 = serve readerAPI (hoistServer readerAPI funToHandler funServerT)
1167+## Streaming endpoints
1169+We can create endpoints that don't just give back a single result, but give back a *stream* of results, served one at a time. Stream endpoints only provide a single content type, and also specify what framing strategy is used to delineate the results. To serve these results, we need to give back a stream producer. Adapters can be written to `Pipes`, `Conduit` and the like, or written directly as `StreamGenerator`s. StreamGenerators are IO-based continuations that are handed two functions -- the first to write the first result back, and the second to write all subsequent results back. (This is to allow handling of situations where the entire stream is prefixed by a header, or where a boundary is written between elements, but not prior to the first element). The API of a streaming endpoint needs to explicitly specify which sort of generator it produces. Note that the generator itself is returned by a `Handler` action, so that additional IO may be done in the creation of one.
1172+type StreamAPI = "userStream" :> StreamGet NewlineFraming JSON (StreamGenerator User)
1173+streamAPI :: Proxy StreamAPI
1176+streamUsers :: StreamGenerator User
1177+streamUsers = StreamGenerator $ \sendFirst sendRest -> do
1183+app6 = serve streamAPI (return streamUsers)
1186+This simple application returns a stream of `User` values encoded in JSON format, with each value separated by a newline. In this case, the stream will consist of the value of `isaac`, followed by the value of `albert`, then the value of `albert` a third time. Importantly, the stream is written back as results are produced, rather than all at once. This means first that results are delivered when they are available, and second, that if an exception interrupts production of the full stream, nonetheless partial results have already been written back.
1167## Conclusion
1188## Conclusion
1169You're now equipped to write webservices/web-applications using
1190You're now equipped to write webservices/web-applications using