| id | openapi |
|---|---|
| title | OpenAPI |
zio-blocks-openapi is a complete, type-safe OpenAPI 3.1 data model for building API documentation programmatically. It provides immutable case classes and sealed traits representing every OpenAPI concept—operations, parameters, security schemes, and components—enabling you to construct OpenAPI documents in compile-time-safe Scala and export them as JSON for consumption by tools like Swagger UI, Redoc, and API validators.
Core types: OpenAPI, Info, Paths, PathItem, Operation, Parameter, RequestBody, Response, Components, SchemaObject, SecurityScheme, ReferenceOr.
final case class OpenAPI(
openapi: String,
info: Info,
servers: Option[Chunk[Server]] = None,
paths: Option[Paths] = None,
components: Option[Components] = None,
security: Option[Chunk[SecurityRequirement]] = None
)OpenAPI documents are the lingua franca for API specifications. They define request/response contracts, authentication methods, and data schemas in a standardized JSON or YAML format that external tools consume. Building these documents manually in JSON is error-prone; maintaining them as your API evolves is tedious.
The OpenAPI module bridges the gap by letting you author API specs as Scala code—leveraging the type system for compile-time correctness—then export to standard JSON that any OpenAPI tool understands. You get type safety during authoring plus interoperability with the entire OpenAPI ecosystem.
libraryDependencies += "dev.zio" %% "zio-blocks-openapi" % "@VERSION@"
// You'll also need the schema module for Schema[A] integration:
libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "@VERSION@"For Scala.js:
libraryDependencies += "dev.zio" %%% "zio-blocks-openapi" % "@VERSION@"Supported Scala versions: 2.13.x and 3.x.
The OpenAPI module follows a clear workflow:
1. Define your data types using ZIO Blocks Schema:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
case class User(id: Int, name: String, email: String)
object User {
implicit val schema: Schema[User] = Schema.derived
}
case class ErrorResponse(code: Int, message: String)
object ErrorResponse {
implicit val schema: Schema[ErrorResponse] = Schema.derived
}2. Create an OpenAPI document by composing types:
val api = OpenAPI(
openapi = "3.1.0",
info = Info(
title = "User API",
version = "1.0.0",
description = Some(md"API for managing users")
),
paths = Some(Paths(ChunkMap(
"/users" -> PathItem(
get = Some(Operation(
summary = Some(md"List all users"),
description = Some(md"Returns a paginated list of users"),
responses = Responses(ChunkMap(
"200" -> ReferenceOr.Value(Response(
description = md"Successful response",
content = ChunkMap(
"application/json" -> MediaType(
schema = Some(ReferenceOr.Value(
Schema[List[User]].toOpenAPISchema
))
)
)
))
))
))
),
"/users/{id}" -> PathItem(
get = Some(Operation(
summary = Some(md"Get a user by ID"),
parameters = Chunk(
ReferenceOr.Value(Parameter(
name = "id",
in = ParameterLocation.Path,
required = true,
schema = Some(ReferenceOr.Value(
Schema[Int].toOpenAPISchema
))
))
),
responses = Responses(ChunkMap(
"200" -> ReferenceOr.Value(Response(
description = md"User found",
content = ChunkMap(
"application/json" -> MediaType(
schema = Some(ReferenceOr.Value(
Schema[User].toOpenAPISchema
))
)
)
)),
"404" -> ReferenceOr.Value(Response(
description = md"User not found",
content = ChunkMap(
"application/json" -> MediaType(
schema = Some(ReferenceOr.Value(
Schema[ErrorResponse].toOpenAPISchema
))
)
)
))
))
))
)
))),
components = Some(Components(
schemas = ChunkMap(
Schema[User].toRefSchema._2._1 -> ReferenceOr.Value(Schema[User].toRefSchema._2._2),
Schema[ErrorResponse].toRefSchema._2._1 -> ReferenceOr.Value(Schema[ErrorResponse].toRefSchema._2._2)
)
))
)3. Serialize to JSON for tools to consume:
import zio.blocks.openapi.OpenAPICodec._
val json = openAPICodec.encodeValue(api)4. Render or serve the JSON (e.g., to Swagger UI):
import zio.blocks.schema.json._
val jsonString = Json.jsonCodec.encodeToString(json, WriterConfig.withIndentionStep2)OpenAPI (root document)
├─ info: Info (metadata)
├─ servers: Option[Chunk[Server]]
├─ paths: Option[Paths] (map of path strings to PathItem)
│ └─ PathItem
│ ├─ get: Operation
│ ├─ post: Operation
│ ├─ put: Operation
│ └─ ... (other HTTP methods)
│ ├─ parameters: Chunk[ReferenceOr[Parameter]]
│ ├─ requestBody: ReferenceOr[RequestBody]
│ │ └─ content: Map[String, MediaType]
│ │ └─ schema: ReferenceOr[SchemaObject]
│ └─ responses: Responses
│ └─ Map[statusCode, ReferenceOr[Response]]
│ └─ content: Map[String, MediaType]
│ └─ schema: ReferenceOr[SchemaObject]
├─ components: Option[Components]
│ ├─ schemas: ChunkMap[String, ReferenceOr[SchemaObject]]
│ ├─ responses: ChunkMap[String, ReferenceOr[Response]]
│ ├─ parameters: ChunkMap[String, ReferenceOr[Parameter]]
│ └─ securitySchemes: ChunkMap[String, ReferenceOr[SecurityScheme]]
└─ security: Option[Chunk[SecurityRequirement]]
Avoid duplicating schema definitions by moving them to components.schemas:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
case class User(id: Int, name: String, email: String)
object User {
implicit val schema: Schema[User] = Schema.derived
}
val userSchemaComponent = Schema[User].toRefSchema
// Returns: (ReferenceOr.Ref(...), ("User", SchemaObject(...)))
// Use the ref in operations, store the component in components.schemasReferenceOr[A] is a sealed trait with two cases:
ReferenceOr.Ref: Points to a schema in#/components/schemas/<name>ReferenceOr.Value: Inline schema definition
Prefer Ref for reusable schemas; use Value for simple, one-off schemas:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
// Reusable: use Ref
val userRef = ReferenceOr.Ref(Reference(`$ref` = "#/components/schemas/User"))
// One-off: use Value
val simpleString = ReferenceOr.Value(Schema[String].toOpenAPISchema)Define authentication methods in components.securitySchemes:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val apiKeyScheme = SecurityScheme.APIKey(
name = "X-API-Key",
in = APIKeyLocation.Header,
description = Some(md"API key for authentication")
)
val oauthScheme = SecurityScheme.OAuth2(
flows = OAuthFlows(
authorizationCode = Some(OAuthFlow(
authorizationUrl = Some("https://example.com/oauth/authorize"),
tokenUrl = Some("https://example.com/oauth/token"),
scopes = ChunkMap("read" -> "Read access", "write" -> "Write access")
))
),
description = Some(md"OAuth 2.0 authorization")
)
val components = Components(
securitySchemes = ChunkMap(
"api_key" -> ReferenceOr.Value(apiKeyScheme),
"oauth2" -> ReferenceOr.Value(oauthScheme)
)
)Distinguish parameter locations using ParameterLocation:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val pathParam = Parameter(
name = "id",
in = ParameterLocation.Path,
required = true,
schema = Some(ReferenceOr.Value(Schema[Int].toOpenAPISchema))
)
val queryParam = Parameter(
name = "limit",
in = ParameterLocation.Query,
required = false,
schema = Some(ReferenceOr.Value(Schema[Int].toOpenAPISchema))
)
val headerParam = Parameter(
name = "X-Custom-Header",
in = ParameterLocation.Header,
required = false,
schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema))
)The OpenAPI module integrates tightly with other ZIO Blocks components:
- Schema Integration: All OpenAPI types have
Schema.derivedinstances, enabling round-trip serialization viaDynamicValue. UseSchema[A].toOpenAPISchemato convert any schema to an OpenAPI component. - Markdown Support: Description fields use the in-house
Doctype, which supports CommonMark rendering. This ensures markdown descriptions round-trip correctly. - JSON AST: All codecs operate on the
JsonAST fromzio-blocks-schema, not external JSON libraries. To render as YAML, pipe theJsonthroughzio-blocks-schema-yamlseparately.
OpenAPI is the root document object representing a complete OpenAPI 3.1 specification.
Every OpenAPI document requires:
openapi: Version string (typically"3.1.0")info: Metadata about the API (Info)
Optional top-level fields include:
servers: Server definitions for the API (Chunk[Server])paths: Map of endpoint paths to operations (Paths)components: Reusable schemas, responses, parameters, and other components (Components)security: Security requirements applied to the API (Chunk[SecurityRequirement])
Response definitions are modeled per Operation, not as a top-level field on OpenAPI.
To construct an OpenAPI document:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val minimalApi = OpenAPI(
openapi = "3.1.0",
info = Info(title = "My API", version = "1.0.0")
)Add paths, operations, and components as shown in the "How They Work Together" section above.
Encode an OpenAPI document to Json AST:
import zio.blocks.openapi._
import zio.blocks.openapi.OpenAPICodec._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
import zio.blocks.schema.json._
val myApi = OpenAPI(openapi = "3.1.0", info = Info(title = "My API", version = "1.0.0"))
val encoded: Json = openAPICodec.encodeValue(myApi)Decode from Json AST back to an OpenAPI instance:
import zio.blocks.openapi._
import zio.blocks.openapi.OpenAPICodec._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
import zio.blocks.schema.json._
val myApi = OpenAPI(openapi = "3.1.0", info = Info(title = "My API", version = "1.0.0"))
val encoded: Json = openAPICodec.encodeValue(myApi)
val decoded: OpenAPI = openAPICodec.decodeValue(encoded)Info contains metadata about the API: title, version, contact, and license.
Required fields:
title: API name (e.g.,"User API")version: API version (e.g.,"1.0.0")
Optional fields:
description: Markdown-formatted description (Doc)termsOfService: Terms of service URLcontact: Contact information (Contact)license: License information (License)
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val info = Info(
title = "Pet Store API",
version = "3.0.0",
description = Some(md"API for managing a pet store"),
contact = Some(Contact(
name = Some("API Support"),
url = Some("https://example.com/support"),
email = Some("support@example.com")
)),
license = Some(License(
name = "Apache 2.0",
identifier = Some("Apache-2.0")
))
)Paths represents the collection of URL paths and their operations. PathItem groups HTTP methods (GET, POST, PUT, etc.) on a single path.
Paths is a wrapper case class with two fields:
paths:ChunkMap[String, PathItem], where keys are path strings (e.g.,"/users/{id}")extensions:ChunkMap[String, Json], for OpenAPI specification extensions
PathItem contains optional fields for each HTTP method:
get,post,put,delete,patch,head,options,trace:Operationinstancesparameters: Path-level parameters shared by all methods on this pathservers: Optional server overrides for this path
To define a path with multiple operations:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val userPaths = Paths(ChunkMap(
"/users" -> PathItem(
get = Some(Operation(
summary = Some(md"List users"),
responses = Responses(ChunkMap(
"200" -> ReferenceOr.Value(Response(
description = md"User list",
content = ChunkMap(
"application/json" -> MediaType(schema = None)
)
))
))
)),
post = Some(Operation(
summary = Some(md"Create user"),
requestBody = Some(ReferenceOr.Value(RequestBody(
description = Some(md"User data"),
content = ChunkMap(
"application/json" -> MediaType(schema = None)
),
required = true
))),
responses = Responses(ChunkMap(
"201" -> ReferenceOr.Value(Response(
description = md"User created",
content = ChunkMap(
"application/json" -> MediaType(schema = None)
)
))
))
))
),
"/users/{id}" -> PathItem(
parameters = Chunk(
ReferenceOr.Value(Parameter(
name = "id",
in = ParameterLocation.Path,
required = true,
schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema))
))
),
get = Some(Operation(
summary = Some(md"Get user by ID"),
responses = Responses(ChunkMap(
"200" -> ReferenceOr.Value(Response(
description = md"User found",
content = ChunkMap(
"application/json" -> MediaType(schema = None)
)
)),
"404" -> ReferenceOr.Value(Response(
description = md"User not found",
content = ChunkMap(
"application/json" -> MediaType(schema = None)
)
))
))
))
)
))Operation represents a single HTTP operation (GET, POST, etc.) on a path.
Key fields:
responses: Required. Map of status codes to response definitionsoperationId: Unique operation identifiersummary: Short descriptiondescription: Detailed markdown descriptionparameters: Path, query, header, and cookie parametersrequestBody: Request payload definitiondeprecated: Whether the operation is deprecatedtags: Group operations in documentation (e.g.,"users","products")
With summary, description, and parameters:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val getUser = Operation(
tags = Chunk("users"),
summary = Some(md"Retrieve user"),
description = Some(md"Fetches a single user by ID"),
operationId = Some("getUserById"),
parameters = Chunk(
ReferenceOr.Value(Parameter(
name = "id",
in = ParameterLocation.Path,
required = true,
schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema)),
description = Some(md"User ID")
))
),
responses = Responses(ChunkMap(
"200" -> ReferenceOr.Value(Response(
description = md"User found",
content = ChunkMap(
"application/json" -> MediaType(schema = None)
)
)),
"404" -> ReferenceOr.Value(Response(
description = md"User not found",
content = ChunkMap(
"application/json" -> MediaType(schema = None)
)
))
))
)Parameter represents query, path, header, or cookie parameters in a request.
Required fields:
name: Parameter name (e.g.,"id","limit")in: Location—Path,Query,Header, orCookie(ParameterLocation)schema: Data type of the parameter (ReferenceOr[SchemaObject])
Optional fields:
description: Markdown descriptionrequired: Whether the parameter is mandatory (default:false)deprecated: Whether the parameter is deprecatedallowEmptyValue: Whether empty string values are allowed
Path parameter (required):
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val idPathParam = Parameter(
name = "id",
in = ParameterLocation.Path,
required = true,
schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema)),
description = Some(md"User identifier")
)Query parameter (optional with default):
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val limitQueryParam = Parameter(
name = "limit",
in = ParameterLocation.Query,
required = false,
schema = Some(ReferenceOr.Value(Schema[Int].toOpenAPISchema)),
description = Some(md"Maximum number of results (default: 20)")
)Header parameter:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val authHeaderParam = Parameter(
name = "X-API-Key",
in = ParameterLocation.Header,
required = true,
schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema)),
description = Some(md"API key for authentication")
)RequestBody defines the structure of a request payload. Response defines the structure and status of a response.
Key fields:
content: Map of MIME types toMediaTypedefinitionsdescription: Optional markdown descriptionrequired: Whether the request body is mandatory (default:false)
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
import zio.blocks.schema.json._
case class User(name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }
val createUserBody = RequestBody(
description = Some(md"User data to create"),
content = ChunkMap(
"application/json" -> MediaType(
schema = Some(ReferenceOr.Value(Schema[User].toOpenAPISchema)),
example = Some(Json.Object(Chunk(
"name" -> Json.String("John Doe"),
"email" -> Json.String("john@example.com")
)))
)
),
required = true
)Key fields:
description: Required. Markdown description of the responsecontent: Map of MIME types toMediaTypedefinitionsheaders: Optional response headerslinks: Optional links to related operations
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
case class User2(id: Int, name: String, email: String)
object User2 { implicit val schema: Schema[User2] = Schema.derived }
case class ErrorResponse2(code: Int, message: String)
object ErrorResponse2 { implicit val schema: Schema[ErrorResponse2] = Schema.derived }
val successResponse = Response(
description = md"User successfully created",
content = ChunkMap(
"application/json" -> MediaType(
schema = Some(ReferenceOr.Value(Schema[User2].toOpenAPISchema))
)
)
)
val errorResponse = Response(
description = md"Request validation failed",
content = ChunkMap(
"application/json" -> MediaType(
schema = Some(ReferenceOr.Value(Schema[ErrorResponse2].toOpenAPISchema))
)
)
)Responses is a map of HTTP status codes to ReferenceOr[Response]:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val ok = Response(description = md"Created", content = ChunkMap())
val err = Response(description = md"Bad request", content = ChunkMap())
val responses = Responses(ChunkMap(
"201" -> ReferenceOr.Value(ok),
"400" -> ReferenceOr.Value(err),
"401" -> ReferenceOr.Value(Response(
description = md"Unauthorized",
content = ChunkMap()
)),
"500" -> ReferenceOr.Value(Response(
description = md"Internal server error",
content = ChunkMap()
))
))MediaType specifies the schema and encoding for a particular MIME type in a request or response.
Key fields:
schema: Data type for this MIME type (ReferenceOr[SchemaObject])example: Example value asJsonencoding: Encoding rules for multipart form dataextensions: Custom vendor extensions (x-*fields)
With schema and example:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
import zio.blocks.schema.json._
case class User(id: Int, name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }
val jsonMedia = MediaType(
schema = Some(ReferenceOr.Value(Schema[User].toOpenAPISchema)),
example = Some(Json.Object(
"id" -> Json.Number(1),
"name" -> Json.String("Alice"),
"email" -> Json.String("alice@example.com")
))
)For form data:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val formMedia = MediaType(
schema = Some(ReferenceOr.Value(Schema[Map[String, String]].toOpenAPISchema)),
encoding = ChunkMap(
"file" -> Encoding(
contentType = Some("application/octet-stream")
)
)
)Components stores reusable schema and security definitions referenced throughout the document.
Key fields:
schemas: Reusable schema objects (ChunkMap[String, ReferenceOr[SchemaObject]])responses: Reusable response definitionsparameters: Reusable parameter definitionssecuritySchemes: Authentication method definitionsexamples,requestBodies,headers,links,callbacks: Additional reusable components
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
case class User(id: Int, name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }
case class ErrorResponse(code: Int, message: String)
object ErrorResponse { implicit val schema: Schema[ErrorResponse] = Schema.derived }
val components = Components(
schemas = ChunkMap(
Schema[User].toRefSchema._2._1 -> ReferenceOr.Value(Schema[User].toRefSchema._2._2),
Schema[ErrorResponse].toRefSchema._2._1 -> ReferenceOr.Value(Schema[ErrorResponse].toRefSchema._2._2)
),
parameters = ChunkMap(
"id" -> ReferenceOr.Value(Parameter(
name = "id",
in = ParameterLocation.Path,
required = true,
schema = Some(ReferenceOr.Value(Schema[String].toOpenAPISchema))
))
),
responses = ChunkMap(
"NotFound" -> ReferenceOr.Value(Response(
description = md"Resource not found",
content = ChunkMap(
"application/json" -> MediaType(
schema = Some(ReferenceOr.Value(
Schema[ErrorResponse].toOpenAPISchema
))
)
)
)),
"Unauthorized" -> ReferenceOr.Value(Response(
description = md"Unauthorized access",
content = ChunkMap()
))
),
securitySchemes = ChunkMap(
"api_key" -> ReferenceOr.Value(SecurityScheme.APIKey(
name = "X-API-Key",
in = APIKeyLocation.Header,
description = Some(md"API key header")
))
)
)SchemaObject wraps a JSON Schema 2020-12 definition with OpenAPI-specific extensions like discriminator, XML metadata, and examples.
SchemaObject contains:
jsonSchema: Raw JSON Schema 2020-12 asJsonASTdiscriminator: Polymorphism discriminator for oneOf/anyOfxml: XML serialization metadataexample: Example value for documentationextensions: Customx-*fields
Directly from a Schema[A]:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
case class User(id: Int, name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }
val userSchema = Schema[User].toOpenAPISchema
// Returns a SchemaObject with the User type's JSON SchemaOr with additional OpenAPI metadata:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
import zio.blocks.schema.json._
case class User(id: Int, name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }
val enrichedSchema = SchemaObject(
jsonSchema = Schema[User].toJsonSchema.toJson,
discriminator = None,
xml = Some(XML(
name = Some("user"),
namespace = None,
prefix = None,
attribute = false,
wrapped = false
)),
example = Some(Json.Object(
"id" -> Json.Number(1),
"name" -> Json.String("John")
)),
extensions = ChunkMap(
"x-generated" -> Json.String("true"),
"x-version" -> Json.String("1.0.0")
)
)Use the SchemaOps extension methods on any Schema[A]:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
case class User(id: Int, name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }
val schemaObj: SchemaObject = Schema[User].toOpenAPISchema
val (ref, component) = Schema[User].toRefSchema
// ref = ReferenceOr.Ref pointing to #/components/schemas/User
// component = ("User", SchemaObject(...))ReferenceOr[A] is a sealed trait representing the OpenAPI pattern of choosing between a $ref and an inline value.
Two cases:
ReferenceOr.Ref: Points to a definition at#/components/<type>/<name>ReferenceOr.Value: Inline definition without reference
Prefer Ref for reusable components:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val userRef = ReferenceOr.Ref(Reference(`$ref` = "#/components/schemas/User"))
val responseRef = ReferenceOr.Ref(Reference(
`$ref` = "#/components/responses/NotFound"
))Use Value for inline, one-off definitions:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
case class User(id: Int, name: String, email: String)
object User { implicit val schema: Schema[User] = Schema.derived }
val inlineUser = ReferenceOr.Value(Schema[User].toOpenAPISchema)
val inlineError = ReferenceOr.Value(Response(
description = md"Quick error",
content = ChunkMap()
))import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
def describeRef[A](ref: ReferenceOr[A]): String = ref match {
case ReferenceOr.Ref(r) => s"Reference to ${r.`$ref`}"
case ReferenceOr.Value(_) => "Inline value"
}SecurityScheme is a sealed trait representing different authentication methods. Variants include API Key, HTTP Basic/Bearer, OAuth 2.0, OpenID Connect, and Mutual TLS.
Sealed trait variants:
APIKey: API key in header, query, or cookieHTTP: HTTP authentication (Basic, Bearer, etc.)OAuth2: OAuth 2.0 authorization flowsOpenIdConnect: OpenID Connect discoveryMutualTLS: Mutual TLS certificate-based
API Key authentication:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val apiKeySecurity = SecurityScheme.APIKey(
name = "X-API-Key",
in = APIKeyLocation.Header,
description = Some(md"API key required in header")
)HTTP Bearer token:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val bearerSecurity = SecurityScheme.HTTP(
scheme = "bearer",
bearerFormat = Some("JWT"),
description = Some(md"JWT bearer token")
)OAuth 2.0:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val oauthSecurity = SecurityScheme.OAuth2(
flows = OAuthFlows(
authorizationCode = Some(OAuthFlow(
authorizationUrl = Some("https://example.com/oauth/authorize"),
tokenUrl = Some("https://example.com/oauth/token"),
scopes = ChunkMap(
"read:users" -> "Read user data",
"write:users" -> "Modify user data"
)
))
),
description = Some(md"OAuth 2.0 authorization")
):::note
The OAuthFlows type supports multiple flow types: implicit, password, clientCredentials, and authorizationCode. Choose the flow that matches your OAuth 2.0 configuration.
:::
OpenID Connect:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val oidcSecurity = SecurityScheme.OpenIdConnect(
openIdConnectUrl = "https://example.com/.well-known/openid-configuration",
description = Some(md"OpenID Connect discovery")
)Discriminator specifies how to distinguish between different variants in a polymorphic schema (using oneOf or anyOf).
Key fields:
propertyName: Field name used to discriminate (e.g.,"type","kind")mapping: Optional explicit mapping of discriminator values to schema references
Simple discriminator by property name:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val discriminator = Discriminator(propertyName = "type")With explicit value-to-schema mapping:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val mappedDiscriminator = Discriminator(
propertyName = "kind",
mapping = ChunkMap(
"user" -> "#/components/schemas/User",
"admin" -> "#/components/schemas/Admin",
"guest" -> "#/components/schemas/Guest"
)
)Server specifies base URLs and server-specific variables. ServerVariable allows parameterization of server URLs.
Server contains:
url: Server URL (may contain variable placeholders like{base_path})description: Optional descriptionvariables: Map of variable names toServerVariable
ServerVariable contains:
enum: List of allowed valuesdefault: Default valuedescription: Description of the variable
Single static server:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val server = Server(
url = "https://api.example.com",
description = Some(md"Production API")
)Server with variables:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val variableServer = Server(
url = "https://{host}:{port}/{basePath}",
description = Some(md"Development API with variables"),
variables = ChunkMap(
"host" -> ServerVariable(
default = "localhost",
`enum` = Chunk("localhost", "staging.example.com", "api.example.com"),
description = Some(md"API host")
),
"port" -> ServerVariable(
default = "8080",
`enum` = Chunk("8080", "443"),
description = Some(md"Port number")
),
"basePath" -> ServerVariable(
default = "v1",
`enum` = Chunk("v1", "v2"),
description = Some(md"API version path")
)
)
):::note
ServerVariable contains enumerable values for each variable. The field is named to avoid the reserved enum keyword in Scala.
:::
Tag groups related operations under a heading in generated documentation.
Key fields:
name: Tag identifier (e.g.,"users","products")description: Markdown description of the tagexternalDocs: Link to external documentation
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val userTag = Tag(
name = "users",
description = Some(md"User management operations")
)
val productsTag = Tag(
name = "products",
description = Some(md"Product catalog operations"),
externalDocs = Some(ExternalDocumentation(
url = "https://docs.example.com/products",
description = Some(md"Full product API documentation")
))
)All types support custom x-* extension fields for vendor-specific metadata. These extensions are preserved during encoding/decoding:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
import zio.blocks.schema.json._
val operationWithExtensions = Operation(
summary = Some(md"Get user"),
responses = Responses(ChunkMap(
"200" -> ReferenceOr.Value(Response(
description = md"User found",
content = ChunkMap()
))
)),
extensions = ChunkMap(
"x-internal" -> Json.Boolean(true),
"x-rate-limit" -> Json.Number(100),
"x-deprecated-at" -> Json.String("2024-01-01")
)
)All OpenAPI types have Schema.derived instances, enabling serialization through DynamicValue:
import zio.blocks.openapi._
import zio.blocks.docs._
import zio.blocks.chunk._
import zio.blocks.schema._
val myApi = OpenAPI(openapi = "3.1.0", info = Info(title = "My API", version = "1.0.0"))
val openAPISchema = Schema[OpenAPI]
val apiDynamic = openAPISchema.toDynamicValue(myApi)
val apiRestored = openAPISchema.fromDynamicValue(apiDynamic)This enables integration with other ZIO Blocks modules that work with DynamicValue.