#[http]Expand description
Generate HTTP handlers from an impl block.
§Basic Usage
ⓘ
use server_less::http;
#[http]
impl UserService {
async fn create_user(&self, name: String) -> User { /* ... */ }
}§With URL Prefix
ⓘ
#[http(prefix = "/api/v1")]
impl UserService {
// POST /api/v1/users
async fn create_user(&self, name: String) -> User { /* ... */ }
}§Per-Method Route Overrides
ⓘ
#[http]
impl UserService {
// Override HTTP method: GET /data becomes POST /data
#[route(method = "POST")]
async fn get_data(&self, payload: String) -> String { /* ... */ }
// Override path: POST /users becomes POST /custom-endpoint
#[route(path = "/custom-endpoint")]
async fn create_user(&self, name: String) -> User { /* ... */ }
// Override both
#[route(method = "PUT", path = "/special/{id}")]
async fn do_something(&self, id: String) -> String { /* ... */ }
// Skip route generation (internal methods)
#[route(skip)]
fn internal_helper(&self) -> String { /* ... */ }
// Hide from OpenAPI but still generate route
#[route(hidden)]
fn secret_endpoint(&self) -> String { /* ... */ }
}§Parameter Handling
ⓘ
#[http]
impl BlogService {
// Path parameters (id, post_id, etc. go in URL)
async fn get_post(&self, post_id: u32) -> Post { /* ... */ }
// GET /posts/{post_id}
// Query parameters (GET methods use query string)
async fn search_posts(&self, query: String, tag: Option<String>) -> Vec<Post> {
/* ... */
}
// GET /posts?query=rust&tag=tutorial
// Body parameters (POST/PUT/PATCH use JSON body)
async fn create_post(&self, title: String, content: String) -> Post {
/* ... */
}
// POST /posts with body: {"title": "...", "content": "..."}
}§Error Handling
ⓘ
#[http]
impl UserService {
// Return Result for error handling
async fn get_user(&self, id: u32) -> Result<User, MyError> {
if id == 0 {
return Err(MyError::InvalidId);
}
Ok(User { id, name: "Alice".into() })
}
// Return Option - None becomes 404
async fn find_user(&self, email: String) -> Option<User> {
// Returns 200 with user or 404 if None
None
}
}§Server-Sent Events (SSE) Streaming
Return impl Stream<Item = T> to enable Server-Sent Events streaming.
Important for Rust 2024: You must add + use<> to impl Trait return types
to explicitly capture all generic parameters in scope. This is required by the
Rust 2024 edition’s stricter lifetime capture rules.
ⓘ
use futures::stream::{self, Stream};
#[http]
impl DataService {
// Simple stream - emits values immediately
// Note the `+ use<>` syntax for Rust 2024
fn stream_numbers(&self, count: u32) -> impl Stream<Item = u32> + use<> {
stream::iter(0..count)
}
// Async stream with delays
async fn stream_events(&self, n: u32) -> impl Stream<Item = Event> + use<> {
stream::unfold(0, move |count| async move {
if count >= n {
return None;
}
tokio::time::sleep(Duration::from_secs(1)).await;
Some((Event { id: count }, count + 1))
})
}
}Clients receive data as SSE:
data: {"id": 0}
data: {"id": 1}
data: {"id": 2}Why + use<>?
- Rust 2024 requires explicit capture of generic parameters in return position impl Trait
+ use<>captures all type parameters and lifetimes from the function context- Without it, you’ll get compilation errors about uncaptured parameters
- See: examples/streaming_service.rs for a complete working example
§Real-World Example
ⓘ
#[http(prefix = "/api/v1")]
impl UserService {
// GET /api/v1/users?page=0&limit=10
async fn list_users(
&self,
#[param(default = 0)] page: u32,
#[param(default = 20)] limit: u32,
) -> Vec<User> {
/* ... */
}
// GET /api/v1/users/{user_id}
async fn get_user(&self, user_id: u32) -> Result<User, ApiError> {
/* ... */
}
// POST /api/v1/users with body: {"name": "...", "email": "..."}
#[response(status = 201)]
#[response(header = "Location", value = "/api/v1/users/{id}")]
async fn create_user(&self, name: String, email: String) -> Result<User, ApiError> {
/* ... */
}
// PUT /api/v1/users/{user_id}
async fn update_user(
&self,
user_id: u32,
name: Option<String>,
email: Option<String>,
) -> Result<User, ApiError> {
/* ... */
}
// DELETE /api/v1/users/{user_id}
#[response(status = 204)]
async fn delete_user(&self, user_id: u32) -> Result<(), ApiError> {
/* ... */
}
}§Generated Methods
http_router() -> axum::Router- Complete router with all endpointshttp_routes() -> Vec<&'static str>- List of route pathsopenapi_spec() -> serde_json::Value- OpenAPI 3.0 specification (unlessopenapi = false)
§OpenAPI Control
By default, #[http] generates both HTTP routes and OpenAPI specs. You can disable
OpenAPI generation:
ⓘ
#[http(openapi = false)] // No openapi_spec() method generated
impl MyService { /* ... */ }For standalone OpenAPI generation without HTTP routing, see #[openapi].