Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Skyzen
An HTTP framework for Rust that compiles to a native Tokio server or to a
WinterCG fetch handler for Cloudflare Workers, from the same source.
Most Rust web frameworks assume a long-lived process with a thread pool. Most
edge SDKs assume a single-threaded WASM sandbox and hand you provider types
directly. Skyzen targets both: handlers are written against portable traits
(Kv, Storage, Queue, Db), Send bounds are applied conditionally per
target, and #[skyzen::main] expands to either a fn main() or a fetch
export depending on target_arch. When you need something a portable trait
can't express — D1's raw SQL, Durable Objects, alarms — the provider types are
still there.
Quick start
[]
= "0.1"
use ;
cargo run starts the server and logs its address. cargo run -- --port 8787
pins the port.
Routing
Routes are built from path literals. CreateRouteNode adds .at(), .get(),
.post(), .put(), .delete(), .ws(), and .route() to &str, so a route
tree is just a tuple of them:
use ;
Matching is done by matchit's radix
tree, so a request is resolved by walking its own path rather than by testing
routes one at a time.
Handlers
A handler is an async function. Its arguments are extractors, its return type is a responder — nothing needs to be registered, and tuples of either compose automatically.
use ;
async
async
Json, Form, Query, Params, Multipart, State, BearerToken, and
ClientIp are extractors out of the box. String, &str, Json<T>,
Response, and Result<T> are responders. Implement Extractor or
Responder for your own types.
Errors
#[skyzen::error] writes the Display, Error, and HttpError impls, and
maps each variant to a status code. Messages interpolate fields the same way
thiserror does:
Returning Err(ApiError::UserNotFound(7)) from a handler produces
404 {"error":"no user with id 7"}. Note that 5xx responses replace the
message with a generic "Internal server error" — a Display impl is for
your logs, and shouldn't leak into a client's error body by accident.
Portable services
skyzen-services exposes four capability wrappers — Kv, Storage, Queue,
and Db. Handlers take them as extractors and never name a provider type:
use ;
async
The backend is chosen once, at wiring time:
// Native
let kv = new;
let storage = new;
// Cloudflare
let kv = new;
let storage = new;
// Tests
let kv = new;
let storage = new;
| Capability | Native | Cloudflare | AWS | Azure | Test |
|---|---|---|---|---|---|
| Key-value | skyzen-redis |
CfKv |
DynamoKv |
CosmosKv |
InMemoryKv |
| Object storage | skyzen-s3 |
CfR2 |
S3Storage |
AzureBlob |
InMemoryStorage |
| Message queue | — | CfQueue |
SqsQueue |
ServiceBusQueue |
InMemoryQueue |
| SQL | Db via sqlx |
Db via D1 |
— | — | — |
Native and Cloudflare are wired automatically from Skyzen.toml. The AWS and
Azure crates are usable as backends, but you construct and inject them
yourself; there is no automatic wiring for them yet.
The provider-specific types (CfD1, DurableKv, DurableDb, Alarm) are
public and can be mixed into the same app. See the
services guide and the
Durable Object + SQL guide.
Running on the edge
The same router runs on Cloudflare Workers. Build a cdylib instead of a
binary:
[]
= ["cdylib", "rlib"]
On wasm32, #[skyzen::main] emits a WinterCG fetch export instead of a
main. Workers' queue and cron entrypoints have their own macros:
async
async
Stateful workloads use #[skyzen::durable_object] with the DurableObject
trait. Full setup is in the deployment guide.
WebSocket
new
Native uses async-tungstenite; WASM uses WebSocketPair. Two differences
worth knowing: WASM caps messages at 1 MiB, and it gives no control over
ping/pong frames.
OpenAPI
Annotated handlers register themselves at compile time through linkme, so
the spec is assembled from the same signatures the router uses:
/// Fetch a user by id.
async
Doc comments become descriptions. Generation is gated on debug_assertions
and native targets, so neither release builds nor Workers bundles carry the
schema.
#[skyzen::main]
On native targets the macro sets up a Tokio runtime and Hyper server, installs
a tracing subscriber that respects RUST_LOG, parses --host / --port /
--listen, and shuts down gracefully on Ctrl+C. To install your own
subscriber:
async
If you need to drive the server yourself — embedding Skyzen in a larger
process, or using a non-Tokio executor — skyzen-hyper implements the Server
trait directly:
use Hyper;
Hyper.serve.await;
CLI
Bindings, queues, and databases are declared in
Skyzen.toml. For Cloudflare the CLI
generates .skyzen/gen/wrangler.toml — don't hand-edit wrangler.toml.
Crates
| Crate | What it is |
|---|---|
skyzen |
Routing, extractors, responders, middleware, runtime |
skyzen-core |
Extractor, Responder, Server traits; no_std-capable |
skyzen-hyper |
Hyper backend for the Server trait |
skyzen-macros |
#[skyzen::main], #[skyzen::openapi], #[skyzen::error], and friends |
skyzen-services |
Kv, Storage, Queue, Db and the traits behind them |
skyzen-test |
In-memory services, TestClient, assertions |
skyzen-redis |
Redis KeyValueStore |
skyzen-s3 |
S3-compatible ObjectStorage |
skyzen-cloudflare |
Workers KV, R2, Queues, D1, Durable Objects (wasm32 only) |
skyzen-aws |
DynamoDB, SQS, S3 backends |
skyzen-azure |
Cosmos DB, Blob Storage, Service Bus backends |
skyzen-cli |
skyzen new / dev / deploy / doctor |
Guides
Runnable examples live in examples/.
License
Licensed under either of Apache-2.0 or MIT, at your option.