Complete HTTP API example with Axum server, demonstrating:
ServerBuilderfull_api/
└── main.rs # Complete API in a single file (~300 lines)
cargo run --example full_api
The server will start on http://localhost:3000.
ServerBuilderEntityFetcher and EntityCreatorimpl_data_entity!(User, "user", ["name", "email"], {
email: String,
});
impl_data_entity!(Car, "car", ["name", "brand", "model"], {
brand: String,
model: String,
year: i32,
});
impl_data_entity!(Company, "company", ["name", "registration_number"], {
registration_number: String,
});
When you run the server, you’ll see:
🚀 Full API Example - this-rs
🌐 Server running on http://127.0.0.1:3000
📚 Auto-generated Entity Routes:
GET /users - List all users
POST /users - Create a user
GET /users/{id} - Get a user
PUT /users/{id} - Update a user
DELETE /users/{id} - Delete a user
GET /cars - List all cars
POST /cars - Create a car
... (same for companies)
🔗 Auto-generated Link Routes:
GET /users/{id}/cars-owned - List cars owned by user
POST /users/{id}/cars-owned - Create new car + link
POST /users/{id}/cars-owned/{car_id} - Link existing car
GET /cars/{id}/owner - Get owner of car
GET /users/{id}/companies-owned - List companies owned by user
GET /companies/{id}/owner - Get owner of company
# Create a user
curl -X POST http://localhost:3000/users \
-H 'Content-Type: application/json' \
-d '{
"name": "Alice",
"email": "alice@example.com",
"status": "active"
}'
# Create a car
curl -X POST http://localhost:3000/cars \
-H 'Content-Type: application/json' \
-d '{
"name": "Tesla Model 3",
"brand": "Tesla",
"model": "Model 3",
"year": 2023,
"status": "active"
}'
curl -X POST http://localhost:3000/users/{user_id}/cars-owned/{car_id} \
-H 'Content-Type: application/json' \
-d '{
"metadata": {
"purchase_date": "2024-01-15",
"price": 45000
}
}'
curl -X POST http://localhost:3000/users/{user_id}/cars-owned \
-H 'Content-Type: application/json' \
-d '{
"entity": {
"name": "BMW X5",
"brand": "BMW",
"model": "X5",
"year": 2024,
"status": "active"
},
"metadata": {
"purchase_date": "2024-03-20",
"price": 65000
}
}'
# Get all cars owned by a user (with full car data!)
curl http://localhost:3000/users/{user_id}/cars-owned | jq .
# Get owner of a car (with full user data!)
curl http://localhost:3000/cars/{car_id}/owner | jq .
# Get specific link with both entities
curl http://localhost:3000/users/{user_id}/cars-owned/{car_id} | jq .
# Update a user
curl -X PUT http://localhost:3000/users/{user_id} \
-H 'Content-Type: application/json' \
-d '{
"name": "Alice Smith",
"email": "alice.smith@example.com"
}'
# Delete a link
curl -X DELETE http://localhost:3000/users/{user_id}/cars-owned/{car_id}
# Delete an entity
curl -X DELETE http://localhost:3000/cars/{car_id}
let app = ServerBuilder::new()
.with_link_service(InMemoryLinkService::new())
.register_module(app_module)?
.build()?;
This single call:
Zero manual routing code!
impl Module for AppModule {
fn name(&self) -> &str { "full-api" }
fn entity_types(&self) -> Vec<&str> {
vec!["user", "car", "company"]
}
fn links_config(&self) -> Result<LinksConfig> {
Ok(LinksConfig {
entities: vec![
EntityConfig { singular: "user".into(), plural: "users".into() },
EntityConfig { singular: "car".into(), plural: "cars".into() },
EntityConfig { singular: "company".into(), plural: "companies".into() },
],
links: vec![
LinkDefinition {
link_type: "owner".to_string(),
source_type: "user".to_string(),
target_type: "car".to_string(),
forward_route_name: "cars-owned".to_string(),
reverse_route_name: "owner".to_string(),
// ...
},
// More link definitions...
],
// ...
})
}
fn register_entities(&self, registry: &mut EntityRegistry) {
registry.register(Box::new(UserDescriptor::new(self.user_store.clone())));
registry.register(Box::new(CarDescriptor::new(self.car_store.clone())));
registry.register(Box::new(CompanyDescriptor::new(self.company_store.clone())));
}
fn get_entity_fetcher(&self, entity_type: &str) -> Option<Arc<dyn EntityFetcher>> {
match entity_type {
"user" => Some(Arc::new(self.user_store.clone())),
"car" => Some(Arc::new(self.car_store.clone())),
"company" => Some(Arc::new(self.company_store.clone())),
_ => None,
}
}
fn get_entity_creator(&self, entity_type: &str) -> Option<Arc<dyn EntityCreator>> {
match entity_type {
"user" => Some(Arc::new(self.user_store.clone())),
"car" => Some(Arc::new(self.car_store.clone())),
"company" => Some(Arc::new(self.company_store.clone())),
_ => None,
}
}
}
Request:
GET /users/abc-123/cars-owned
Response:
{
"links": [
{
"id": "link-uuid",
"source_id": "abc-123",
"target_id": "car-uuid",
"target": {
"id": "car-uuid",
"type": "car",
"name": "Tesla Model 3",
"brand": "Tesla",
"model": "Model 3",
"year": 2023,
"status": "active"
},
"metadata": {
"purchase_date": "2024-01-15",
"price": 45000
}
}
],
"count": 1
}
Request:
POST /users/abc-123/cars-owned
Body: {
"entity": { "name": "BMW X5", "brand": "BMW", "model": "X5", "year": 2024, "status": "active" },
"metadata": { "purchase_date": "2024-03-20" }
}
Response:
{
"entity": {
"id": "new-car-uuid",
"type": "car",
"name": "BMW X5",
"brand": "BMW",
"model": "X5",
"year": 2024,
"created_at": "2024-10-23T12:00:00Z",
"updated_at": "2024-10-23T12:00:00Z",
"status": "active"
},
"link": {
"id": "new-link-uuid",
"type": "link",
"link_type": "owner",
"source_id": "abc-123",
"target_id": "new-car-uuid",
"metadata": {
"purchase_date": "2024-03-20"
},
"created_at": "2024-10-23T12:00:00Z",
"updated_at": "2024-10-23T12:00:00Z"
}
}
✅ Auto-Generated Routes - Zero manual routing
✅ Entity Macros - Define entities in 4 lines
✅ Link Enrichment - Full entity data in responses
✅ Bidirectional Navigation - Query from either side
✅ EntityCreator - Create entities with automatic linking
✅ Module System - Clean organization
✅ Type Safety - Full Rust compile-time checks
A complete, production-ready API in ~300 lines of code! 🚀🦀✨