🏰 Stately
Type-safe state management with entity relationships and CRUD operations
Overview
Stately provides a framework for managing application configuration and state with built-in support for:
- 🔗 Entity Relationships - Reference entities inline or by ID
- 📝 CRUD Operations - Create, read, update, delete for all entity types
- 🔄 Serialization - Full serde support
- 📚 OpenAPI Schemas - Automatic schema generatio with
utoipan - 🆔 Time-Sortable IDs - UUID v7 for naturally ordered identifiers
- 🚀 Web APIs - Optional Axum integration with generated handlers (more frameworks coming soon)
- 🔍 Search & Query: Built-in entity search across collections
Stately does not provide the configuration and structures that comprise the state. Instead it provides an ultra-thin container management strategy that provides seamless integration with @stately/ui.
Installation
Add to your Cargo.toml:
[]
= "0.2.1"
With Axum API generation:
[]
= { = "0.2.1", = ["axum"] }
Quick Start
Define Entities
Use the #[stately::entity] macro to define your domain entities:
use *;
Define State
Use the #[stately::state] macro to create your application state:
This generates:
StateEntryenum for entity type discriminationEntityenum for type-erased entity access- Collections with full CRUD operations
- Search and query methods
Use the #[collection(...)] attributes to configure the application state properties:
// Can be a struct that implements `StateCollection`. This type alias is for simplicity.
type CustomStateCollectionImpl = ;
Use the State
let mut state = new;
// Create entities
let source_id = state.sources.create;
// Reference entities
let pipeline = Pipeline ;
let pipeline_id = state.pipelines.create;
// Query
let = state.get_entity.unwrap;
// List all
let summaries = state.list_entities;
// Search
let results = state.search_entities;
// Update
state.pipelines.update?;
// Delete
state.pipelines.remove?;
📖 Examples
use *;
// Define your entities
// Define your application state
See the examples directory:
basic.rs- Core CRUD operations and entity relationshipsaxum_api.rs- Web API generation with Axum
Run examples:
Entity Relationships with Link<T>
The Link<T> type allows flexible entity references:
// Reference by ID
let link = create_ref;
// Inline embedding
let link = inline;
// Access
match &pipeline.source
Singleton Entities
For configuration that should have exactly one instance:
🌐 Web API Generation (Axum)
Generate a complete REST API with OpenAPI documentation:
// Now in scope:
// - Trait implementations
// - All endpoints, response, request, and query types and ResponseEvent enum
// - `link_aliases` module
// - `impl AppState` with all state methods
async
Event Middleware for Persistence
The axum_api macro generates a ResponseEvent enum and event_middleware() method for integrating with databases:
use mpsc;
// Your event enum that wraps ResponseEvent
// Implement From<ResponseEvent> for ApiEvent
let = channel;
let app = new
.nest
.layer
.with_state;
// Background task to handle events
spawn;
The axum_api macro generates:
- ✅ Complete REST API handlers as methods on your struct
- ✅ OpenAPI 3.0 documentation (with
openapiparameter) - ✅ Type-safe request/response types
- ✅
router()method andApiState::openapi()for docs - ✅
ResponseEventenum andevent_middleware()for event-driven persistence
Macro Parameters
#[stately::state(openapi)]- Enables OpenAPI schema generation for entities#[stately::axum_api(State, openapi, components = [...])]- First parameter: The state type name
openapi: Enable OpenAPI documentation generationcomponents = [...]: Additional types to include in OpenAPI schemas (e.g., Link types)
Generated API Routes
The axum_api macro generates these endpoints:
PUT /- Create a new entityGET /- Get all entitiesGET /list- List all entities by summaryGET /list/{type}- List all entities filtered by type by summaryGET /{id}?type=<type>- Get entity by ID and typePOST /{id}- Update an existing entityPATCH /{id}- Patch an existing entityDELETE /{entry}/{id}- Delete an entity
OpenAPI Documentation
Access the generated OpenAPI spec:
use OpenApi;
let openapi = openapi;
let json = openapi.to_json.unwrap;
Feature Flags
| Feature | Description | Default |
|---|---|---|
openapi |
Enable OpenAPI schema generation via utoipa |
✅ Yes |
axum |
Enable Axum web framework integration | ❌ No |
Entity Attributes
The #[stately::entity] macro supports these attributes:
// Use a different field for the entity name
// Mark as singleton (only one instance)
// Use a field for description
// Use a static description
Examples
See the examples directory:
basic.rs- Core functionality demonstrationaxum_api.rs- Web API generation
Run examples:
API Reference
Core Types
Collection<T>- A collection of entities with CRUD operationsSingleton<T>- A single entity instanceLink<T>- Reference to another entity (by ID or inline)EntityId- UUID v7 identifier for entitiesSummary- Lightweight entity summary for listings
Traits
StateEntity- Trait for all entity types (implemented by#[stately::entity])StateCollection- Trait for entity collections (implemented by#[stately::state])
Macros
#[stately::entity]- Define an entity type#[stately::state]- Define application state with entity collections
Architecture
Stately uses procedural macros to generate boilerplate at compile time:
#[stately::entity]implements theStateEntitytrait#[stately::state]generates:StateEntryenum for entity type discriminationEntityenum for type-erased entity wrapper- Collection fields with type-safe accessors
- CRUD operation methods
link_aliasesmodule withLink<T>type aliases
#[stately::axum_api(State, ...)]generates (optional):- REST API handler methods on your struct
router()method for Axum integration- OpenAPI documentation (when
openapiparameter is used) ResponseEventenum for CRUD operationsevent_middleware()method for event streaming
All generated code is type-safe and benefits from Rust's compile-time guarantees.
Generated Code
link_aliases Module (from #[stately::state]):
ResponseEvent Enum (from #[stately::axum_api]):
These enable type-safe event-driven architectures for persistence, logging, and system integration.
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.