@zio.dev/zio-blocks 0.0.33 → 0.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/guides/compile-time-resource-safety-with-scope.md +16 -17
- package/guides/getting-started-with-mux.md +1507 -0
- package/guides/query-dsl-extending.md +161 -102
- package/guides/query-dsl-fluent-builder.md +217 -157
- package/guides/query-dsl-reified-optics.md +12 -10
- package/guides/query-dsl-sql.md +246 -165
- package/guides/telemetry-guide.md +1069 -0
- package/guides/zio-schema-migration.md +29 -22
- package/index.md +292 -50
- package/package.json +1 -1
- package/plans/config-follow-up-prs.md +188 -0
- package/plans/config-pr-assessment-roadmap.md +310 -0
- package/reference/MuxDataFlow.jsx +250 -0
- package/reference/async.md +651 -0
- package/reference/chunk.md +3533 -308
- package/reference/codegen/case-class.md +436 -0
- package/reference/codegen/emitter-config.md +383 -0
- package/reference/codegen/examples.md +664 -0
- package/reference/codegen/field.md +316 -0
- package/reference/codegen/index.md +317 -0
- package/reference/codegen/scala-emitter.md +392 -0
- package/reference/codegen/scala-file.md +276 -0
- package/reference/codegen/sealed-trait.md +408 -0
- package/reference/codegen/type-definition.md +340 -0
- package/reference/codegen/type-ref.md +201 -0
- package/reference/combinators.md +347 -117
- package/reference/config.md +158 -0
- package/reference/context.md +4 -4
- package/reference/datastar.md +346 -0
- package/reference/docs.md +1461 -345
- package/reference/endpoint/auth-type.md +146 -0
- package/reference/endpoint/endpoint.md +297 -0
- package/reference/endpoint/http-codec.md +249 -0
- package/reference/endpoint/index.md +825 -0
- package/reference/endpoint/path-codec.md +237 -0
- package/reference/endpoint/route-pattern.md +196 -0
- package/reference/endpoint/route-tree.md +111 -0
- package/reference/endpoint/segment-codec.md +212 -0
- package/reference/html.md +1120 -0
- package/reference/htmx/attribute-values.md +359 -0
- package/reference/htmx/hx-encoding.md +111 -0
- package/reference/htmx/hx-params.md +204 -0
- package/reference/htmx/hx-swap.md +276 -0
- package/reference/htmx/hx-sync.md +251 -0
- package/reference/htmx/hx-target.md +314 -0
- package/reference/htmx/hx-trigger.md +457 -0
- package/reference/htmx/hx-url-update.md +239 -0
- package/reference/htmx/index.md +855 -0
- package/reference/http-model/index.md +47 -0
- package/reference/http-model/model.md +1481 -0
- package/reference/http-model/schema.md +747 -0
- package/reference/maybe.md +826 -0
- package/reference/media-type.md +2 -2
- package/reference/mux.mdx +823 -0
- package/reference/openapi.md +1351 -0
- package/reference/resource-management/defer-handle.md +1 -1
- package/reference/resource-management/resource.md +31 -2
- package/reference/resource-management/scope.md +28 -12
- package/reference/resource-management/wire.md +3 -7
- package/reference/ringbuffer/MpmcDiagram.jsx +717 -0
- package/reference/ringbuffer/MpscDiagram.jsx +618 -0
- package/reference/ringbuffer/SpmcDiagram.jsx +680 -0
- package/reference/ringbuffer/SpscDiagram.jsx +677 -0
- package/reference/ringbuffer/advanced.mdx +109 -0
- package/reference/ringbuffer/index.mdx +145 -0
- package/reference/ringbuffer/mpmc.mdx +151 -0
- package/reference/ringbuffer/mpsc.mdx +132 -0
- package/reference/ringbuffer/spmc.mdx +108 -0
- package/reference/ringbuffer/spsc.mdx +344 -0
- package/reference/{allows.md → schema/allows.md} +4 -4
- package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
- package/reference/{binding.md → schema/binding.md} +2 -3
- package/reference/schema/built-in-codecs/avro.md +451 -0
- package/reference/schema/built-in-codecs/bson.md +480 -0
- package/reference/schema/built-in-codecs/csv.md +564 -0
- package/reference/schema/built-in-codecs/index.md +77 -0
- package/reference/schema/built-in-codecs/json/index.md +295 -0
- package/reference/schema/built-in-codecs/json/json-config.md +217 -0
- package/reference/{json-patch.md → schema/built-in-codecs/json/json-patch.md} +5 -5
- package/reference/{json-schema.md → schema/built-in-codecs/json/json-schema.md} +14 -47
- package/reference/schema/built-in-codecs/json/json-selection.md +322 -0
- package/reference/{json.md → schema/built-in-codecs/json/json.md} +32 -64
- package/reference/schema/built-in-codecs/messagepack.md +508 -0
- package/reference/schema/built-in-codecs/thrift.md +433 -0
- package/reference/schema/built-in-codecs/toon.md +1078 -0
- package/reference/{xml.md → schema/built-in-codecs/xml.md} +13 -9
- package/reference/schema/built-in-codecs/yaml.md +552 -0
- package/reference/{codec.md → schema/codec.md} +10 -10
- package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +151 -5
- package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
- package/reference/schema/format.md +92 -0
- package/reference/schema/index.md +50 -0
- package/reference/schema/migration.md +297 -0
- package/reference/{modifier.md → schema/modifier.md} +58 -7
- package/reference/{optics.md → schema/optics.md} +2 -2
- package/reference/{patch.md → schema/patch.md} +1 -1
- package/{path-interpolator.md → reference/schema/path-interpolator.md} +165 -72
- package/reference/{schema-evolution → schema/schema-evolution}/as.md +8 -8
- package/reference/{schema-evolution → schema/schema-evolution}/index.md +2 -2
- package/reference/{schema-evolution → schema/schema-evolution}/into.md +8 -8
- package/reference/{schema-expr.md → schema/schema-expr.md} +110 -175
- package/reference/{schema.md → schema/schema.md} +12 -0
- package/reference/{structural-types.md → schema/structural-types.md} +1 -1
- package/reference/{type-class-derivation.md → schema/type-class-derivation.md} +63 -1
- package/reference/smithy.md +533 -0
- package/reference/sql/db-codec-deriver.md +71 -0
- package/reference/sql/db-codec.md +687 -0
- package/reference/sql/db-con.md +271 -0
- package/reference/sql/db-connection.md +153 -0
- package/reference/sql/db-param-writer.md +77 -0
- package/reference/sql/db-param.md +66 -0
- package/reference/sql/db-result-reader.md +146 -0
- package/reference/sql/db-tx.md +82 -0
- package/reference/sql/db-value.md +41 -0
- package/reference/sql/ddl.md +85 -0
- package/reference/sql/frag.md +254 -0
- package/reference/sql/index.md +341 -0
- package/reference/sql/repo.md +600 -0
- package/reference/sql/sql-dialect.md +73 -0
- package/reference/sql/sql-logger.md +62 -0
- package/reference/sql/sql-name-mapper.md +70 -0
- package/reference/sql/table-metadata.md +134 -0
- package/reference/sql/table.md +448 -0
- package/reference/sql/transactor-zio.md +399 -0
- package/reference/sql/transactor.md +353 -0
- package/reference/sql-zio.md +112 -0
- package/reference/streams/concurrent-operators.md +106 -0
- package/reference/streams/index.md +653 -0
- package/reference/streams/pipeline.md +718 -0
- package/reference/streams/reader.md +1284 -0
- package/reference/streams/scala-2-compatibility.md +55 -0
- package/reference/streams/sink.md +1426 -0
- package/reference/streams/stream.md +2526 -0
- package/reference/streams/writer.md +1045 -0
- package/reference/streams/zero-boxing.md +275 -0
- package/reference/telemetry.md +693 -0
- package/reference/typeid.md +5 -19
- package/sidebars.js +238 -43
- package/reference/formats.md +0 -694
- package/reference/http-model.md +0 -1716
- package/reference/streams.md +0 -989
- package/ringbuffer.md +0 -249
- /package/reference/{json-differ.md → schema/built-in-codecs/json/json-differ.md} +0 -0
- /package/reference/{dynamic-value.md → schema/dynamic-value.md} +0 -0
- /package/reference/{lazy.md → schema/lazy.md} +0 -0
- /package/reference/{reflect.md → schema/reflect.md} +0 -0
- /package/reference/{registers.md → schema/registers.md} +0 -0
- /package/reference/{schema-error.md → schema/schema-error.md} +0 -0
- /package/reference/{syntax.md → schema/syntax.md} +0 -0
- /package/reference/{validation.md → schema/validation.md} +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: auth-type
|
|
3
|
+
title: "AuthType"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`AuthType` is a sealed trait that describes an HTTP authentication scheme as a first-class type parameter on `Endpoint`. Each `AuthType` variant carries an associated type `ClientRequirement` — the type of credential the client must provide — and a codec that extracts it from the request. Its definition is:
|
|
7
|
+
|
|
8
|
+
```scala
|
|
9
|
+
sealed trait AuthType {
|
|
10
|
+
type ClientRequirement
|
|
11
|
+
def codec: HttpCodec[CodecKind.Request, ClientRequirement]
|
|
12
|
+
def unauthorizedStatus: Status
|
|
13
|
+
}
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Motivation
|
|
17
|
+
|
|
18
|
+
Authentication requirements are often encoded informally — a comment in the handler, a middleware convention, or a bare string header check. `AuthType` makes the auth requirement part of the endpoint's static type. A bearer-secured endpoint has type `Endpoint[..., AuthType.Bearer]`, which means:
|
|
19
|
+
|
|
20
|
+
- The `auth.codec` field is typed as `HttpCodec[CodecKind.Request, zio.http.Header.Authorization.Bearer]`, not `HttpCodec[..., String]`.
|
|
21
|
+
- Interpreters (server, client, OpenAPI) can inspect the auth type without stringly-typed reflection.
|
|
22
|
+
- Composing auth types with `|` produces a union that the compiler verifies is discriminated.
|
|
23
|
+
|
|
24
|
+
## Built-in Variants
|
|
25
|
+
|
|
26
|
+
Each built-in variant maps to a specific HTTP authorization scheme. Use the one that matches your API's authentication model.
|
|
27
|
+
|
|
28
|
+
### `AuthType.None`
|
|
29
|
+
|
|
30
|
+
The default auth type — no authentication required. Its `ClientRequirement` is `Unit` and its codec is `HttpCodec.Empty`:
|
|
31
|
+
|
|
32
|
+
```scala
|
|
33
|
+
import zio.blocks.endpoint._
|
|
34
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
35
|
+
import zio.http.Method
|
|
36
|
+
|
|
37
|
+
val publicEndpoint = Endpoint(Method.GET / "health")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### `AuthType.Basic`
|
|
41
|
+
|
|
42
|
+
HTTP Basic authentication. The `ClientRequirement` is `zio.http.Header.Authorization.Basic`:
|
|
43
|
+
|
|
44
|
+
```scala
|
|
45
|
+
import zio.blocks.endpoint._
|
|
46
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
47
|
+
import zio.http.Method
|
|
48
|
+
|
|
49
|
+
val basicEndpoint = Endpoint(Method.GET / "admin")
|
|
50
|
+
.auth(AuthType.Basic)
|
|
51
|
+
|
|
52
|
+
val codec = basicEndpoint.auth.codec
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### `AuthType.Bearer`
|
|
56
|
+
|
|
57
|
+
Bearer token authentication (OAuth 2.0 / JWT). The `ClientRequirement` is `zio.http.Header.Authorization.Bearer`:
|
|
58
|
+
|
|
59
|
+
```scala
|
|
60
|
+
import zio.blocks.endpoint._
|
|
61
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
62
|
+
import zio.http.Method
|
|
63
|
+
|
|
64
|
+
val bearerEndpoint = Endpoint(Method.GET / "me")
|
|
65
|
+
.auth(AuthType.Bearer)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### `AuthType.Digest`
|
|
69
|
+
|
|
70
|
+
HTTP Digest authentication. The `ClientRequirement` is `zio.http.Header.Authorization.Digest`:
|
|
71
|
+
|
|
72
|
+
```scala
|
|
73
|
+
import zio.blocks.endpoint._
|
|
74
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
75
|
+
import zio.http.Method
|
|
76
|
+
|
|
77
|
+
val digestEndpoint = Endpoint(Method.GET / "secure")
|
|
78
|
+
.auth(AuthType.Digest)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `AuthType.Custom`
|
|
82
|
+
|
|
83
|
+
For authentication schemes not covered by the built-in variants, `Custom` wraps any `HttpCodec[CodecKind.Request, ClientReq]`:
|
|
84
|
+
|
|
85
|
+
```scala
|
|
86
|
+
import zio.blocks.endpoint._
|
|
87
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
88
|
+
import zio.blocks.schema.Schema
|
|
89
|
+
import zio.http.Method
|
|
90
|
+
|
|
91
|
+
final case class ApiKey(value: String)
|
|
92
|
+
|
|
93
|
+
val apiKeyCodec = HttpCodec.requestHeader("X-Api-Key", Schema.string.transform[ApiKey](ApiKey(_), _.value))
|
|
94
|
+
val apiKeyAuth = AuthType.Custom(apiKeyCodec)
|
|
95
|
+
val keyEndpoint = Endpoint(Method.GET / "data").auth(apiKeyAuth)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Composition
|
|
99
|
+
|
|
100
|
+
`AuthType` values compose in two ways: `|` builds an OR alternative that accepts either scheme, and `Scoped` attaches scope requirements to an existing auth type.
|
|
101
|
+
|
|
102
|
+
### `AuthType#|` — OR composition
|
|
103
|
+
|
|
104
|
+
Two `AuthType` values can be combined with `|` to accept either scheme. The result is an `AuthType.Or` whose `ClientRequirement` is the union of both:
|
|
105
|
+
|
|
106
|
+
```scala
|
|
107
|
+
import zio.blocks.endpoint._
|
|
108
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
109
|
+
import zio.http.Method
|
|
110
|
+
|
|
111
|
+
val flexEndpoint = Endpoint(Method.GET / "resource")
|
|
112
|
+
.auth(AuthType.Basic | AuthType.Bearer)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The `|` operator automatically computes the combined `ClientRequirement` type as a union of both requirements. The codec of the resulting `Or` is a `HttpCodec.Fallback` — it tries the left scheme first and falls back to the right.
|
|
116
|
+
|
|
117
|
+
### `AuthType.Scoped`
|
|
118
|
+
|
|
119
|
+
To attach OAuth scope requirements to a bearer auth, wrap it in `AuthType.Scoped`:
|
|
120
|
+
|
|
121
|
+
```scala
|
|
122
|
+
import zio.blocks.endpoint._
|
|
123
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
124
|
+
import zio.http.Method
|
|
125
|
+
|
|
126
|
+
val scopedEndpoint = Endpoint(Method.GET / "admin")
|
|
127
|
+
.auth(AuthType.Scoped(AuthType.Bearer, List("admin:read", "admin:write")))
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`Scoped` does not change the codec — it carries the scopes as metadata for interpreters that perform scope-level authorization checks.
|
|
131
|
+
|
|
132
|
+
## Unauthorized Status
|
|
133
|
+
|
|
134
|
+
By default, `AuthType` returns `Status.NotFound` when the client does not meet the auth requirement (to avoid leaking endpoint existence). To change this, use `Endpoint#unauthorizedStatus` or call `AuthType#withUnauthorizedStatus` directly:
|
|
135
|
+
|
|
136
|
+
```scala
|
|
137
|
+
import zio.blocks.endpoint._
|
|
138
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
139
|
+
import zio.http.{Method, Status}
|
|
140
|
+
|
|
141
|
+
val endpoint = Endpoint(Method.GET / "me")
|
|
142
|
+
.auth(AuthType.Bearer)
|
|
143
|
+
.unauthorizedStatus(Status.Unauthorized)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`Or` composition preserves `AuthType#unauthorizedStatus` — it uses the left auth type's status.
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: endpoint
|
|
3
|
+
title: "Endpoint"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`Endpoint[PathInput, Input, Err, Output, Auth]` is the top-level descriptor for an HTTP endpoint. It holds a typed route, three independent codec channels (request input, error output, success output), an authentication type, and documentation metadata. `Endpoint` is pure data — it carries no server or client logic and imposes no effect type. Its full shape is:
|
|
7
|
+
|
|
8
|
+
```scala
|
|
9
|
+
final case class Endpoint[PathInput, Input, Err, Output, Auth <: AuthType](
|
|
10
|
+
route: RoutePattern[PathInput],
|
|
11
|
+
input: HttpCodec[CodecKind.Request, Input],
|
|
12
|
+
error: HttpCodec[CodecKind.Response, Err],
|
|
13
|
+
output: HttpCodec[CodecKind.Response, Output],
|
|
14
|
+
auth: Auth,
|
|
15
|
+
doc: Doc
|
|
16
|
+
)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Motivation
|
|
20
|
+
|
|
21
|
+
An endpoint descriptor separates the **shape** of an HTTP surface from its **execution**. A single `Endpoint` value can be interpreted by a server to generate routes, by a client generator to produce typed API calls, or by an OpenAPI renderer to produce specification documents. This means the endpoint definition is the single source of truth — change it once and every interpreter updates.
|
|
22
|
+
|
|
23
|
+
The five type parameters track everything the compiler needs to enforce consistency across the whole stack:
|
|
24
|
+
|
|
25
|
+
| Parameter | Meaning |
|
|
26
|
+
|-------------|------------------------------------------------------------|
|
|
27
|
+
| `PathInput` | Type of values extracted from path segments |
|
|
28
|
+
| `Input` | Aggregate type of all request inputs (query, header, body) |
|
|
29
|
+
| `Err` | Aggregate type of all error response shapes |
|
|
30
|
+
| `Output` | Aggregate type of all success response shapes |
|
|
31
|
+
| `Auth` | Authentication scheme, carries the client requirement |
|
|
32
|
+
|
|
33
|
+
## Construction
|
|
34
|
+
|
|
35
|
+
We create an `Endpoint` from a `RoutePattern` using `Endpoint.apply`:
|
|
36
|
+
|
|
37
|
+
```scala
|
|
38
|
+
import zio.blocks.endpoint._
|
|
39
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
40
|
+
import zio.http.Method
|
|
41
|
+
|
|
42
|
+
val ep = Endpoint(Method.GET / "users" / PathCodec.int("id"))
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The route can also be built from a separate `RoutePattern` value:
|
|
46
|
+
|
|
47
|
+
```scala
|
|
48
|
+
import zio.blocks.endpoint._
|
|
49
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
50
|
+
import zio.http.Method
|
|
51
|
+
|
|
52
|
+
val route = Method.POST / "orders" / PathCodec.uuid("orderId")
|
|
53
|
+
val ep = Endpoint(route)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The initial `Endpoint` starts with `Unit` for all three codec channels and `AuthType.None` for auth, so further builder calls always widen the types additively.
|
|
57
|
+
|
|
58
|
+
## Request Input Builders
|
|
59
|
+
|
|
60
|
+
Every `Endpoint#in`, `Endpoint#query`, and `Endpoint#header` call adds another input component to the endpoint, widening the `Input` type parameter.
|
|
61
|
+
|
|
62
|
+
### Body input
|
|
63
|
+
|
|
64
|
+
To add a request body typed by a `Schema`, use `Endpoint#in`:
|
|
65
|
+
|
|
66
|
+
```scala
|
|
67
|
+
import zio.blocks.endpoint._
|
|
68
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
69
|
+
import zio.blocks.schema.Schema
|
|
70
|
+
import zio.http.Method
|
|
71
|
+
|
|
72
|
+
val ep = Endpoint(Method.POST / "users")
|
|
73
|
+
.in(Schema.string)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
To specify the content type explicitly, pass a `MediaType` as well:
|
|
77
|
+
|
|
78
|
+
```scala
|
|
79
|
+
import zio.blocks.endpoint._
|
|
80
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
81
|
+
import zio.blocks.mediatype.MediaTypes
|
|
82
|
+
import zio.blocks.schema.Schema
|
|
83
|
+
import zio.http.Method
|
|
84
|
+
|
|
85
|
+
val ep = Endpoint(Method.POST / "users")
|
|
86
|
+
.in(MediaTypes.application.`json`, Schema.string)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
To add a raw `HttpCodec.Body` node directly, use `Endpoint#in`:
|
|
90
|
+
|
|
91
|
+
```scala
|
|
92
|
+
import zio.blocks.endpoint._
|
|
93
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
94
|
+
import zio.blocks.schema.Schema
|
|
95
|
+
import zio.http.Method
|
|
96
|
+
|
|
97
|
+
val ep = Endpoint(Method.POST / "users")
|
|
98
|
+
.in(HttpCodec.requestBody(Schema.string))
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Query parameters
|
|
102
|
+
|
|
103
|
+
To add a query parameter by name and schema, use `Endpoint#query`:
|
|
104
|
+
|
|
105
|
+
```scala
|
|
106
|
+
import zio.blocks.endpoint._
|
|
107
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
108
|
+
import zio.blocks.schema.Schema
|
|
109
|
+
import zio.http.Method
|
|
110
|
+
|
|
111
|
+
val ep = Endpoint(Method.GET / "users")
|
|
112
|
+
.query("page", Schema.int)
|
|
113
|
+
.query("limit", Schema.int)
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
To add a pre-built `HttpCodec.Query` node, use `Endpoint#query`:
|
|
117
|
+
|
|
118
|
+
```scala
|
|
119
|
+
import zio.blocks.endpoint._
|
|
120
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
121
|
+
import zio.blocks.schema.Schema
|
|
122
|
+
import zio.http.Method
|
|
123
|
+
|
|
124
|
+
val pageCodec = HttpCodec.query("page", Schema.int)
|
|
125
|
+
val ep = Endpoint(Method.GET / "users").query(pageCodec)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Request headers
|
|
129
|
+
|
|
130
|
+
To add a request header by name and schema, use `Endpoint#header`:
|
|
131
|
+
|
|
132
|
+
```scala
|
|
133
|
+
import zio.blocks.endpoint._
|
|
134
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
135
|
+
import zio.blocks.schema.Schema
|
|
136
|
+
import zio.http.Method
|
|
137
|
+
|
|
138
|
+
val ep = Endpoint(Method.GET / "users")
|
|
139
|
+
.header("X-Trace-Id", Schema.string)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
To add a pre-built `HttpCodec.Header` node, use `Endpoint#header`:
|
|
143
|
+
|
|
144
|
+
```scala
|
|
145
|
+
import zio.blocks.endpoint._
|
|
146
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
147
|
+
import zio.blocks.schema.Schema
|
|
148
|
+
import zio.http.Method
|
|
149
|
+
|
|
150
|
+
val traceCodec = HttpCodec.requestHeader("X-Trace-Id", Schema.string)
|
|
151
|
+
val ep = Endpoint(Method.GET / "users").header(traceCodec)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Success Output Builders
|
|
155
|
+
|
|
156
|
+
Every `Endpoint#out` and `Endpoint#outHeader` call adds a success response alternative or header component, widening `Output`.
|
|
157
|
+
|
|
158
|
+
### Response body
|
|
159
|
+
|
|
160
|
+
To add a 200 OK response body, use `Endpoint#out`:
|
|
161
|
+
|
|
162
|
+
```scala
|
|
163
|
+
import zio.blocks.endpoint._
|
|
164
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
165
|
+
import zio.blocks.schema.Schema
|
|
166
|
+
import zio.http.Method
|
|
167
|
+
|
|
168
|
+
val ep = Endpoint(Method.GET / "users")
|
|
169
|
+
.out(Schema.string)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
To specify a non-200 status code, use `Endpoint#out` with a `Status`:
|
|
173
|
+
|
|
174
|
+
```scala
|
|
175
|
+
import zio.blocks.endpoint._
|
|
176
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
177
|
+
import zio.blocks.schema.Schema
|
|
178
|
+
import zio.http.{Method, Status}
|
|
179
|
+
|
|
180
|
+
val ep = Endpoint(Method.POST / "users")
|
|
181
|
+
.out(Status.Created, Schema.int)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
To add content-type negotiation, pass a `MediaType`:
|
|
185
|
+
|
|
186
|
+
```scala
|
|
187
|
+
import zio.blocks.endpoint._
|
|
188
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
189
|
+
import zio.blocks.mediatype.MediaTypes
|
|
190
|
+
import zio.blocks.schema.Schema
|
|
191
|
+
import zio.http.{Method, Status}
|
|
192
|
+
|
|
193
|
+
val ep = Endpoint(Method.GET / "users")
|
|
194
|
+
.out(MediaTypes.application.`json`, Schema.string)
|
|
195
|
+
.out(Status.Created, MediaTypes.text.`plain`, Schema.int)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Multiple `Endpoint#out` calls produce alternatives. The output type widens from `Unit` to the first schema type, then to a nested `Either` for each additional alternative.
|
|
199
|
+
|
|
200
|
+
### Response headers
|
|
201
|
+
|
|
202
|
+
To add a typed response header, use `Endpoint#outHeader`:
|
|
203
|
+
|
|
204
|
+
```scala
|
|
205
|
+
import zio.blocks.endpoint._
|
|
206
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
207
|
+
import zio.blocks.schema.Schema
|
|
208
|
+
import zio.http.Method
|
|
209
|
+
|
|
210
|
+
val ep = Endpoint(Method.GET / "users")
|
|
211
|
+
.out(Schema.string)
|
|
212
|
+
.outHeader("X-Total-Count", Schema.int)
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Error Output Builders
|
|
216
|
+
|
|
217
|
+
Error channels work like success channels but populate the `Err` type parameter. Two variants exist: `Endpoint#outError` (cross-version) and `Endpoint#orOutError` (Scala 3 unions).
|
|
218
|
+
|
|
219
|
+
### `Endpoint#outError` — cross-version additive errors
|
|
220
|
+
|
|
221
|
+
To add an error response with a status code and body schema, use `Endpoint#outError`:
|
|
222
|
+
|
|
223
|
+
```scala
|
|
224
|
+
import zio.blocks.endpoint._
|
|
225
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
226
|
+
import zio.blocks.schema.Schema
|
|
227
|
+
import zio.http.{Method, Status}
|
|
228
|
+
|
|
229
|
+
val ep = Endpoint(Method.GET / "users")
|
|
230
|
+
.out(Schema.string)
|
|
231
|
+
.outError(Status.NotFound, Schema.string)
|
|
232
|
+
.outError(Status.BadRequest, Schema.string)
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Each `Endpoint#outError` call widens `Err` by one nested `Either` layer.
|
|
236
|
+
|
|
237
|
+
### `Endpoint#orOutError` — Scala 3 union errors
|
|
238
|
+
|
|
239
|
+
On Scala 3, `Endpoint#orOutError` accumulates error types as a native union type instead of nested `Either`s. The first call replaces the initial `Unit` error outright; subsequent calls build a `Fallback` codec backed by `Unions` derivation:
|
|
240
|
+
|
|
241
|
+
```scala
|
|
242
|
+
import zio.blocks.endpoint._
|
|
243
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
244
|
+
import zio.blocks.schema.Schema
|
|
245
|
+
import zio.http.{Method, Status}
|
|
246
|
+
|
|
247
|
+
val ep = Endpoint(Method.GET / "users")
|
|
248
|
+
.orOutError(Status.NotFound, Schema.string)
|
|
249
|
+
.orOutError(Status.Conflict, Schema.int)
|
|
250
|
+
|
|
251
|
+
val typed: Endpoint[Unit, Unit, String | Int, Unit, AuthType.None.type] = ep
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
The compiler rejects overlapping union members — two `Endpoint#orOutError` calls both using `Schema.string` produce a compile error because `String | String` is not a valid discriminated union.
|
|
255
|
+
|
|
256
|
+
## Authentication
|
|
257
|
+
|
|
258
|
+
To attach an authentication scheme, use `Endpoint#auth`:
|
|
259
|
+
|
|
260
|
+
```scala
|
|
261
|
+
import zio.blocks.endpoint._
|
|
262
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
263
|
+
import zio.http.Method
|
|
264
|
+
|
|
265
|
+
val secured = Endpoint(Method.GET / "me")
|
|
266
|
+
.auth(AuthType.Bearer)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
The `Auth` type parameter carries the `ClientRequirement` associated type, so a bearer-secured endpoint exposes `auth.codec` typed as `HttpCodec[CodecKind.Request, zio.http.Header.Authorization.Bearer]`. See [AuthType](./auth-type.md) for all variants.
|
|
270
|
+
|
|
271
|
+
To override the HTTP status the server sends when the client does not meet the auth requirement, use `Endpoint#unauthorizedStatus`:
|
|
272
|
+
|
|
273
|
+
```scala
|
|
274
|
+
import zio.blocks.endpoint._
|
|
275
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
276
|
+
import zio.http.{Method, Status}
|
|
277
|
+
|
|
278
|
+
val secured = Endpoint(Method.GET / "me")
|
|
279
|
+
.auth(AuthType.Bearer)
|
|
280
|
+
.unauthorizedStatus(Status.Unauthorized)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
## Documentation
|
|
284
|
+
|
|
285
|
+
To attach a `Doc` value to the endpoint as a whole, use `Endpoint#doc`:
|
|
286
|
+
|
|
287
|
+
```scala
|
|
288
|
+
import zio.blocks.docs.Doc
|
|
289
|
+
import zio.blocks.endpoint._
|
|
290
|
+
import zio.blocks.endpoint.RoutePattern._
|
|
291
|
+
import zio.http.Method
|
|
292
|
+
|
|
293
|
+
val ep = Endpoint(Method.GET / "users")
|
|
294
|
+
.doc(Doc.empty)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Documentation attached here flows through to OpenAPI generation and any other documentation interpreters.
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: http-codec
|
|
3
|
+
title: "HttpCodec"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
`HttpCodec[K, A]` is a composable, typed descriptor for HTTP request and response parts. The phantom type parameter `K` (either `CodecKind.Request` or `CodecKind.Response`) tracks which direction the codec belongs to, so the compiler prevents mixing request-side codecs (query, request header, request body) with response-side codecs (status, response header, response body). The trait signature is:
|
|
7
|
+
|
|
8
|
+
```scala
|
|
9
|
+
sealed trait HttpCodec[+K <: CodecKind, A]
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Motivation
|
|
13
|
+
|
|
14
|
+
HTTP surfaces have two directions — request and response — and each direction has several distinct parts. Without static direction tracking, it is easy to accidentally pass a response status codec where a request header codec is expected, or combine a query parameter with a response body.
|
|
15
|
+
|
|
16
|
+
`HttpCodec` makes that class of mistake a compile error. The phantom type `K` carries direction at the type level, so `HttpCodec[CodecKind.Request, A]` and `HttpCodec[CodecKind.Response, A]` are incompatible types. All combinators (`++`, `|`) preserve this constraint: combining two request codecs yields a request codec, and combining request with response is a type error.
|
|
17
|
+
|
|
18
|
+
## `CodecKind`
|
|
19
|
+
|
|
20
|
+
`CodecKind` is a phantom type hierarchy with two sealed subtypes:
|
|
21
|
+
|
|
22
|
+
```scala
|
|
23
|
+
sealed trait CodecKind
|
|
24
|
+
|
|
25
|
+
object CodecKind {
|
|
26
|
+
sealed trait Request extends CodecKind // query, request header, request body
|
|
27
|
+
sealed trait Response extends CodecKind // status, response header, response body
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
These are never instantiated — they exist only to parameterize `HttpCodec[K, A]` at the type level.
|
|
32
|
+
|
|
33
|
+
## Structure
|
|
34
|
+
|
|
35
|
+
`HttpCodec` is an ADT with seven node types:
|
|
36
|
+
|
|
37
|
+
| Node | Kind | Carries |
|
|
38
|
+
| ------------- | --------- | ---------------------------------------------------- |
|
|
39
|
+
| `Empty` | both | No data — neutral element for `++` |
|
|
40
|
+
| `Combine` | both | Two codecs composed sequentially with `++` |
|
|
41
|
+
| `Fallback` | both | Two codecs composed as alternatives with `|` |
|
|
42
|
+
| `Query` | `Request` | Named query parameter with `Schema[A]` |
|
|
43
|
+
| `Header` | both | Named HTTP header with `Schema[A]` (request or response) |
|
|
44
|
+
| `Body` | both | Request or response body with `Schema[A]` |
|
|
45
|
+
| `StatusCodec` | `Response`| HTTP status code |
|
|
46
|
+
|
|
47
|
+
## Construction
|
|
48
|
+
|
|
49
|
+
Smart constructors on the `HttpCodec` companion build each atom type. Choose the constructor that matches the HTTP part you are describing.
|
|
50
|
+
|
|
51
|
+
### Query parameters
|
|
52
|
+
|
|
53
|
+
To describe a named query parameter, use `HttpCodec.query`:
|
|
54
|
+
|
|
55
|
+
```scala
|
|
56
|
+
import zio.blocks.endpoint._
|
|
57
|
+
import zio.blocks.schema.Schema
|
|
58
|
+
|
|
59
|
+
val limitCodec: HttpCodec.Query[Int] = HttpCodec.query("limit", Schema.int)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Optional fields on `Query` include `default`, `doc`, `examples`, and `deprecated`. To create a query codec with a default value:
|
|
63
|
+
|
|
64
|
+
```scala
|
|
65
|
+
import zio.blocks.endpoint._
|
|
66
|
+
import zio.blocks.schema.Schema
|
|
67
|
+
|
|
68
|
+
val pageCodec = HttpCodec.query("page", Schema.int, default = Some(1))
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Request headers
|
|
72
|
+
|
|
73
|
+
To describe a request header by name and schema, use `HttpCodec.requestHeader`:
|
|
74
|
+
|
|
75
|
+
```scala
|
|
76
|
+
import zio.blocks.endpoint._
|
|
77
|
+
import zio.blocks.schema.Schema
|
|
78
|
+
|
|
79
|
+
val traceHeader: HttpCodec.Header[CodecKind.Request, String] =
|
|
80
|
+
HttpCodec.requestHeader("X-Trace-Id", Schema.string)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
To use a zio-http typed header instance (which provides its own name and parse/render logic), pass the typed header directly:
|
|
84
|
+
|
|
85
|
+
```scala
|
|
86
|
+
import zio.blocks.endpoint._
|
|
87
|
+
import zio.http.Header
|
|
88
|
+
|
|
89
|
+
val authHeader: HttpCodec.Header[CodecKind.Request, Header.Authorization] =
|
|
90
|
+
HttpCodec.requestHeader(Header.Authorization)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Response headers
|
|
94
|
+
|
|
95
|
+
To describe a response header, use `HttpCodec.responseHeader`:
|
|
96
|
+
|
|
97
|
+
```scala
|
|
98
|
+
import zio.blocks.endpoint._
|
|
99
|
+
import zio.blocks.schema.Schema
|
|
100
|
+
|
|
101
|
+
val totalCount: HttpCodec.Header[CodecKind.Response, Int] =
|
|
102
|
+
HttpCodec.responseHeader("X-Total-Count", Schema.int)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Request body
|
|
106
|
+
|
|
107
|
+
To describe a request body, use `HttpCodec.requestBody`:
|
|
108
|
+
|
|
109
|
+
```scala
|
|
110
|
+
import zio.blocks.endpoint._
|
|
111
|
+
import zio.blocks.schema.Schema
|
|
112
|
+
|
|
113
|
+
val body: HttpCodec[CodecKind.Request, String] =
|
|
114
|
+
HttpCodec.requestBody(Schema.string)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
To restrict the accepted content types, pass a `Chunk[MediaType]`:
|
|
118
|
+
|
|
119
|
+
```scala
|
|
120
|
+
import zio.blocks.chunk.Chunk
|
|
121
|
+
import zio.blocks.endpoint._
|
|
122
|
+
import zio.blocks.mediatype.MediaTypes
|
|
123
|
+
import zio.blocks.schema.Schema
|
|
124
|
+
|
|
125
|
+
val jsonBody: HttpCodec[CodecKind.Request, String] =
|
|
126
|
+
HttpCodec.requestBody(Schema.string, mediaTypes = Chunk.single(MediaTypes.application.`json`))
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Response body
|
|
130
|
+
|
|
131
|
+
To describe a response body, use `HttpCodec.responseBody`:
|
|
132
|
+
|
|
133
|
+
```scala
|
|
134
|
+
import zio.blocks.endpoint._
|
|
135
|
+
import zio.blocks.schema.Schema
|
|
136
|
+
|
|
137
|
+
val body: HttpCodec[CodecKind.Response, String] =
|
|
138
|
+
HttpCodec.responseBody(Schema.string)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Status codes
|
|
142
|
+
|
|
143
|
+
To describe a required HTTP status code, use `HttpCodec.status`:
|
|
144
|
+
|
|
145
|
+
```scala
|
|
146
|
+
import zio.blocks.endpoint._
|
|
147
|
+
import zio.http.Status
|
|
148
|
+
|
|
149
|
+
val created: HttpCodec[CodecKind.Response, Unit] = HttpCodec.status(Status.Created)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Predefined status constants are available directly on `HttpCodec`:
|
|
153
|
+
|
|
154
|
+
```scala
|
|
155
|
+
import zio.blocks.endpoint._
|
|
156
|
+
|
|
157
|
+
val ok = HttpCodec.Ok
|
|
158
|
+
val created = HttpCodec.Created
|
|
159
|
+
val notFound = HttpCodec.NotFound
|
|
160
|
+
val badRequest = HttpCodec.BadRequest
|
|
161
|
+
val unauthorized = HttpCodec.Unauthorized
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
For any other status code, use `HttpCodec.CustomStatus(code)`.
|
|
165
|
+
|
|
166
|
+
## Composition
|
|
167
|
+
|
|
168
|
+
Two operators combine `HttpCodec` values: `++` sequences parts within the same direction, while `|` creates alternatives for content negotiation or multi-status responses.
|
|
169
|
+
|
|
170
|
+
### Sequential composition with `++`
|
|
171
|
+
|
|
172
|
+
`++` combines two codecs of the same direction into a single codec whose type is the product of both. The result type is automatically flattened, eliminating `Unit` components and nested tuples:
|
|
173
|
+
|
|
174
|
+
```scala
|
|
175
|
+
import zio.blocks.endpoint._
|
|
176
|
+
import zio.blocks.schema.Schema
|
|
177
|
+
|
|
178
|
+
val queryAndHeader: HttpCodec[CodecKind.Request, (String, Int)] =
|
|
179
|
+
HttpCodec.query("name", Schema.string) ++ HttpCodec.query("age", Schema.int)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The compiler rejects mixing directions — combining a request codec with a response codec is a type error:
|
|
183
|
+
|
|
184
|
+
```scala
|
|
185
|
+
import zio.blocks.endpoint._
|
|
186
|
+
import zio.blocks.schema.Schema
|
|
187
|
+
import zio.http.Status
|
|
188
|
+
|
|
189
|
+
// This would be a compile error:
|
|
190
|
+
// HttpCodec.query("name", Schema.string) ++ HttpCodec.status(Status.Ok)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Alternative composition with `|`
|
|
194
|
+
|
|
195
|
+
`|` combines two codecs as alternatives. The result type is automatically computed as a nested `Either`:
|
|
196
|
+
|
|
197
|
+
```scala
|
|
198
|
+
import zio.blocks.endpoint._
|
|
199
|
+
import zio.blocks.schema.Schema
|
|
200
|
+
import zio.http.Status
|
|
201
|
+
|
|
202
|
+
val okOrCreated =
|
|
203
|
+
(HttpCodec.responseBody(Schema.string) ++ HttpCodec.Ok) |
|
|
204
|
+
(HttpCodec.responseBody(Schema.int) ++ HttpCodec.Created)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Authentication Codecs
|
|
208
|
+
|
|
209
|
+
Pre-built request codecs for common authorization header schemes are available on `HttpCodec`:
|
|
210
|
+
|
|
211
|
+
```scala
|
|
212
|
+
import zio.blocks.endpoint._
|
|
213
|
+
import zio.http.Header
|
|
214
|
+
|
|
215
|
+
val basic: HttpCodec[CodecKind.Request, Header.Authorization.Basic] = HttpCodec.basicAuth
|
|
216
|
+
val bearer: HttpCodec[CodecKind.Request, Header.Authorization.Bearer] = HttpCodec.bearerAuth
|
|
217
|
+
val digest: HttpCodec[CodecKind.Request, Header.Authorization.Digest] = HttpCodec.digestAuth
|
|
218
|
+
val proxy: HttpCodec[CodecKind.Request, Header.ProxyAuthorization] = HttpCodec.proxyAuthorization
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
These codecs use `Schema.transform` internally to parse the raw `Authorization` header string into the typed zio-http auth model, surfacing a `SchemaError` if the scheme does not match.
|
|
222
|
+
|
|
223
|
+
## Metadata Fields
|
|
224
|
+
|
|
225
|
+
Every atom node (`Query`, `Header`, `Body`, `StatusCodec`) carries optional metadata that documentation renderers and OpenAPI generators consume:
|
|
226
|
+
|
|
227
|
+
| Field | Type | Purpose |
|
|
228
|
+
| ------------ | ----------------- | ---------------------------------------------- |
|
|
229
|
+
| `doc` | `Doc` | Free-text description for OpenAPI output |
|
|
230
|
+
| `examples` | `Chunk[(String, A)]` | Example values for the OpenAPI spec |
|
|
231
|
+
| `deprecated` | `Option[Doc]` | Marks the field as deprecated with a message |
|
|
232
|
+
| `default` | `Option[A]` | Default value (query and header only) |
|
|
233
|
+
|
|
234
|
+
To create a query codec with documentation and an example:
|
|
235
|
+
|
|
236
|
+
```scala
|
|
237
|
+
import zio.blocks.chunk.Chunk
|
|
238
|
+
import zio.blocks.docs.Doc
|
|
239
|
+
import zio.blocks.endpoint._
|
|
240
|
+
import zio.blocks.schema.Schema
|
|
241
|
+
|
|
242
|
+
val limitCodec = HttpCodec.query(
|
|
243
|
+
name = "limit",
|
|
244
|
+
schema = Schema.int,
|
|
245
|
+
default = Some(20),
|
|
246
|
+
doc = Doc.empty,
|
|
247
|
+
examples = Chunk("default" -> 20, "max" -> 100)
|
|
248
|
+
)
|
|
249
|
+
```
|