kemal-openapi
Version, currently 0.4.03 versions
- 0.4.0latestApr 18, 2026
- 0.3.0not indexedMay 19, 2026
- 0.2.0not indexedMay 19, 2026
github.com/erayjsx/kemal-openapi
OpenAPI 3.1 documentation generator for Kemal. Automatically generates Swagger/OpenAPI specs from annotations, validates requests, and serves Swagger UI.
Installation
# Add this to your shard.yml
dependencies:
kemal-openapi:
github: erayjsx/kemal-openapi
version: ~> 0.4.0Then run:
shards installshard.yml
- Crystal
>= 1.19.1- License
- MIT
- Author
- Eray can Topcu
Dependencies
Runtime Dependencies
- kemal~> 1.11github: kemalcr/kemal
README
kemal-openapi
OpenAPI 3.1 documentation generator for Kemal. Automatically generates Swagger/OpenAPI specs, validates requests at runtime, and serves Swagger UI & ReDoc.
Features
- OpenAPI 3.1.0 spec generation
- Annotation-based route definitions (
@[OpenAPI(...)]) - Runtime Request Validation (automatically validates request body against schema)
- Built-in Swagger UI at
/docs - Built-in ReDoc at
/redoc - JSON spec endpoint at
/openapi.json - Schema components &
$refsupport - Security schemes (Bearer, API Key, OAuth2)
- Compile-time schema generation from
JSON::Serializabletypes
Installation
Requirements
- Crystal
>= 1.19.1 - Kemal
~> 1.11
Add to your shard.yml:
dependencies:
kemal-openapi:
github: erayjsx/kemal-openapi
version: ~> 0.4.0
shards install
Quick Start
1. Define your models
require "json"
require "kemal-openapi"
class User
include JSON::Serializable
property id : Int32
property name : String
property email : String
end
# Register the schema for reuse
Kemal::OpenAPI.register_schema("User", Kemal::OpenAPI.schema_for(User))
2. Annotate your routes
require "kemal"
@[OpenAPI(
summary: "Create user",
tags: ["Users"],
request_body: {
description: "User data",
schema: "User", # References the registered "User" schema
required: true
},
responses: {
201 => {description: "User created", schema: "User"},
422 => {description: "Validation error"}
}
)]
post "/users" do |env|
# If execution reaches here, the request body is GUARANTEED to be valid
# according to the "User" schema.
user = User.from_json(env.request.body.not_nil!)
env.response.status_code = 201
user.to_json
end
@[OpenAPI(
summary: "List users",
tags: ["Users"],
responses: {
200 => {description: "List of users", schema: Kemal::OpenAPI.array_of("User")}
}
)]
get "/users" do |env|
[{id: 1, name: "Alice", email: "alice@example.com"}].to_json
end
v0.4.0 Notes
- Requires Kemal
~> 1.11(supports 1.10.1 and 1.11.0+) Kemal::OpenAPI.setupand.configureare idempotent. Calling them repeatedly no longer duplicates handlers or discovered operations.- Missing
$refschemas now fail validation with422instead of being silently ignored. - Route declarations must start with
/(same behavior as Kemal DSL). - New
setupparameters for Kemal 1.10.1+ and 1.11.0+ features:
Kemal::OpenAPI.setup(
title: "My API",
version: "1.0.0",
# Kemal 1.10.1+: graceful shutdown — wait for in-flight requests before exit
shutdown_timeout: 10.seconds,
# Kemal 1.11.0+: max multipart form field size (default: 8MB)
max_multipart_form_field_size: 16 * 1024 * 1024,
# Kemal 1.11.0+: restrict WebSocket connections to allowed origins
websocket_allowed_origins: ["https://myapp.com", "http://localhost:3000"]
)
3. Setup and Run
# Enable OpenAPI docs
Kemal::OpenAPI.setup(
title: "My API",
version: "1.0.0"
)
Kemal.run
Then visit:
- Swagger UI: http://localhost:3000/docs
- ReDoc: http://localhost:3000/redoc
- JSON Spec: http://localhost:3000/openapi.json
Automatic Validation
When you define a request_body with a schema in your annotation, kemal-openapi automatically validates incoming requests before your route handler is executed.
If the validation fails (e.g. missing required fields, wrong data types), the server automatically responds with:
- Status:
400 Bad Request(invalid JSON) or422 Unprocessable Entity(schema validation failure) - Body:
{"error": "validation_error", "message": "..."}
You don't need to write manual validation code for your schemas!
Manual Usage (DSL)
If you prefer not to use annotations, you can still use the manual registration API:
Kemal::OpenAPI.register(
Kemal::OpenAPI::Operation.new(
method: "get",
path: "/api/users",
summary: "List all users",
tags: ["Users"],
responses: {
"200" => Kemal::OpenAPI::Response.new(status_code: 200, description: "User list"),
}
)
)
Development
shards install
crystal spec
Contributing
- Fork it (https://github.com/erayjsx/kemal-openapi/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Contributors
- Eray can Topcu - creator and maintainer
License
Acknowledgments
Thanks to the Crystal and Kemal communities for their support.
MIT
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.4.0- Tagged
- Apr 18, 2026
- Commit
0663e043307a- Crystal
>= 1.19.1- Indexed
- yes
Dependents
No indexed shard depends on this one yet.
Repository
github.com/erayjsx/kemal-openapi
Metadata
- Created
- Aug 12, 2026
- Updated
- Aug 15, 2026
- Synced
- Aug 15, 2026
- Versions
- 3