Skip to main content

Services

Understanding Actix services and modularity.

Actix 4.9+ 4 min readAdvanced Topic

# Services

In Actix Web, almost everything that handles requests is a "Service". A Service is a core abstraction (originating from the actix-service crate) that represents an asynchronous operation taking a request and returning a response.

##Service Abstraction

At its most fundamental level, a Service implements a trait that looks roughly like this conceptually:

code_blocksrust
pub trait Service<Req> {
    type Response;
    type Error;
    type Future: Future<Output = Result<Self::Response, Self::Error>>;

    fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll<Result<(), Self::Error>>;
    fn call(&self, req: Req) -> Self::Future;
}

Every route, scope, and middleware in Actix Web is built on this abstraction. When you define an async handler function, Actix Web automatically wraps it in a struct that implements the Service trait.

##Composing Services

Because everything is a Service, they can be composed. The App itself is a Service that routes requests to smaller inner Services (your routes).

You can use web::scope() to group Services under a common path prefix:

code_blocksrust
use actix_web::{web, App, HttpResponse, Responder};

async fn show_users() -> impl Responder {
    HttpResponse::Ok().body("Users")
}

async fn show_posts() -> impl Responder {
    HttpResponse::Ok().body("Posts")
}

// In your App setup:
App::new()
    .service(
        web::scope("/api")
            .route("/users", web::get().to(show_users))
            .route("/posts", web::get().to(show_posts))
    )

In this example, /api/users and /api/posts are available. The web::scope("/api") acts as a Service that strips the /api prefix and forwards the modified request to the matching inner Service.

Understanding the Service abstraction helps when you start writing custom middleware, because middleware is essentially a Service that wraps another Service.

Was this page helpful?

Reference Implementation

The following code snippet demonstrates the authoritative pattern for this documentation.

src/main.rs
1use actix_web::{web, App, HttpServer, Responder};
2
3async fn version() -> impl Responder {
4 "v1.0.0"
5}
6
7#[actix_web::main]
8async fn main() -> std::io::Result<()> {
9 HttpServer::new(|| {
10 App::new().service(
11 web::scope("/admin")
12 .route("/version", web::get().to(version))
13 )
14 })
15 .bind("127.0.0.1:8080")?
16 .run()
17 .await
18}
Was this page helpful?