Poem Web Framework

repository·master·Indexed 26 days ago

https://github.com/poem-web/poem

A full-featured and easy-to-use web framework for the Rust programming language. Poem provides a modular ecosystem including the core framework, OpenAPI support via poem-openapi, gRPC support via poem-grpc and poem-grpc-build, AWS Lambda integration via poem-lambda, and Model Context Protocol (MCP) server implementation via poem-mcpserver.

Tokens
23.6K
Snippets
42
Records
144
Agent score
89%

What's inside poem

  1. Overview of Poem Framework components

    master

    Poem is a full-featured and easy-to-use web framework for the Rust programming language. The ecosystem consists of several specialized crates depending on your deployment and protocol needs:

    • poem: The core Poem Web framework.
    • poem-lambda: Support for running Poem on AWS Lambda.
    • poem-openapi: OpenAPI specification support for Poem Web.
    • poem-grpc: gRPC support for Poem.
    • poem-mcpserver: Model Context Protocol (MCP) Server implementation for Poem.
  2. Define OpenAPI Tags using the `Tags` macro

    master

    Use the #[derive(Tags)] macro on an enum to define OpenAPI tags for your API. Each variant in the enum represents a tag. You can use doc comments on the enum variants to provide descriptions for the tags in the generated OpenAPI specification.

    use poem_openapi::Tags;
    
    #[derive(Tags)]
    enum ApiTags {
        /// Operations about user
        User,
        /// Operations about pet
        Pet,
    }
  3. Implement a custom `bad_request_handler`

    master

    You can provide a custom handler to the ApiResponse macro to intercept errors and convert them into a specific response variant. This is useful for mapping internal poem::Error types to your API's error responses.

    1. Define the handler function: fn handler(err: Error) -> YourResponseEnum.
    2. Reference the function name in the macro: #[oai(bad_request_handler = "handler_name")].
    use poem::Error;
    use poem_openapi::{payload::PlainText, ApiResponse};
    
    #[derive(ApiResponse)]
    #[oai(bad_request_handler = "bad_request_handler")]
    enum CreateUserResponse {
        #[oai(status = 200)]
        Ok,
        #[oai(status = 409)]
        UserAlreadyExists,
        #[oai(status = 400)]
        BadRequest(PlainText<String>),
    }
    
    fn bad_request_handler(err: Error) -> CreateUserResponse {
        CreateUserResponse::BadRequest(PlainText(format!("error: {}", err.to_string())))
    }
  4. Define OAuth scopes using the OAuthScopes macro

    master

    Use the #[derive(OAuthScopes)] macro on an enum to define a set of OAuth scopes for your API. This allows the OpenAPI specification to correctly represent the available scopes for OAuth2 authentication.

    use poem_openapi::OAuthScopes;
    
    #[derive(OAuthScopes)]
    enum GithubScopes {
        /// Read data
        Read,
        /// Write data
        Write,
    }
  5. Define OpenAPI response content using ResponseContent

    master

    To define multiple possible content types for an OpenAPI response, implement the ResponseContent derive macro on an enum. Each variant of the enum represents a different content type (e.g., Json, PlainText, Binary).

    use poem_openapi::{
        payload::{Binary, Json, PlainText},
        ApiResponse, ResponseContent,
    };
    
    #[derive(ResponseContent)]
    enum MyResponseContent {
        A(Json<i32>),
        B(PlainText<String>),
        C(Binary<Vec<u8>>),
    }
    
    #[derive(ApiResponse)]
    enum MyResponse {
        #[oai(status = 200)]
        Ok(MyResponseContent),
    }
  6. Define OpenAPI webhooks using the #[Webhook] macro

    master

    You can define OpenAPI webhooks by implementing a trait decorated with the #[Webhook] macro. The OpenApiService can then include these webhooks using the .webhooks::<T>() method, where T is a trait object of your webhook definition.

    Webhooks are defined via Rust documentation comments for summary and description, and via the #[oai(...)] attribute for operation-specific metadata.

    use poem_openapi::{Object, Webhook, payload::Json, OpenApiService};
    
    #[derive(Object)]
    struct Pet {
        id: i64,
        name: String,
    }
    
    #[Webhook]
    trait MyWebhooks {
        /// This is the summary of the operation
        ///
        /// This is the description of the operation
        #[oai(method = "post")]
        fn new_pet(&self, pet: Json<Pet>);
    }
    
    let api = OpenApiService::new((), "Demo", "1.0.0")
        .webhooks::<&dyn MyWebhooks>();
  7. Define an OpenAPI discriminator for Unions

    master

    When using the #[derive(Union)] macro, you can define an OpenAPI discriminator to help clients identify which variant of a union is being sent in a payload. This is done using the #[oai(discriminator_name = "...")] attribute on the enum.

    To use a discriminator, specify the property name that will hold the type identifier using discriminator_name.

    use poem_openapi::{Object, Union};
    
    #[derive(Object, Debug, PartialEq)]
    struct A {
        v1: i32,
        v2: String,
    }
    
    #[derive(Object, Debug, PartialEq)]
    struct B {
        v3: f32,
    }
    
    #[derive(Union, Debug, PartialEq)]
    #[oai(discriminator_name = "type")]
    enum MyObj {
        A(A),
        B(B),
    }
  8. Define a new type using the `NewType` macro

    master

    Use the #[derive(NewType)] macro to wrap an existing type into a new type that automatically implements the necessary OpenAPI traits for JSON serialization, parameter parsing, and header conversion. By default, it implements ParseFromJSON, ParseFromParameter, ParseFromMultipartField, ToJSON, and ToHeader.

    use poem_openapi::NewType;
    
    #[derive(NewType)]
    struct MyString(String);
  9. Implement optional authentication with fallback variants

    master

    To create endpoints that support both authenticated and anonymous requests (e.g., a personalized GET /me), use an enum with a variant marked #[oai(fallback)].

    If the security extractor fails, the fallback variant is used. If you require a 401 Unauthorized response for invalid credentials, use a standard SecurityScheme instead of a fallback variant.

    use poem::Request;
    use poem_openapi::{OpenApi, SecurityScheme};
    use poem_openapi::auth::ApiKey;
    use poem_openapi::payload::PlainText;
    
    struct User {
        username: String,
    }
    
    #[derive(SecurityScheme)]
    #[oai(
        ty = "api_key",
        key_name = "session",
        key_in = "cookie",
        checker = "session_checker"
    )]
    struct SessionAuthorization(User);
    
    async fn session_checker(_req: &Request, api_key: ApiKey) -> Option<User> {
        match api_key.key.as_str() {
            "demo-token" => Some(User {
                username: "demo".to_string(),
            }),
            _ => None,
        }
    }
    
    #[derive(SecurityScheme)]
    enum OptionalSessionAuthorization {
        Session(SessionAuthorization),
        #[oai(fallback)]
        Anonymous,
    }
    
    struct MyApi;
    
    #[OpenApi]
    impl MyApi {
        #[oai(path = "/hello", method = "get")]
        async fn hello(&self, auth: OptionalSessionAuthorization) -> PlainText<String> {
            match auth {
                OptionalSessionAuthorization::Session(auth) => {
                    PlainText(format!("hello, {}", auth.0.username))
                }
                OptionalSessionAuthorization::Anonymous => {
                    PlainText("hello, anonymous".to_string())
                }
            }
        }
    }
  10. Define an OpenAPI enum using the Enum macro

    master

    Use the #[derive(Enum)] macro from poem_openapi to define an enum that will be represented in your OpenAPI schema. You can customize the enum's name, its casing convention, and its deprecation status using macro attributes.

    use poem_openapi::Enum;
    
    #[derive(Enum)]
    enum PetStatus {
        Available,
        Pending,
        Sold,
    }
  11. Define OpenAPI responses with `#[derive(ApiResponse)]`

    master

    Use the #[derive(ApiResponse)] macro on an enum to define the possible OpenAPI responses for an API endpoint. You can specify HTTP status codes, content types, and headers using the #[oai(...)] attribute on individual enum variants.

    use poem_openapi::{payload::PlainText, ApiResponse};
    
    #[derive(ApiResponse)]
    enum CreateUserResponse {
        #[oai(status = 200)]
        Ok(#[oai(header = "X-Id")] String),
        #[oai(status = 201)]
        OkWithBody(PlainText<String>, #[oai(header = "X-Id")] String),
    }