@zio.dev/zio-blocks 0.0.33 → 0.0.55
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/adr/2026-07-18-data-migration.md +123 -0
- package/guides/async-getting-started.md +687 -0
- package/guides/compile-time-resource-safety-with-scope.md +21 -16
- package/guides/getting-started-with-mux.md +1395 -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 +640 -165
- package/guides/sql-checked-interpolation.md +173 -0
- package/guides/sql-transactions.md +286 -0
- package/guides/telemetry-guide.md +1130 -0
- package/guides/zio-schema-migration.md +29 -22
- package/index.md +248 -389
- 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 +1499 -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/config-decoder.md +460 -0
- package/reference/config/config-source.md +489 -0
- package/reference/config/errors.md +278 -0
- package/reference/config/flags.md +369 -0
- package/reference/config/formats.md +314 -0
- package/reference/config/index.md +304 -0
- package/reference/config/rollout.md +336 -0
- package/reference/context.md +9 -52
- package/reference/data-migration.md +269 -0
- package/reference/datastar/attributes.md +302 -0
- package/reference/datastar/events.md +234 -0
- package/reference/datastar/index.md +256 -0
- package/reference/datastar/signals.md +230 -0
- package/reference/datastar/sse.md +295 -0
- package/reference/datastar.md +346 -0
- package/reference/docs.md +1461 -345
- package/reference/endpoint/auth-type.md +146 -0
- package/reference/endpoint/bulk-creation.md +96 -0
- package/reference/endpoint/endpoint.md +297 -0
- package/reference/endpoint/http-codec.md +249 -0
- package/reference/endpoint/index.md +745 -0
- package/reference/endpoint/path-codec.md +225 -0
- package/reference/endpoint/route-pattern.md +194 -0
- package/reference/endpoint/route-tree.md +111 -0
- package/reference/endpoint/segment-codec.md +199 -0
- package/reference/html.md +1424 -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 +807 -0
- package/reference/htmx/response-headers.md +240 -0
- package/reference/http-model/headers.md +735 -0
- package/reference/http-model/index.md +49 -0
- package/reference/http-model/model.md +1517 -0
- package/reference/http-model/schema-codecs.md +522 -0
- package/reference/http-model/schema.md +750 -0
- package/reference/http-model/server-sent-event.md +341 -0
- package/reference/jwt.md +195 -0
- package/reference/maybe.md +943 -0
- package/reference/media-type.md +2 -2
- package/reference/mux.md +254 -0
- package/reference/mux.mdx +828 -0
- package/reference/openapi.md +1351 -0
- package/reference/projection.md +654 -0
- package/reference/resource-management/defer-handle.md +1 -1
- package/reference/resource-management/resource.md +31 -98
- package/reference/resource-management/scope.md +28 -220
- package/reference/resource-management/wire.md +5 -55
- 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 +185 -0
- package/reference/ringbuffer/mpsc.mdx +164 -0
- package/reference/ringbuffer/spmc.mdx +108 -0
- package/reference/ringbuffer/spsc.mdx +416 -0
- package/reference/{allows.md → schema/allows.md} +4 -100
- package/reference/{binding-resolver.md → schema/binding-resolver.md} +1 -1
- package/reference/{binding.md → schema/binding.md} +3 -4
- package/reference/schema/built-in-codecs/avro.md +451 -0
- package/reference/schema/built-in-codecs/bson.md +510 -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} +11 -11
- package/reference/{dynamic-optic.md → schema/dynamic-optic.md} +196 -5
- package/reference/{dynamic-schema.md → schema/dynamic-schema.md} +8 -8
- package/reference/schema/format.md +92 -0
- package/reference/schema/index.md +52 -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} +167 -72
- package/reference/schema/reflect-transformer.md +140 -0
- 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/schema-search.md +263 -0
- package/reference/{schema.md → schema/schema.md} +22 -2
- 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 +1032 -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 +148 -0
- package/reference/sql/db-tx.md +114 -0
- package/reference/sql/db-value.md +41 -0
- package/reference/sql/ddl.md +85 -0
- package/reference/sql/frag.md +288 -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 +363 -0
- package/reference/sql-zio.md +112 -0
- package/reference/streams/core/index.md +32 -0
- package/reference/streams/core/pipeline.md +854 -0
- package/reference/streams/core/sink.md +1404 -0
- package/reference/streams/core/stream.md +3236 -0
- package/reference/streams/execution-and-compatibility/async-execution.md +822 -0
- package/reference/streams/execution-and-compatibility/index.md +35 -0
- package/reference/streams/execution-and-compatibility/platform-differences.md +297 -0
- package/reference/streams/execution-and-compatibility/scala-2-compatibility.md +88 -0
- package/reference/streams/execution-and-compatibility/zero-boxing.md +393 -0
- package/reference/streams/index.md +726 -0
- package/reference/streams/primitives/index.md +30 -0
- package/reference/streams/primitives/reader.md +1992 -0
- package/reference/streams/primitives/writer.md +1201 -0
- package/reference/telemetry/common/any-value.md +90 -0
- package/reference/telemetry/common/attribute-key.md +87 -0
- package/reference/telemetry/common/attributes.md +118 -0
- package/reference/telemetry/common/index.md +39 -0
- package/reference/telemetry/common/instrumentation-scope.md +24 -0
- package/reference/telemetry/common/resource.md +34 -0
- package/reference/telemetry/index.md +311 -0
- package/reference/telemetry/logging/index.md +197 -0
- package/reference/telemetry/logging/log-enrichment.md +72 -0
- package/reference/telemetry/logging/log-formatter.md +100 -0
- package/reference/telemetry/logging/log-record-processor.md +56 -0
- package/reference/telemetry/logging/log-record.md +44 -0
- package/reference/telemetry/logging/log-writer.md +64 -0
- package/reference/telemetry/logging/logger-provider.md +142 -0
- package/reference/telemetry/logging/logger.md +83 -0
- package/reference/telemetry/logging/severity.md +62 -0
- package/reference/telemetry/metrics/index.md +150 -0
- package/reference/telemetry/metrics/instruments.md +183 -0
- package/reference/telemetry/metrics/labeled-instruments.md +74 -0
- package/reference/telemetry/metrics/meter-provider.md +76 -0
- package/reference/telemetry/metrics/meter.md +98 -0
- package/reference/telemetry/metrics/metric-data.md +57 -0
- package/reference/telemetry/otel/custom-exporter.md +216 -0
- package/reference/telemetry/otel/index.md +212 -0
- package/reference/telemetry/tracing/index.md +155 -0
- package/reference/telemetry/tracing/sampler.md +89 -0
- package/reference/telemetry/tracing/span-builder.md +57 -0
- package/reference/telemetry/tracing/span-context.md +39 -0
- package/reference/telemetry/tracing/span-data.md +32 -0
- package/reference/telemetry/tracing/span-kind.md +55 -0
- package/reference/telemetry/tracing/span-processor.md +53 -0
- package/reference/telemetry/tracing/span-status.md +47 -0
- package/reference/telemetry/tracing/span.md +117 -0
- package/reference/telemetry/tracing/tracer-provider.md +91 -0
- package/reference/telemetry/tracing/tracer.md +52 -0
- package/reference/typeid.md +5 -83
- package/sidebars.js +376 -43
- package/undocumented-report.md +528 -270
- 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,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: bulk-creation
|
|
3
|
+
title: "Bulk Endpoint Creation"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
## Bulk endpoint creation with `endpoints { ... }`
|
|
7
|
+
|
|
8
|
+
The `endpoints` macro (Scala 3.8+ with `-experimental` for `NamedTuple`, also works on Scala 3.7 with `-experimental`) lets you define multiple `Endpoint` values in a block and access them by name on the returned `NamedTuple`. Member names are either explicit `val` names or auto-generated from the `RoutePattern.render` string (method prefix + path template). Prefix grouping via `/` (`"api" / endpoints { ... }`, `PathCodec.int("id") / endpoints { ... }`) is available via the default `import zio.blocks.endpoint.*` — no extra import is needed. All examples below assume `import zio.blocks.endpoint.*` and `scalacOptions += "-experimental"`.
|
|
9
|
+
|
|
10
|
+
String prefixes such as `"api"` are auto-converted to a literal `PathCodec[Unit]` via a `Conversion[String, PathCodec[Unit]]` provided by `zio.blocks.endpoint.*` — there is no String-specific `/` operator; constant prefixes auto-convert and use the same grouping `/` as capturing prefixes.
|
|
11
|
+
|
|
12
|
+
> **Inline-only:** prefix grouping (`prefix / endpoints { ... }`) requires an inline `endpoints { ... }` block. Binding the group to a value first (`val g = endpoints { ... }; "api" / g`) is not supported — the macro must see the block literal to compose prefixes.
|
|
13
|
+
|
|
14
|
+
```scala
|
|
15
|
+
import zio.blocks.endpoint.*
|
|
16
|
+
import zio.blocks.endpoint.RoutePattern.*
|
|
17
|
+
import zio.http.Method
|
|
18
|
+
|
|
19
|
+
val api = "api" / endpoints {
|
|
20
|
+
val customer = Endpoint(Method.GET / "customers")
|
|
21
|
+
Endpoint(Method.GET / "health")
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Member access is static (zero runtime cost):
|
|
26
|
+
|
|
27
|
+
```scala
|
|
28
|
+
import zio.blocks.endpoint.*
|
|
29
|
+
import zio.blocks.endpoint.RoutePattern.*
|
|
30
|
+
import zio.http.Method
|
|
31
|
+
|
|
32
|
+
val api = "api" / endpoints {
|
|
33
|
+
val customer = Endpoint(Method.GET / "customers")
|
|
34
|
+
Endpoint(Method.GET / "health")
|
|
35
|
+
}
|
|
36
|
+
val c: Endpoint[Unit, Unit, Unit, Unit, AuthType.None.type] = api.customer
|
|
37
|
+
val h: Endpoint[Unit, Unit, Unit, Unit, AuthType.None.type] = api.`GET /health`
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Auto-naming follows `RoutePattern.render` exactly:
|
|
41
|
+
|
|
42
|
+
- `GET /user/{userId}` for path variables (RFC 6570 `{var}`)
|
|
43
|
+
- `GET#|POST /orders` for multi-method
|
|
44
|
+
- `v{major}` for `~` concat segments
|
|
45
|
+
- `...` for trailing segments
|
|
46
|
+
- `.unused` renders as `{name}`
|
|
47
|
+
|
|
48
|
+
Constant-prefix nesting bakes the prefix into each child's `RoutePattern` at the description level; grouping nodes have no path themselves:
|
|
49
|
+
|
|
50
|
+
```scala
|
|
51
|
+
import zio.blocks.endpoint.*
|
|
52
|
+
import zio.blocks.endpoint.RoutePattern.*
|
|
53
|
+
import zio.http.Method
|
|
54
|
+
|
|
55
|
+
val nested = "api" / endpoints {
|
|
56
|
+
"v1" / endpoints {
|
|
57
|
+
val users = Endpoint(Method.GET / "users")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
val u = nested.v1.users // route.render == "GET /api/v1/users"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Path-variable prefixes are implemented the same way. A capturing prefix contributes its segment to every child's path, while children keep their relative auto-names:
|
|
64
|
+
|
|
65
|
+
```scala
|
|
66
|
+
import zio.blocks.endpoint.*
|
|
67
|
+
import zio.blocks.endpoint.RoutePattern.*
|
|
68
|
+
import zio.http.Method
|
|
69
|
+
|
|
70
|
+
val byId = PathCodec.int("id") / endpoints {
|
|
71
|
+
val get = Endpoint(Method.GET / "orders")
|
|
72
|
+
Endpoint(Method.DELETE / "orders") // auto-named `DELETE /orders`
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
val getOrder: Endpoint[Int, Unit, Unit, Unit, AuthType.None.type] = byId.get
|
|
76
|
+
val delOrder = byId.`DELETE /orders`
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Both children carry the captured segment: `byId.get.route.render == "GET /{id}/orders"` and the auto-named member renders `"DELETE /{id}/orders"`. Static types are preserved — `byId.get` is an `Endpoint[Int, Unit, Unit, Unit, AuthType.None.type]`, so the captured `id` will be delivered to handlers when routes are created on the zio-http side. A child with its own path variables composes positionally: under `int("id")`, `Endpoint(Method.GET / "orders" / PathCodec.int("orderId"))` renders as `GET /{id}/orders/{orderId}` and carries `(Int, Int)`:
|
|
80
|
+
|
|
81
|
+
```scala
|
|
82
|
+
import zio.blocks.endpoint.*
|
|
83
|
+
import zio.blocks.endpoint.RoutePattern.*
|
|
84
|
+
import zio.http.Method
|
|
85
|
+
|
|
86
|
+
val ordersById = PathCodec.int("id") / endpoints {
|
|
87
|
+
val o = Endpoint(Method.GET / "orders" / PathCodec.int("orderId"))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
val lookup: Endpoint[(Int, Int), Unit, Unit, Unit, AuthType.None.type] = ordersById.o
|
|
91
|
+
// lookup.route.render == "GET /{id}/orders/{orderId}"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Variable prefixes should be bound to an explicit `val` — the val names the subgroup whose members you access through it (`byId.get`, `ordersById.o`). Constant-prefix and capturing-prefix subgroups can be freely nested inside each other: `"api" / endpoints { PathCodec.int("id") / endpoints { ... } }` composes both prefixes into every leaf route at compile time.
|
|
95
|
+
|
|
96
|
+
The returned type is a Scala 3 `NamedTuple` — static member access, erased at runtime. The DSL is Scala 3 only (3.7+ with `-experimental` for named tuples). All examples above compile against the `endpoint` module on Scala 3.8.3 with `scalacOptions += "-experimental"`.
|
|
@@ -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.
|