azu
Version, currently 0.5.49198 versions
- 1.0.7latestNov 2, 2026
- 1.0.6not indexedNov 2, 2026
- 1.0.5not indexedNov 2, 2026
- 1.0.4not indexedNov 2, 2026
- 1.0.3Oct 29, 2026
- 1.0.2not indexedOct 29, 2026
- 1.0.1not indexedOct 29, 2026
- 1.0.0not indexedOct 29, 2026
- 0.10.5not indexedOct 29, 2026
- 0.10.4not indexedOct 29, 2026
- 0.10.3not indexedOct 29, 2026
- 0.10.2not indexedOct 29, 2026
- 0.10.1not indexedOct 29, 2026
- 0.10.0not indexedOct 29, 2026
- 0.9.2not indexedOct 29, 2026
- 0.9.1not indexedOct 29, 2026
- 0.9.0not indexedOct 29, 2026
- 0.8.1not indexedOct 29, 2026
- 0.8.0not indexedOct 29, 2026
- 0.7.3not indexedOct 29, 2026
- 0.7.2not indexedOct 29, 2026
- 0.7.1not indexedOct 29, 2026
- 0.7.0not indexedOct 29, 2026
- 0.6.2not indexedOct 29, 2026
- 0.6.1not indexedOct 29, 2026
- 0.6.0not indexedOct 29, 2026
- 0.5.136not indexedOct 29, 2026
- 0.5.135not indexedOct 29, 2026
- 0.5.134not indexedOct 29, 2026
- 0.5.133not indexedOct 29, 2026
- 0.5.132not indexedOct 29, 2026
- 0.5.131not indexedOct 29, 2026
- 0.5.130not indexedOct 29, 2026
- 0.5.129not indexedOct 29, 2026
- 0.5.128not indexedOct 29, 2026
- 0.5.127not indexedOct 29, 2026
- 0.5.126not indexedOct 29, 2026
- 0.5.125not indexedOct 29, 2026
- 0.5.124not indexedOct 29, 2026
- 0.5.123not indexedOct 29, 2026
- 0.5.122not indexedOct 29, 2026
- 0.5.121not indexedOct 29, 2026
- 0.5.120not indexedOct 29, 2026
- 0.5.119not indexedOct 29, 2026
- 0.5.118not indexedOct 29, 2026
- 0.5.117not indexedOct 29, 2026
- 0.5.116not indexedOct 29, 2026
- 0.5.115not indexedOct 29, 2026
- 0.5.114not indexedOct 29, 2026
- 0.5.113not indexedOct 29, 2026
- 0.5.112not indexedOct 29, 2026
- 0.5.111not indexedOct 29, 2026
- 0.5.110not indexedOct 29, 2026
- 0.5.109not indexedOct 29, 2026
- 0.5.108not indexedOct 29, 2026
- 0.5.107not indexedOct 29, 2026
- 0.5.106not indexedOct 29, 2026
- 0.5.105not indexedOct 29, 2026
- 0.5.104not indexedOct 29, 2026
- 0.5.103not indexedOct 29, 2026
- 0.5.102not indexedOct 29, 2026
- 0.5.101not indexedOct 29, 2026
- 0.5.100not indexedOct 29, 2026
- 0.5.99not indexedOct 29, 2026
- 0.5.98not indexedOct 29, 2026
- 0.5.97not indexedOct 29, 2026
- 0.5.96not indexedOct 29, 2026
- 0.5.95not indexedOct 29, 2026
- 0.5.94Oct 24, 2026
- 0.5.93not indexedOct 24, 2026
- 0.5.92not indexedOct 24, 2026
- 0.5.91not indexedOct 24, 2026
- 0.5.90not indexedOct 24, 2026
- 0.5.89not indexedOct 24, 2026
- 0.5.88not indexedOct 24, 2026
- 0.5.87not indexedOct 24, 2026
- 0.5.86not indexedOct 24, 2026
- 0.5.85not indexedOct 24, 2026
- 0.5.84not indexedOct 24, 2026
- 0.5.83not indexedOct 24, 2026
- 0.5.82not indexedOct 24, 2026
- 0.5.81not indexedOct 24, 2026
- 0.5.80not indexedOct 24, 2026
- 0.5.79not indexedOct 24, 2026
- 0.5.78not indexedOct 24, 2026
- 0.5.77not indexedOct 24, 2026
- 0.5.76not indexedOct 24, 2026
- 0.5.75not indexedOct 24, 2026
- 0.5.74not indexedOct 24, 2026
- 0.5.73not indexedOct 24, 2026
- 0.5.72not indexedOct 24, 2026
- 0.5.71not indexedOct 24, 2026
- 0.5.70not indexedOct 24, 2026
- 0.5.69not indexedOct 24, 2026
- 0.5.68not indexedOct 24, 2026
- 0.5.67not indexedOct 24, 2026
- 0.5.66not indexedOct 24, 2026
- 0.5.65not indexedOct 24, 2026
- 0.5.64not indexedOct 24, 2026
- 0.5.63not indexedOct 24, 2026
- 0.5.62not indexedOct 24, 2026
- 0.5.61not indexedOct 24, 2026
- 0.5.60not indexedOct 24, 2026
- 0.5.59not indexedOct 24, 2026
- 0.5.58not indexedOct 24, 2026
- 0.5.57not indexedOct 24, 2026
- 0.5.56not indexedOct 24, 2026
- 0.5.55not indexedOct 24, 2026
- 0.5.54not indexedOct 24, 2026
- 0.5.53not indexedOct 24, 2026
- 0.5.52not indexedOct 24, 2026
- 0.5.51not indexedOct 24, 2026
- 0.5.50not indexedOct 24, 2026
- 0.5.49not indexedOct 24, 2026
- 0.5.48not indexedOct 24, 2026
- 0.5.47not indexedOct 24, 2026
- 0.5.46not indexedOct 24, 2026
- 0.5.45not indexedOct 24, 2026
- 0.5.44not indexedOct 24, 2026
- 0.5.43not indexedOct 24, 2026
- 0.5.42not indexedOct 24, 2026
- 0.5.41not indexedOct 24, 2026
- 0.5.40not indexedOct 24, 2026
- 0.5.39not indexedOct 24, 2026
- 0.5.38not indexedOct 24, 2026
- 0.5.37not indexedOct 24, 2026
- 0.5.36not indexedOct 24, 2026
- 0.5.35Mar 8, 2026
- 0.5.34not indexedMar 8, 2026
- 0.5.33not indexedMar 8, 2026
- 0.5.32not indexedMar 8, 2026
- 0.5.31not indexedMar 8, 2026
- 0.5.30not indexedMar 8, 2026
- 0.5.29not indexedMar 8, 2026
- 0.5.28not indexedMar 8, 2026
- 0.5.27not indexedMar 8, 2026
- 0.5.26not indexedMar 8, 2026
- 0.5.25not indexedMar 8, 2026
- 0.5.24not indexedMar 8, 2026
- 0.5.23not indexedMar 8, 2026
- 0.5.22not indexedMar 8, 2026
- 0.5.21not indexedMar 8, 2026
- 0.5.20not indexedMar 8, 2026
- 0.5.19not indexedMar 8, 2026
- 0.5.18not indexedMar 8, 2026
- 0.5.17not indexedMar 8, 2026
- 0.5.16not indexedMar 8, 2026
- 0.5.15not indexedMar 8, 2026
- 0.5.14not indexedMar 8, 2026
- 0.5.13not indexedMar 8, 2026
- 0.5.12not indexedMar 8, 2026
- 0.5.11not indexedMar 8, 2026
- 0.5.10not indexedMar 8, 2026
- 0.5.9not indexedMar 8, 2026
- 0.5.8not indexedMar 8, 2026
- 0.5.7not indexedMar 8, 2026
- 0.5.6not indexedMar 8, 2026
- 0.5.5not indexedMar 8, 2026
- 0.5.4not indexedMar 8, 2026
- 0.5.3not indexedMar 8, 2026
- 0.5.2not indexedMar 8, 2026
- 0.5.1not indexedMar 8, 2026
- 0.5.0not indexedMar 8, 2026
- 0.4.14not indexedMar 8, 2026
- 0.4.13not indexedMar 8, 2026
- 0.4.12not indexedMar 8, 2026
- 0.4.11not indexedMar 8, 2026
- 0.4.10not indexedMar 8, 2026
- 0.4.9not indexedMar 8, 2026
- 0.4.8not indexedMar 8, 2026
- 0.4.7not indexedMar 8, 2026
- 0.4.6not indexedMar 8, 2026
- 0.4.5not indexedMar 8, 2026
- 0.4.4not indexedMar 8, 2026
- 0.4.3not indexedMar 8, 2026
- 0.4.2not indexedMar 8, 2026
- 0.4.1not indexedMar 8, 2026
- 0.4.0not indexedMar 8, 2026
- 0.3.3not indexedMar 8, 2026
- 0.3.2not indexedMar 8, 2026
- 0.3.1not indexedMar 8, 2026
- 0.3.0not indexedMar 8, 2026
- 0.2.3not indexedMar 8, 2026
- 0.2.2not indexedMar 8, 2026
- 0.2.1not indexedMar 8, 2026
- 0.2.0not indexedMar 8, 2026
- 0.1.11not indexedMar 8, 2026
- 0.1.10not indexedMar 8, 2026
- 0.1.9not indexedMar 8, 2026
- 0.1.8not indexedMar 8, 2026
- 0.1.7not indexedMar 8, 2026
- 0.1.6not indexedMar 8, 2026
- 0.1.5not indexedMar 8, 2026
- 0.1.4not indexedMar 8, 2026
- 0.1.3not indexedMar 8, 2026
- 0.1.2not indexedMar 8, 2026
- 0.1.1not indexedMar 8, 2026
- 0.1.0not indexedMar 8, 2026
github.com/azutoolkit/azu
Application Development Toolkit for Crystal Language
Nothing has been indexed for 0.5.49 yet. The tag is recorded, its shard.yml has not been read, so the manifest and dependency list below are empty because they are unknown rather than because they are absent.
Installation
# Add this to your shard.yml
dependencies:
azu:
github: azutoolkit/azu
version: ~> 0.5.49Then run:
shards installshard.yml
No shard.yml has been indexed for 0.5.49. You can read it on the repository.
Dependencies
Unknown: the shard.yml for this version has not been read yet.
README
This README is the one indexed from the repository at its latest ref, not from the tag for this version.
Azu
Azu is a web framework for Crystal built on typed contracts and real-time. Every endpoint declares the request it accepts and the response it returns, Azu validates the request before your code runs, and the same contracts give you links, an OpenAPI document and tests. Live components, WebSockets, pub/sub and presence are in the box.
For allocation-sensitive APIs, Azu::Response::Json.new({message: "Hello, World!"})
serializes directly to buffered HTTP IO. See the request-path performance guide
for typed endpoints, multi-core configuration, measurements and streaming tradeoffs.
Hello, contract
require "azu"
module Hello
include Azu
end
struct GreetRequest
include Azu::Request
getter name : String
validate name, size: 2..30, message: "must be 2 to 30 characters"
def initialize(@name = "")
end
end
struct GreetResponse
include Azu::Response
def initialize(@name : String)
end
def render
{message: "Hello, #{@name}!"}.to_json
end
end
struct GreetEndpoint
include Azu::Endpoint(GreetRequest, GreetResponse)
get "/hello"
def call : GreetResponse
content_type "application/json"
GreetResponse.new greet_request.name
end
end
Hello.start
Put this in hello.cr, with a shard.yml next to it, and install:
name: hello
version: 0.1.0
crystal: ">= 1.22.1"
dependencies:
azu:
github: azutoolkit/azu
version: ~> 0.10.0
shards install
crystal run hello.cr
curl "http://127.0.0.1:4000/hello?name=Ada"
# {"message":"Hello, Ada!"}
curl -H "Accept: application/problem+json" "http://127.0.0.1:4000/hello?name=A"
# {"type":"about:blank","title":"Validation Error","status":422,"detail":"The request could not be processed due to validation errors.","errors":{"name":["must be 2 to 30 characters"]},"instance":"/hello"}
call never ran for the second request: the contract failed first. The getting started tutorial builds this out step by step.
Get started in 5 minutes
The Azu CLI generates a working app. You need Crystal 1.22.1 or newer with shards, a C toolchain, and the development libraries Crystal and the CLI link against (on Debian or Ubuntu: build-essential libreadline-dev libevent-dev libssl-dev libxml2-dev libyaml-dev libgmp-dev libsqlite3-dev).
git clone https://github.com/azutoolkit/azu_cli && cd azu_cli
shards install
make install PREFIX=$HOME/.local # builds `azu` into ~/.local/bin (put it on your PATH)
cd ..
azu new blog --db sqlite --no-joobq --yes # a project, with its shards installed
cd blog
azu generate scaffold Post title:string body:text
azu db:setup # creates the SQLite database and runs the migration
azu dev # builds, serves, and rebuilds when you change code
The first azu dev build takes a minute or so. Installing with a PREFIX you own keeps bin/ and lib/ of the clone from becoming root-owned, which a plain sudo make install (it rebuilds, as root) would do.
Open http://localhost:4000/posts. The scaffold gave you a model, a migration, request contracts, endpoints, pages, templates and CSRF-protected forms, named Blog::Posts::CreateEndpoint, Blog::Posts::CreateRequest and so on. azu routes lists them with their contract types.
blog/
├── src/
│ ├── blog.cr # the app: handlers, CSRF, configuration
│ ├── server.cr # Blog.start
│ ├── endpoints/ # Endpoint(Request, Response)
│ ├── requests/ # contracts and validation
│ ├── pages/ # responses that render templates
│ ├── models/ # CQL models
│ ├── db/ # schema.cr and migrations/
│ └── channels/ components/ services/ middleware/
├── public/ # static files and Jinja templates
├── spec/
└── .env # PORT, CSRF_SECRET (git-ignored)
azu new defaults to PostgreSQL; pass --db sqlite to try things without a database server. All commands are in the CLI reference.
A tour
Contracts are checked before your code. A request that fails validation never reaches call: bad values get a 422 problem document, a request that cannot be built at all gets a 400. See Handle validation errors.
Links and forms follow your routes. Each endpoint generates template helpers from its verb and type, so renaming a route updates every link:
{{ link_to_get_posts_show("Read it", id=post.id) }}
{{ form_for_patch_posts_update(id=post.id) }}
{{ csrf_field() }}
...
{{ end_form() }}
Spark components keep state on the server. The browser sends events over a WebSocket and gets back only the HTML that changed:
class Counter < Azu::Spark::Component
mountable
prop label : String = "Clicks"
state count : Int32 = 0
event def increment(by : Int32 = 1) : Nil
self.count += by
end
def render
div class: "counter" do
h2 "#{label}: #{count}"
button "+1", **spark_click(:increment)
button "+10", **spark_click(:increment, by: 10)
end
end
end
Channels, pub/sub and presence. Topics work in one process or across many through Redis:
class RoomChannel < Azu::Channel
include Azu::Presence::Tracked
ws "/rooms/:room"
def on_connect
subscribe room
presence_updates room # joins and leaves reach this socket
track_presence room, params["user"]
end
def on_message(message : String)
broadcast room, message
end
def on_binary(binary : Bytes)
end
def on_ping(message : String)
end
def on_pong(message : String)
end
def on_close(code : HTTP::WebSocket::CloseCode?, message : String?)
end
private def room : String
"room:#{context.request.path_params["room"]}"
end
end
A test client that goes through the real router. No socket, the whole handler chain:
require "azu/testing"
client = Azu::Testing::Client.new
response = client.get "/hello", params: {name: "Ada"}
response.status_code # => 200
response.json["message"] # => "Hello, Ada!"
OpenAPI from the contracts. The document is built from your Endpoint(Request, Response) types, so it is generated from your types and cannot go stale:
info = Azu::OpenAPI::Info.new("Hello API", "1.0.0")
puts Azu::OpenAPI.document(Azu::App.default, info).to_json(pretty: true)
Shared browser and terminal diagnostics. azu dev can consume the same health, latency, cache, component, and tracing snapshot as the browser. Older telemetry fields remain compatible; optional session and mailer adapters report metadata only. See development telemetry.
A dashboard while you develop. require "azu/dev" adds routes, configuration, logs and timings at /dev-dashboard, and with CQL every query tied to its request, with N+1 detection. See the tutorial.
Everything in the box
Features marked opt-in need their own require. The full list has a sentence and a link for each.
| Area | What you get | Learn more |
|---|---|---|
| Routing | Typed endpoints, segment-tree router, scope, mount, apps as values (Azu::App) | Router, App |
| Contracts | Azu::Request with validation rules, typed params, uploads, 422 problem documents | Request |
| Responses | Content negotiation, streaming and server-sent events, error classes and pages | Response, Streaming |
| Real-time | Channels, PubSub (memory or Redis), presence, Spark live views | Channel, Presence, Spark |
| Templates | Jinja (Crinja), preloaded at boot outside development, form, link, asset, date, number, HTML and i18n helpers | Helpers |
| Security | CSRF, security headers, CORS, throttling, IP spoofing guard, session seam, secrets | Handlers, Env |
| Operations | Memory and Redis cache, static files, compression, health check, request ids, graceful shutdown | Cache, Shutdown |
| Testing and docs | Test client (opt-in azu/testing), OpenAPI 3.1 from your contracts | Testing, OpenAPI |
| Development | Dashboard (opt-in azu/dev), SQL and N+1 insight (opt-in azu/cql), .env loading (Azu::Env.load_all, which generated apps call before require "azu"), the CLI | CQL bridge, Azu::Env |
Performance
Azu is not the fastest thing you can write in Crystal, and it does not claim to be. The repository has a reproducible benchmark suite that compares it with a hand-written HTTP::Server, Kemal and Lucky: same machine, same responses, same load. The numbers below are one snapshot, not a promise.
Measured on 2026-10-26 (UTC): Azu 0.5.123 at commit cc96c64 with CQL 0.0.808 (an uncommitted bump in the benchmark app on a dirty working tree; scripts/bench.sh as committed builds the app with CQL 0.0.541), Crystal 1.22.1 (--release), a 4 vCPU shared virtual machine, wrk with 64 connections, median of 5 runs of 10 seconds, servers and load generator on separate CPUs. That is older than the current release (0.9.x). It predates the near-zero default middleware (#229), the segment-tree router (#234) and the server limits (#256, #258), so the default-stack column and the middleware gap may not describe the current release; a re-run on 0.9.x is pending. Each cell is requests per second as a percentage of the baseline in the same scenario (82,261 req/s on plaintext), a hand-written HTTP::Server.
| Scenario | Azu (default stack) | Azu (minimal stack) | Kemal | Lucky |
|---|---|---|---|---|
| plaintext | 80 | 90 | 73 | 82 |
| JSON | 80 | 84* | 73 | 80 |
| 100 parameterized routes | 77 | 81 | 73 | 83* |
| template† | 22 | 22 | 82 | 46 |
| one SQLite row | 72 | 77 | 77 | 78 |
| 20 SQLite rows | 73 | 75 | 90 | 91 |
* the repetitions disagreed by more than 5 %, so treat the cell as indicative. † the baseline itself was noisy in this scenario (5.5 %), so the percentages in that row are soft; Azu's own numbers were steady. The WebSocket scenario is left out: Lucky has no WebSocket support, and for the others the load generator was the limit (about 190,000 deliveries per second each), so it says nothing about who is faster. Athena, which earlier snapshots included, was not part of this run.
Good to know when you read it:
- Two Azu columns. The default stack runs
RequestId,SecurityHeadersandStaticon every request and sends more headers. The minimal stack is the router, the endpoint,Rescuerand the smallApp::Scopewrapper everyAzu::Appputs in front of the chain, so the gap between the columns is what that default middleware costs: about 10 points on plaintext, 4 to 5 on JSON, routing and one SQLite row, about 2 on 20 rows, and not measurable on templates. - Templates are Azu's weak spot. Jinja (Crinja) templates are parsed once at boot but interpreted on every render; the others run ECR the compiler turned into plain code. This compares template systems, not routers.
- The ORM costs something. Azu is the only app going through an ORM (CQL 0.0.808); the others call SQLite directly. With raw SQLite in the same Azu app the 20 row scenario measured 92 to 93 % instead of 73 to 75 %, and the one row scenario 78 to 80 % instead of 72 to 77 %.
- Do not compare with older snapshots. The previous one (Azu 0.5.119, Crystal 1.19.1) showed higher absolute numbers, but the whole machine was about 15 to 25 % slower this time (the unchanged baseline app lost that much), and the compiler changed too. We cannot tell the compiler effect from the machine's mood, so the percentages are only good within this one run, and differences of a few points are inside its run-to-run spread. Read it as "Azu sits between 77 and 90 % of the hand-written server on plaintext, JSON and routing", not as a speed-up or a regression.
- One shared VM is noisy. Compare these numbers with each other, never with numbers from another machine.
The raw snapshot is committed: report and JSON. To reproduce on your own machine, run scripts/bench.sh; method, fairness rules and known confounds are in benchmark/README.md.
Documentation
- New here? Getting started, then Building a user API.
- Doing a task? How-to guides.
- Looking something up? Reference.
- Want the why? Concepts, starting with Everything in the box.
- Want running code? Examples: the apps in
playground/and how to start them. - Upgrading? Migrate to 0.6 and breaking changes.
Status and compatibility
- Azu is 0.x: the API can still change between minor versions, and every break is written down in breaking changes.
- Crystal 1.22.1 or newer. CI runs 1.22.1 (the oldest supported version) and the latest release.
- Redis is optional for Azu: it backs the cache, pub/sub and presence when you want them shared between processes. The JoobQ job queue that
azu newincludes by default does need it (--no-joobqleaves it out). - Databases go through CQL (
azu/cqlneeds cql 0.0.609 or newer, see the CQL bridge page). The end-to-end checks of generated apps run on SQLite; PostgreSQL apps are only booted in CI. - Most Crystal examples in this README and in the docs are compiled in CI against the code in this repository; the ones that need another shard or another file are marked as fragments and are not.
Contributing
Bug reports and pull requests are welcome. Contributing setup covers building and testing; standards covers style. Run crystal spec and crystal tool format --check src spec before you push.
Found a vulnerability? Please report it privately as SECURITY.md describes, not in a public issue.
License
MIT, see LICENSE. Created by Elias J. Perez and the contributors.
Documentation
Built from the current release. The first visit to a release nobody has asked for starts its build.
Links
This release
- Version
0.5.49- Tagged
- Oct 24, 2026
- Commit
4bdac9fa4630- Indexed
- not yet
Dependents
Repository
github.com/azutoolkit/azu
Metadata
- Created
- Aug 12, 2026
- Updated
- Nov 9, 2026
- Synced
- Nov 9, 2026
- Versions
- 198