From b7792015ad8b7f2081bfdb61c9f4712a07457e81 Mon Sep 17 00:00:00 2001 From: daveads Date: Thu, 25 Apr 2024 16:19:04 +0100 Subject: [PATCH 1/8] restructed sidebarjs and create tutorials section --- docs/sidebars.js | 96 +++++++++++++++-------- docs/{ => tutorials}/testing-http-apps.md | 0 2 files changed, 63 insertions(+), 33 deletions(-) rename docs/{ => tutorials}/testing-http-apps.md (100%) diff --git a/docs/sidebars.js b/docs/sidebars.js index 5dde97296f..d5948d9b3e 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -1,102 +1,132 @@ const sidebars = { + sidebar: [ { + type: "category", label: "ZIO HTTP", collapsed: true, link: { type: "doc", id: "index" }, items: [ - "installation", - "overview", + "installation", + "overview", + { + // Subcategory: Reference type: "category", collapsed: true, label: "Reference", items: [ - "reference/server", + "reference/server", "reference/client", + "reference/template", + "reference/handler", + "reference/endpoint", + { + // Subcategory: Routing type: "category", label: "Routing", items: [ - "reference/routes", - "reference/route_pattern", - "reference/path_codec", + "reference/routes", + "reference/route_pattern", + "reference/path_codec", ], }, - "reference/handler", + + { + // Subcategory: HTTP Messages type: "category", label: "HTTP Messages", items: [ { + // Sub-subcategory: Headers type: "category", label: "Headers", items: [ "reference/headers", "reference/cookies", - "reference/flash", + "reference/flash", ], }, - "reference/request", + + "reference/request", { + // Sub-subcategory: Response type: "category", label: "Response", - items: ["reference/response", "reference/status"], + items: ["reference/response", "reference/status"], }, + { + // Sub-subcategory: Message Body type: "category", label: "Message Body", items: ["reference/body", "reference/form"], }, ], - }, - "reference/endpoint", + }, + { + // Subcategory: HTTP Middleware type: "category", label: "HTTP Middleware", items: [ "reference/protocol-stack", - "reference/middleware", - "reference/handler_aspect", + "reference/middleware", + "reference/handler_aspect", ], }, + { + // Subcategory: WebSocket type: "category", label: "WebSocket", items: [ - "reference/socket/socket", - "reference/socket/websocketframe", + "reference/socket/socket", + "reference/socket/websocketframe", ], }, - "reference/template", + ], }, - "binary_codecs", - "testing-http-apps", - "faq", + { + // Subcategory: Tutorials + type: "category", + label: "Tutorials", + items: [ + "tutorials/testing-http-apps", + ], + }, + + { + // Subcategory: Examples type: "category", label: "Examples", link: { type: "doc", id: "index" }, items: [ - "examples/hello-world", - "examples/http-client-server", + "examples/hello-world", + "examples/http-client-server", "examples/https-client-server", - "examples/serving-static-files", + "examples/serving-static-files", "examples/html-templating", - "examples/websocket", - "examples/streaming", - "examples/endpoint", - "examples/middleware-cors-handling", - "examples/authentication", - "examples/graceful-shutdown", - "examples/cli", - "examples/concrete-entity", - "examples/multipart-form-data", - "examples/server-sent-events-in-endpoints", + "examples/websocket", + "examples/streaming", + "examples/endpoint", + "examples/middleware-cors-handling", + "examples/authentication", + "examples/graceful-shutdown", + "examples/cli", + "examples/concrete-entity", + "examples/multipart-form-data", + "examples/server-sent-events-in-endpoints", ], }, + + "binary_codecs", + "faq", ], }, ], diff --git a/docs/testing-http-apps.md b/docs/tutorials/testing-http-apps.md similarity index 100% rename from docs/testing-http-apps.md rename to docs/tutorials/testing-http-apps.md From 1dd6f15b567623d987ad20adb126a3269d86b41a Mon Sep 17 00:00:00 2001 From: daveads Date: Thu, 25 Apr 2024 16:38:43 +0100 Subject: [PATCH 2/8] added doc card --- docs/examples/index.md | 10 ++++++++++ docs/reference/index.md | 10 ++++++++++ docs/sidebars.js | 3 ++- 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 docs/examples/index.md create mode 100644 docs/reference/index.md diff --git a/docs/examples/index.md b/docs/examples/index.md new file mode 100644 index 0000000000..b39278f1f4 --- /dev/null +++ b/docs/examples/index.md @@ -0,0 +1,10 @@ +--- +id: index +title: "Zio http Examples" +--- + +This section aims to demonstrate the usage of key concepts and ideas in the ZIO HTTP library with examples. + +import DocCardList from '@theme/DocCardList'; + + \ No newline at end of file diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 0000000000..2c84e91eb6 --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,10 @@ +--- +id: index +title: "Zio http Reference" +--- + +This section offers a detailed reference for the essential concepts and ideas in the ZIO HTTP library. + +import DocCardList from '@theme/DocCardList'; + + \ No newline at end of file diff --git a/docs/sidebars.js b/docs/sidebars.js index d5948d9b3e..ef65fffef5 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -16,6 +16,7 @@ const sidebars = { type: "category", collapsed: true, label: "Reference", + link: { type: "doc", id: "reference/index" }, items: [ "reference/server", "reference/client", @@ -105,7 +106,7 @@ const sidebars = { // Subcategory: Examples type: "category", label: "Examples", - link: { type: "doc", id: "index" }, + link: { type: "doc", id: "examples/index" }, items: [ "examples/hello-world", "examples/http-client-server", From f812c938fded412ceb280ef1009a6ebd45971d62 Mon Sep 17 00:00:00 2001 From: daveads Date: Sat, 27 Apr 2024 11:42:01 +0100 Subject: [PATCH 3/8] sidebar >> overview --- docs/reference/overview.md | 246 +++++++++++++++++++++++++++++++++++++ docs/sidebars.js | 82 ++++++++----- 2 files changed, 294 insertions(+), 34 deletions(-) create mode 100644 docs/reference/overview.md diff --git a/docs/reference/overview.md b/docs/reference/overview.md new file mode 100644 index 0000000000..8893b98d5e --- /dev/null +++ b/docs/reference/overview.md @@ -0,0 +1,246 @@ +--- +id: overview +title: Introduction to ZIO HTTP +--- + +ZIO HTTP offers an expressive API for creating HTTP applications. It uses a domain-specific language (DSL) to define routes and handlers. Both server and client are designed in terms of **HTTP as a function**, so they are functions from `Request` to `Response`. + +## Core Concepts + +ZIO HTTP has powerful functional domains that help in creating, modifying, and composing apps easily. Let's take a look at the core domain: + +- `Routes` - A collection of `Route`s. If the error type of the routes is `Response`, then they can be served. +- `Route` - A single route that can be matched against an HTTP `Request` and produce a `Response`. It comprises a `RoutePattern` and a `Handler`: + 1. `RoutePattern` - A pattern that can be matched against an HTTP `Request`. It is a combination of `Method` and `PathCodec` which can be used to match the `Method` and `Path` of the `Request`. + 2. `Handler` - A function that can convert a `Request` into a `Response`. + +Let's see each of these concepts inside a simple example: + +```scala mdoc:silent +import zio._ +import zio.http._ + +object ExampleServer extends ZIOAppDefault { + + // A route that matches GET requests to /greet + // It doesn't require any service from the ZIO environment + // so the first type parameter is Any + // All its errors are handled so the second type parameter is Nothing + val greetRoute: Route[Any, Nothing] = + // The whole Method.GET / "greet" is a RoutePattern + Method.GET / "greet" -> + // The handler is a function that takes a Request and returns a Response + handler { (req: Request) => + val name = req.queryParamToOrElse("name", "World") + Response.text(s"Hello $name!") + } + + // A route that matches POST requests to /echo + // It doesn't require any service from the ZIO environment + // It is an unhandled route so the second type parameter is something other than Nothing + val echoRoute: Route[Any, Throwable] = + Method.POST / "echo" -> handler { (req: Request) => + req.body.asString.map(Response.text(_)) + } + + // The Routes that don't require any service from the ZIO environment, + // so the first type parameter is Any. + // All the errors are handled by turning them into a Response. + val routes: Routes[Any, Response] = + // List of all the routes + Routes(greetRoute, echoRoute) + // Handle all unhandled errors + .handleError(e => Response.internalServerError(e.getMessage)) + + // Serving the routes using the default server layer on port 8080 + def run = Server.serve(routes).provide(Server.default) +} +``` + +### 1.Routes + +The `Routes` is a collection of `Route` values. It can be created using its default constructor: + +```scala mdoc:invisible +import zio.http._ + +val greetRoute: Route[Any, Nothing] = Route.notFound +val echoRoute: Route[Any, Throwable] = Route.notFound +``` + +```scala mdoc:silent +import zio.http._ + +val app: Routes[Any, Response] = + Routes(greetRoute, echoRoute) + .handleError(e => Response.internalServerError(e.getMessage)) +``` + +The `Handler` and `Route` can be transformed to `Routes` by the `.toRoutes` method. To serve the routes, all errors should be handled by converting them into a `Response` using for example the `.handleError` method. + +For handling routes, ZIO HTTP has a [`Routes`](routing/routes.md) value, which allows us to aggregate a collection of individual routes. Behind the scenes, ZIO HTTP builds an efficient prefix-tree whenever needed to optimize dispatch. + +### 2. Route + +Each `Route` is a combination of a [`RoutePattern`](routing/route_pattern.md) and a [`Handler`](handler.md). The `RoutePattern` is a combination of a `Method` and a [`PathCodec`](routing/path_codec.md) that can be used to match the method and path of the request. The `Handler` is a function that can convert a `Request` into a `Response`. + +The `PathCodec` can be parameterized to extract values from the path. In such cases, the `Handler` should be a function that accepts the extracted values besides the `Request`: + +```scala mdoc:silent:nest +import zio.http._ + +val routes = Routes( + Method.GET / "user" / int("id") -> + handler { (id: Int, req: Request) => + Response.text(s"Requested User ID: $id") + } +) +``` + +To learn more about routes, see the [Routes](routing/routes.md) page. + +### 3. Handler + +The `Handler` describes the transformation from an incoming `Request` to an outgoing `Response`: + +```scala mdoc:compile-only +val helloHandler = + handler { (_: Request) => + Response.text("Hello World!") + } +``` + +The `Handler` can be effectful, in which case it should be a function that returns a `ZIO` effect, e.g.: + +```scala mdoc:compile-only +val randomGeneratorHandler = + handler { (_: Request) => + Random.nextIntBounded(100).map(_.toString).map(Response.text(_)) + } +``` + +There are several ways to create a `Handler`, to learn more about handlers, see the [Handlers](reference/handler.md) page. + +## Accessing the Request + +To access the request, just create a handler that accepts the request: + +```scala mdoc:silent:reset +import zio.http._ +import zio._ + +val routes = Routes( + Method.GET / "fruits" / "a" -> handler { (req: Request) => + Response.text("URL:" + req.url.path.toString + " Headers: " + req.headers) + }, + + Method.POST / "fruits" / "a" -> handler { (req: Request) => + req.body.asString.map(Response.text(_)) + } +) +``` + +To learn more about the request, see the [Request](request.md) page. + +## Accessing Services from The Environment + +ZIO HTTP is built on top of ZIO, which means that we can access services from the environment in our handlers. For example, we can access a `Ref[Int]` service to create a simple counter: + +```scala mdoc:compile-only +import zio._ +import zio.http._ + +object CounterExample extends ZIOAppDefault { + val routes: Routes[Ref[Int], Response] = + Routes( + Method.GET / "count" / int("n") -> + handler { (n: Int, _: Request) => + for { + ref <- ZIO.service[Ref[Int]] + res <- ref.updateAndGet(_ + n) + } yield Response.text(s"Counter: $res") + }, + ) + + def run = Server.serve(routes).provide(Server.default, ZLayer.fromZIO(Ref.make(0))) +} +``` + +Finally, we should provide the required services to the server using the `provide` method. In the above example, we provided the `Ref[Int]` service using the `ZLayer.fromZIO` method. + +## WebSocket Connection + +To handle WebSocket connections, we can use `Handler.webSocket` to create a socket app. To create a socket app, we need to create a socket that accepts `WebSocketChannel` and produces `ZIO`. Finally, we need to convert socketApp to `Response` using `toResponse`, so that we can run it like any other HTTP app. + +The below example shows a simple socket app, which sends `WebsSocketTextFrame` "BAR" on receiving `WebsSocketTextFrame` "FOO": + +```scala mdoc:silent:reset +import zio.http._ +import zio.stream._ +import zio._ + +val socket = + Handler.webSocket { channel => + channel.receiveAll { + case ChannelEvent.Read(WebSocketFrame.Text("FOO")) => + channel.send(ChannelEvent.Read(WebSocketFrame.text("BAR"))) + case _ => + ZIO.unit + } + } + +val routes = + Routes( + Method.GET / "greet" / string("name") -> handler { (name: String, req: Request) => + Response.text(s"Greetings {$name}!") + }, + Method.GET / "ws" -> handler(socket.toResponse) + ) +``` + +We have a more detailed explanation of the WebSocket connection on the [Socket](socket/socket.md) page. + +## Server + +As we have seen how to create HTTP apps, the only thing left is to run an HTTP server and serve requests. + +ZIO HTTP provides a way to set configurations for our server. The server can be configured according to the leak detection level, request size, address etc. + +To launch our app, we need to start the server on a port. The below example shows a simple HTTP app that responds with empty content and a `200` status code, deployed on port `8090` using `Server.start`: + +```scala mdoc:silent:reset +import zio.http._ +import zio._ + +object HelloWorld extends ZIOAppDefault { + val routes = Handler.ok.toRoutes + + override def run = + Server.serve(routes).provide(Server.defaultWithPort(8090)) +} +``` + +Finally, we provided the default server with the port `8090` to the app. To learn more about the server, see the [Server](server.md) page. + +## Client + +Besides creating HTTP apps, ZIO HTTP also provides a way to create HTTP clients. The client can be used to send requests to the server and receive responses: + +```scala mdoc:compile-only +import zio._ +import zio.http._ + +object ClientExample extends ZIOAppDefault { + + val app = + for { + client <- ZIO.serviceWith[Client](_.host("localhost").port(8090)) + response <- client.request(Request.get("/")) + _ <- ZIO.debug("Response Status: " + response.status) + } yield () + + def run = app.provide(Client.default, Scope.default) +} +``` + +In the above example, we obtained the `Client` service from the environment and sent a `GET` request to the server. Finally, to run the client app, we provided the default `Client` and `Scope` services to the app. For more information about the client, see the [Client](client.md) page. \ No newline at end of file diff --git a/docs/sidebars.js b/docs/sidebars.js index f9bcbde67a..2398ee00b6 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -2,84 +2,89 @@ const sidebars = { sidebar: [ { - type: "category", label: "ZIO HTTP", collapsed: true, + + // main documentation index link: { type: "doc", id: "index" }, items: [ - "installation", + "installation", + + + // Reference section { - // Subcategory: Reference type: "category", collapsed: true, link: { type: "doc", id: "reference/index" }, label: "Reference", - link: { type: "doc", id: "reference/index" }, items: [ + "reference/overview", "reference/server", - "reference/client", - "reference/template", + "reference/client", "reference/handler", - "reference/endpoint", + "reference/endpoint", + // Routing subsection { - // Subcategory: Routing type: "category", label: "Routing", items: [ "reference/routing/routes", - "reference/routing/route_pattern", + "reference/routing/route_pattern", "reference/routing/path_codec", ], }, - + // HTTP Messages subsection { - // Subcategory: HTTP Messages type: "category", label: "HTTP Messages", items: [ "reference/request", "reference/response/response", + "reference/response/status", + + // Headers subsection { - // Sub-subcategory: Headers type: "category", label: "Headers", items: [ - "reference/headers/headers", - "reference/headers/session/cookies", - "reference/headers/session/flash", + "reference/headers/headers", + "reference/headers/session/cookies", + "reference/headers/session/flash", ], }, + // Message Body subsection { - // Sub-subcategory: Message Body type: "category", label: "Message Body", items: [ - "reference/body/body", + "reference/body/body", "reference/body/form", "reference/body/binary_codecs", - "reference/body/template", + "reference/body/template", ], }, - "reference/response/status", + ], }, - "reference/endpoint", + + + // Aspects subsection { type: "category", label: "Aspects", items: [ - "reference/aop/protocol-stack", - "reference/aop/middleware", - "reference/aop/handler_aspect", + "reference/aop/protocol-stack", + "reference/aop/middleware", + "reference/aop/handler_aspect", ], }, + // WebSocket subsection { - // Subcategory: WebSocket type: "category", label: "WebSocket", items: [ @@ -89,36 +94,45 @@ const sidebars = { }, ], }, - "testing-http-apps", - "faq", + + + { + // Subcategory: Tutorials + type: "category", + label: "Tutorials", + items: [ + "tutorials/testing-http-apps", + ], + }, + + // Examples section { type: "category", label: "Examples", - link: { type: "doc", id: "examples/index" }, + link: { type: "doc", id: "index" }, items: [ "examples/hello-world", "examples/http-client-server", - "examples/https-client-server", - "examples/serving-static-files", - "examples/html-templating", + "examples/https-client-server", + "examples/serving-static-files", + "examples/html-templating", "examples/websocket", "examples/streaming", "examples/endpoint", "examples/middleware-cors-handling", "examples/authentication", - "examples/graceful-shutdown", + "examples/graceful-shutdown", "examples/cli", "examples/concrete-entity", "examples/multipart-form-data", - "examples/server-sent-events-in-endpoints", + "examples/server-sent-events-in-endpoints", ], }, - "binary_codecs", "faq", ], }, ], }; -module.exports = sidebars; +module.exports = sidebars; \ No newline at end of file From 7221d05c71ddeebd88a97e956a0e97520a29ae87 Mon Sep 17 00:00:00 2001 From: Adejumo David Adewale Date: Sat, 27 Apr 2024 11:08:31 +0000 Subject: [PATCH 4/8] fix faq link --- docs/faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/faq.md b/docs/faq.md index 4ba3609e84..b48aded922 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -22,7 +22,7 @@ ZIO HTTP provides built-in support for JSON serialization and deserialization us ### How Can I Handle CORS Requests in ZIO HTTP? -ZIO has several middlewares including `CORS` that can be used to handle cross-origin resource sharing requests. Check out the [Middleware](./reference/middleware.md) section in the documentation for more details. +ZIO has several middlewares including `CORS` that can be used to handle cross-origin resource sharing requests. Check out the [Middleware](./reference/aop/middleware) section in the documentation for more details. ### How Does ZIO HTTP Handle Errors? From cfcf15b3a8023cf628d8096aaaa4344b48d46d60 Mon Sep 17 00:00:00 2001 From: Adejumo David Adewale Date: Sat, 27 Apr 2024 13:01:14 +0000 Subject: [PATCH 5/8] fix sidebar.js --- docs/reference/overview.md | 2 +- docs/sidebars.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/reference/overview.md b/docs/reference/overview.md index 8893b98d5e..69aef59001 100644 --- a/docs/reference/overview.md +++ b/docs/reference/overview.md @@ -1,6 +1,6 @@ --- id: overview -title: Introduction to ZIO HTTP +title: Overview --- ZIO HTTP offers an expressive API for creating HTTP applications. It uses a domain-specific language (DSL) to define routes and handlers. Both server and client are designed in terms of **HTTP as a function**, so they are functions from `Request` to `Response`. diff --git a/docs/sidebars.js b/docs/sidebars.js index 2398ee00b6..bf657e41df 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -1,5 +1,4 @@ const sidebars = { - sidebar: [ { type: "category", @@ -95,7 +94,6 @@ const sidebars = { ], }, - { // Subcategory: Tutorials type: "category", @@ -104,12 +102,12 @@ const sidebars = { "tutorials/testing-http-apps", ], }, - + // Examples section { type: "category", label: "Examples", - link: { type: "doc", id: "index" }, + link: { type: "doc", id: "examples/index" }, items: [ "examples/hello-world", "examples/http-client-server", From 0b639447186ae1cc19b7e2f41386559a9d8c3d88 Mon Sep 17 00:00:00 2001 From: Adejumo David Adewale Date: Sun, 28 Apr 2024 14:50:57 +0100 Subject: [PATCH 6/8] Update docs/examples/index.md change title Co-authored-by: Milad Khajavi --- docs/examples/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/examples/index.md b/docs/examples/index.md index b39278f1f4..26cad92ce0 100644 --- a/docs/examples/index.md +++ b/docs/examples/index.md @@ -1,6 +1,7 @@ --- id: index -title: "Zio http Examples" +title: "ZIO HTTP Examples" +sidebar_label: Examples --- This section aims to demonstrate the usage of key concepts and ideas in the ZIO HTTP library with examples. From 35bb405f6a9cc6f2ac8155c5ca60b64cde975b8e Mon Sep 17 00:00:00 2001 From: Adejumo David Adewale Date: Sun, 28 Apr 2024 14:51:39 +0100 Subject: [PATCH 7/8] Update docs/reference/index.md reference Co-authored-by: Milad Khajavi --- docs/reference/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/reference/index.md b/docs/reference/index.md index 2c84e91eb6..5c9047c726 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -1,6 +1,7 @@ --- id: index -title: "Zio http Reference" +title: "ZIO HTTP Reference" +sidebar_label: Reference --- This section offers a detailed reference for the essential concepts and ideas in the ZIO HTTP library. From d7f606de88041847976e7c9769e244d506d2bc32 Mon Sep 17 00:00:00 2001 From: daveads Date: Sun, 28 Apr 2024 14:59:41 +0100 Subject: [PATCH 8/8] mv handler under routing section --- docs/sidebars.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sidebars.js b/docs/sidebars.js index bf657e41df..3fd14961e5 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -10,7 +10,6 @@ const sidebars = { items: [ "installation", - // Reference section { type: "category", @@ -21,7 +20,6 @@ const sidebars = { "reference/overview", "reference/server", "reference/client", - "reference/handler", "reference/endpoint", // Routing subsection @@ -34,6 +32,8 @@ const sidebars = { "reference/routing/path_codec", ], }, + + "reference/handler", // HTTP Messages subsection {