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.
|
Hey, do we have any updates on the PR ? And I apologize for the Copilot participation. I did disable the option in my settings to stop receiving all the messages. I'm available to edit my PR or for any feedback. |
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)