feat(examples): add integration examples for Actix, Axum, and Rocket#260
feat(examples): add integration examples for Actix, Axum, and Rocket#260Maki-Grz wants to merge 3 commits intoqdrant:devfrom
Conversation
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
timvisee
left a comment
There was a problem hiding this comment.
Thanks! It makes sense to include examples like this.
I do have a review remark, see below:
Cargo.toml
Outdated
| actix-web = "4.12.1" | ||
| axum = "0.8.7" | ||
| rocket = { version = "0.5.1", features = ["json"] } | ||
| serde_json = "1.0.128" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
It's an excellent idea. The examples will also be more concrete, complete, and will make greater use of features.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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-integrationscrate containing Actix-web, Axum, and Rocket server examples. - Updates the root
Cargo.tomlto introduce a workspace including the new examples crate (and addsserde_jsonto dev-dependencies). - Implements
/collectionsendpoints in each framework (plus a/healthendpoint 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.
| [workspace] | ||
| members = [ | ||
| ".", | ||
| "examples/web-integrations", | ||
| ] |
There was a problem hiding this comment.
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.
| [dev-dependencies] | ||
| tonic-build = { version = "0.12.3", features = ["prost"] } | ||
| serde_json = "1.0.128" | ||
|
|
||
| [workspace] | ||
| members = [ | ||
| ".", | ||
| "examples/web-integrations", | ||
| ] |
There was a problem hiding this comment.
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).
Cargo.toml
Outdated
|
|
||
| [dev-dependencies] | ||
| tonic-build = { version = "0.12.3", features = ["prost"] } | ||
| serde_json = "1.0.128" |
There was a problem hiding this comment.
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.
| serde_json = "1.0.128" |
| 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()})), | ||
| } |
There was a problem hiding this comment.
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.
| 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()})), | ||
| } |
There was a problem hiding this comment.
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.
| 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()), |
There was a problem hiding this comment.
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).
| 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" })), |
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.
This PR adds documentation examples demonstrating how to integrate the
Qdrantclient with the most popular Rust web frameworks: Actix-web, Axum, and Rocket.As gRPC response types (from
prost) do not implementserde::Serializeby default, these examples demonstrate the recommended pattern: sharing the client via application state and usingserde_json::json!as a bridge to return serializable JSON to web clients.Changes
Cargo.toml: Added frameworks to[dev-dependencies]and declared new[[example]]sections.examples/actix_web.rs: Demonstrates state sharing viaweb::Data.examples/axum.rs: Demonstrates state sharing viaextract::State.examples/rocket.rs: Demonstrates managed state viarocket::State.How to test
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrantrequired-features):Query the endpoint: curl http://localhost:3000/collections
All Submissions:
New Feature Submissions:
Changes to Core Features:
(N/A: This PR only adds documentation examples)