Skip to content

Conversation

@jmg-duarte
Copy link
Contributor

@jmg-duarte jmg-duarte commented Jan 22, 2026

Description

This PR migrates the orderbook API from Warp to Axum. The migration modernizes our web framework while maintaining API
compatibility, with improved code organization and reduced complexity (~200 lines removed).

Changes

  • Replace Warp filters with Axum Router and centralized AppState for dependency injection
  • Migrate all 18 API endpoints (v1 and v2) to Axum handler functions
  • Update dependencies: remove warp, add axum, tower, tower-http
  • Reorganize routes hierarchically with .nest(), alphabetically by prefix
  • Fix route conflicts using .merge() for multiple HTTP methods on same path
  • Fix bug in get_trades_v2 (use database_read instead of database_write)
  • Add comprehensive E2E test suite for HTTP behavior validation in malformed_requests.rs

Breaking Changes:

  • JSON deserialization errors now return 422 instead of 400 (Axum convention)
  • Deserialization error responses return plain text instead of JSON

How to test

Existing tests + staging

I deployed these changes to base staging to ensure metrics were working, here's the graph

Screenshot 2026-01-23 at 12-42-46 Edit panel - GPv2 - Dashboards - Amazon Managed Grafana

Follow up:
image

Unmarked spots are running main, the wrong image was labeling endpoints with unknown because it was a fallback.
That code has been removed and replaced with the current metric labeling scheme.

That scheme can be improved (read, made less verbose and error prone while being more general) but we need to change the metrics label, which I didn't do to minimize changes.

jmg-duarte and others added 8 commits January 22, 2026 16:12
This commit migrates the orderbook HTTP API from the warp web framework
to axum. The migration includes:

- Replace warp filters with axum Router and handlers
- Update HTTP status codes to match axum conventions (422 for JSON
  deserialization errors, proper 400/404 distinction)
- Restructure API with centralized AppState shared across handlers
- Implement axum-compatible middleware for metrics and tracing
- Remove warp dependency and related tracing utilities
- Update e2e tests to reflect new HTTP behavior

Breaking changes:
- Invalid JSON payloads now return 422 (Unprocessable Entity) instead
  of 400
- Path parameter validation now returns 400 for malformed values
- Error response format remains compatible

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Fixed critical issues identified in PR review:
- Fixed route conflicts by using .merge() for multiple HTTP methods on same paths
- Changed get_trades_v2 to use database_read instead of database_write
- Reorganized routes alphabetically by prefix using .nest() for better maintainability
- Made METRIC_LABELS a module-level const for reusability
- Removed convert_json_response helper (too simple to warrant abstraction)
- Optimized Arc wrapping by using trait references directly
- Applied per-method middleware before merging to ensure correct metric labels
- Simplified test code by removing unnecessary verbose match expressions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jmg-duarte jmg-duarte marked this pull request as ready for review January 23, 2026 15:08
@jmg-duarte jmg-duarte requested a review from a team as a code owner January 23, 2026 15:08
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request migrates the orderbook API from Warp to Axum, modernizing the web framework and improving code organization. The changes include replacing Warp filters with Axum Router, migrating API endpoints, updating dependencies, and adding an E2E test suite. The review focuses on identifying critical and high severity issues, with actionable suggestions for improvement, while adhering to the repository's stricter code review style guide. One comment was modified to explicitly reference a relevant repository rule.

Comment on lines +231 to +237
.route(
"/:auction_id",
axum::routing::get(get_solver_competition::get_solver_competition_by_id_handler)
.layer(middleware::from_fn(with_labelled_metric("v1/solver_competition"))),
),
)
// /token/* routes
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The error function constructs a Json response but uses string literals for error_type and description. It would be more robust and maintainable to use String to avoid lifetime issues and allow for dynamic error messages.

Suggested change
.route(
"/:auction_id",
axum::routing::get(get_solver_competition::get_solver_competition_by_id_handler)
.layer(middleware::from_fn(with_labelled_metric("v1/solver_competition"))),
),
)
// /token/* routes
pub fn error(error_type: &str, description: impl AsRef<str>) -> Json<Error> {
Json(Error { // Construct Json<Error> directly
error_type: error_type.to_string(), // Convert to String
description: description.as_ref().to_string(), // Convert to String
data: None,
})
}
References
  1. Comments must include a clear, actionable suggestion for improvement. (link)

Comment on lines +393 to 398
pub fn error(error_type: &str, description: impl AsRef<str>) -> Json<Error> {
Json(Error {
error_type: error_type.to_string(),
description: description.as_ref().to_string(),
data: None,
})
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The error function constructs a Json response but uses string literals for error_type and description. It would be more robust and maintainable to use String to avoid lifetime issues and allow for dynamic error messages.

Suggested change
pub fn error(error_type: &str, description: impl AsRef<str>) -> Json<Error> {
Json(Error {
error_type: error_type.to_string(),
description: description.as_ref().to_string(),
data: None,
})
pub fn error(error_type: &str, description: impl AsRef<str>) -> Json<Error> {
Json(Error { // Construct Json<Error> directly
error_type: error_type.to_string(), // Convert to String
description: description.as_ref().to_string(), // Convert to String
data: None,
})
}
References
  1. Comments must include a clear, actionable suggestion for improvement. (link)

Comment on lines +75 to +78
.database_read
.trades_paginated(&trade_filter)
.await
.context("get_trades_v2");
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The code uses database_read to fetch trades, but the description mentions a bug fix in get_trades_v2 to use database_read instead of database_write. Verify that this handler indeed uses database_read to align with the description and prevent accidental writes.

Comment on lines 25 to 29
match status {
StatusCode::BAD_REQUEST => match error.error_type {
StatusCode::BAD_REQUEST => match error.error_type.as_str() {
"InvalidSignature" => Some(OrderReplacementError::InvalidSignature),
"OldOrderActivelyBidOn" => Some(OrderReplacementError::OldOrderActivelyBidOn),
_ => None,
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The match statement is using error.error_type directly, which is a String. It should be matching against error.error_type.as_str() to compare string slices, which is more efficient and avoids unnecessary allocations.

Suggested change
match status {
StatusCode::BAD_REQUEST => match error.error_type {
StatusCode::BAD_REQUEST => match error.error_type.as_str() {
"InvalidSignature" => Some(OrderReplacementError::InvalidSignature),
"OldOrderActivelyBidOn" => Some(OrderReplacementError::OldOrderActivelyBidOn),
_ => None,
match status {
StatusCode::BAD_REQUEST => match error.error_type.as_str() {
References
  1. Comments must include a clear, actionable suggestion for improvement. (link)

Comment on lines +49 to 52
StatusCode::BAD_REQUEST => match error.error_type.as_str() {
"InvalidSignature" => Some(OrderCancellationError::InvalidSignature),
"AlreadyCancelled" => Some(OrderCancellationError::AlreadyCancelled),
"OrderFullyExecuted" => Some(OrderCancellationError::OrderFullyExecuted),
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

The match statement is using error.error_type directly, which is a String. It should be matching against error.error_type.as_str() to compare string slices, which is more efficient and avoids unnecessary allocations.

match status {
    StatusCode::BAD_REQUEST => match error.error_type.as_str() {
References
  1. Comments must include a clear, actionable suggestion for improvement. (link)

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