Skip to content

feat(examples): add integration examples for Actix, Axum, and Rocket#260

Open
Maki-Grz wants to merge 3 commits intoqdrant:devfrom
Maki-Grz:examples/web-frameworks
Open

feat(examples): add integration examples for Actix, Axum, and Rocket#260
Maki-Grz wants to merge 3 commits intoqdrant:devfrom
Maki-Grz:examples/web-frameworks

Conversation

@Maki-Grz
Copy link

This PR adds documentation examples demonstrating how to integrate the Qdrant client with the most popular Rust web frameworks: Actix-web, Axum, and Rocket.

As gRPC response types (from prost) do not implement serde::Serialize by default, these examples demonstrate the recommended pattern: sharing the client via application state and using serde_json::json! as a bridge to return serializable JSON to web clients.

Changes

  • Updated Cargo.toml: Added frameworks to [dev-dependencies] and declared new [[example]] sections.
  • examples/actix_web.rs: Demonstrates state sharing via web::Data.
  • examples/axum.rs: Demonstrates state sharing via extract::State.
  • examples/rocket.rs: Demonstrates managed state via rocket::State.

How to test

  1. Run a Qdrant instance: docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
  2. Run any example (features are handled automatically via required-features):
    cargo run --example axum

Query the endpoint: curl http://localhost:3000/collections

All Submissions:

  • Contributions should target the dev branch.
  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests?

New Feature Submissions:

  • Does your submission pass tests? (Verified via cargo check --examples)
  • Have you formatted your code locally using cargo +nightly fmt --all?
  • Have you checked your code using cargo clippy --all --all-features?

Changes to Core Features:

  • Have you added an explanation of what your changes do and why?
  • Have you written new tests for your core changes?
  • Have you successfully ran tests with your changes locally?
    (N/A: This PR only adds documentation examples)

Add three new examples demonstrating how to use the Qdrant client within
popular Rust web frameworks. Each example shows how to manage shared
state and handle gRPC-to-JSON serialization.

- Added actix-web, axum, and rocket to dev-dependencies
- Declared examples with 'serde' required-feature in Cargo.toml
- Implemented state sharing and basic endpoints in each framework
Copy link
Member

@timvisee timvisee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! It makes sense to include examples like this.

I do have a review remark, see below:

Cargo.toml Outdated
Comment on lines 34 to 37
actix-web = "4.12.1"
axum = "0.8.7"
rocket = { version = "0.5.1", features = ["json"] }
serde_json = "1.0.128"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also feature gate these examples so that we don't have to pull in these dependencies by default.

Alternatively we can add a new crate dedicated for this example, so that it's isolated from the main project. That may actually be better.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's an excellent idea. The examples will also be more concrete, complete, and will make greater use of features.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I've refactored the examples into a dedicated workspace member examples/web-integrations to avoid polluting the root dev-dependencies.

Move web framework examples into a dedicated workspace member to
streamline management and testing. This change also removes the
individual example definitions from the root Cargo.toml.
Copilot AI review requested due to automatic review settings February 7, 2026 10:23
@Maki-Grz Maki-Grz requested a review from timvisee February 7, 2026 10:23
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Rust web-framework integration examples showing how to share a Qdrant client via application state and return JSON using serde_json::json! as a bridge for non-serde::Serialize gRPC types.

Changes:

  • Adds a new examples/web-integrations crate containing Actix-web, Axum, and Rocket server examples.
  • Updates the root Cargo.toml to introduce a workspace including the new examples crate (and adds serde_json to dev-dependencies).
  • Implements /collections endpoints in each framework (plus a /health endpoint in the Actix example).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
Cargo.toml Adds serde_json dev-dependency and introduces a workspace that includes the new examples crate.
examples/web-integrations/Cargo.toml New crate manifest defining dependencies and three bin targets for the examples.
examples/web-integrations/src/actix_web.rs Actix-web example using web::Data state and JSON responses.
examples/web-integrations/src/axum.rs Axum example using extract::State and JSON responses.
examples/web-integrations/src/rocket.rs Rocket example using managed State and JSON responses.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 36 to 40
[workspace]
members = [
".",
"examples/web-integrations",
]
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a workspace with examples/web-integrations as a member will cause existing CI commands like cargo clippy --workspace --all-targets --all-features and cargo test --all (see .github/workflows/lint.yml and tests/integration-tests.sh) to compile these new Actix/Axum/Rocket binaries. That can significantly increase CI time and can introduce new system dependency requirements. Consider keeping these examples out of the workspace (or adjusting CI commands / moving them under a non-workspace directory) so they don’t become part of the default lint/test surface.

Copilot uses AI. Check for mistakes.
Comment on lines 32 to 40
[dev-dependencies]
tonic-build = { version = "0.12.3", features = ["prost"] }
serde_json = "1.0.128"

[workspace]
members = [
".",
"examples/web-integrations",
]
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description mentions adding frameworks to root [dev-dependencies] and adding new [[example]] sections with required-features, but the implementation instead adds a new workspace member crate with [[bin]] targets. Please align the description with the actual approach, or refactor to match the described [[example]] + required-features pattern (whichever is intended).

Copilot uses AI. Check for mistakes.
Cargo.toml Outdated

[dev-dependencies]
tonic-build = { version = "0.12.3", features = ["prost"] }
serde_json = "1.0.128"
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

serde_json is already an optional normal dependency (enabled by the serde feature). Adding it again as an unconditional root dev-dependency is redundant and makes it harder to reason about feature-gating (e.g., dev builds will have serde_json even if the serde feature is off). Consider removing the dev-dependency unless there’s a specific need for serde_json when serde is disabled.

Suggested change
serde_json = "1.0.128"

Copilot uses AI. Check for mistakes.
Comment on lines 11 to 21
match client.list_collections().await {
Ok(collections) => {
let names: Vec<String> = collections
.collections
.into_iter()
.map(|c| c.name)
.collect();
Json(json!({ "collections": names }))
}
Err(e) => Json(json!({"error": e.to_string()})),
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the error branch this returns Json(...) without setting an HTTP error status, so failures will look like 200 OK to callers. Consider returning a (StatusCode, Json<Value>) (or an axum::response::IntoResponse) so errors map to 5xx/4xx status codes appropriately.

Copilot uses AI. Check for mistakes.
Comment on lines 9 to 20
async fn list_collections(client: &State<Qdrant>) -> Json<Value> {
match client.list_collections().await {
Ok(collections) => {
let names: Vec<String> = collections
.collections
.into_iter()
.map(|c| c.name)
.collect();
Json(json!({ "collections": names }))
}
Err(e) => Json(json!({"error": e.to_string()})),
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error branch returns JSON but does not set a non-2xx status code, so Qdrant failures will be reported as 200 OK. Consider returning a status::Custom<Json<Value>> (or similar) with an appropriate Rocket Status for error cases.

Copilot uses AI. Check for mistakes.
Comment on lines 21 to 28
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}

async fn health_check(data: web::Data<AppState>) -> impl Responder {
match data.client.health_check().await {
Ok(resp) => HttpResponse::Ok().json(json!({ "result": format!("{:?}", resp) })),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The success path returns JSON, but the error path returns a plain-text body. Since these examples are meant to demonstrate a JSON bridge for non-Serialize types, consider returning a JSON error payload here as well (and optionally include an error code/status consistently).

Suggested change
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
}
}
async fn health_check(data: web::Data<AppState>) -> impl Responder {
match data.client.health_check().await {
Ok(resp) => HttpResponse::Ok().json(json!({ "result": format!("{:?}", resp) })),
Err(e) => HttpResponse::InternalServerError().body(e.to_string()),
Err(e) => HttpResponse::InternalServerError()
.json(json!({ "error": e.to_string(), "code": "internal_server_error" })),
}
}
async fn health_check(data: web::Data<AppState>) -> impl Responder {
match data.client.health_check().await {
Ok(resp) => HttpResponse::Ok().json(json!({ "result": format!("{:?}", resp) })),
Err(e) => HttpResponse::InternalServerError()
.json(json!({ "error": e.to_string(), "code": "internal_server_error" })),

Copilot uses AI. Check for mistakes.
This commit updates the web examples to consistently return JSON
formatted error responses instead of plain text. This provides a more
consistent and machine-readable error handling mechanism for API
consumers.

Additionally, the `serde_json` dependency has been removed from the main
workspace as it's no longer directly used there.
@ffuugoo ffuugoo removed their request for review February 10, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants