Skip to content

feat(minibf): add optional base_path configuration#872

Open
adrian1-dot wants to merge 1 commit into
txpipe:mainfrom
adrian1-dot:feat/add-api-v0-base-path
Open

feat(minibf): add optional base_path configuration#872
adrian1-dot wants to merge 1 commit into
txpipe:mainfrom
adrian1-dot:feat/add-api-v0-base-path

Conversation

@adrian1-dot

@adrian1-dot adrian1-dot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Add optional base_path configuration for Blockfrost API spec compliance

Problem

The Blockfrost OpenAPI specification defines all API endpoints under the /api/v0 base path (see servers section in the official spec). However, Dolos minibf currently serves endpoints directly at the root level (e.g., /network, /blocks/latest), which breaks compatibility with tools and libraries that expect canonical Blockfrost endpoint paths.

This incompatibility causes integration issues with:

  • blockfrost-haskell: Client library with hardcoded /api/v0 base path
  • Other Blockfrost-compatible tooling may also expect standard endpoint structure

Solution

Add an optional base_path configuration field to MinibfConfig that allows nesting all routes under a custom prefix.

Configuration example:

[serve.minibf]
listen_address = "[::]:3000"
base_path = "/api/v0"  # Optional - omit for root-level routes

When configured:

  • All routes become accessible at {base_path}/{endpoint} (e.g., /api/v0/network)
  • Original root-level routes are no longer accessible

Changes

  1. crates/core/src/config.rs: Added base_path: Option<String> field to MinibfConfig
  2. crates/minibf/src/lib.rs: Modified build_router() to conditionally nest all routes when base_path is set (~7 lines)
  3. crates/minibf/README.md: Added documentation for the new configuration option
  4. docs/content/apis/minibf.mdx: Updated API documentation
  5. src/bin/dolos/init.rs: Added base_path to default configuration initialization

Implementation Details

The implementation uses Axum's Router::nest() to wrap the entire route tree under the custom base path when configured:

if let Some(base_path) = &cfg.base_path {
    Router::new().nest(base_path, app).layer(NormalizePathLayer::trim_trailing_slash())
} else {
    app.layer(NormalizePathLayer::trim_trailing_slash())
}

This approach minimizes code changes while providing complete Blockfrost spec compliance.

Backwards Compatibility

  • Fully backwards compatible - when base_path is not configured, all routes remain at root level exactly as before.

Testing

  • All 110 existing tests pass (cargo test --workspace --all-features)
  • No clippy warnings (cargo clippy --workspace --all-features)
  • Successfully tested with local atlas server using /api/v0 base path
  • Verified endpoints accessible at /api/v0/* with base_path configured
  • Verified endpoints at root level when base_path is omitted

Impact

This change enables Dolos minibf to serve as a drop-in replacement for Blockfrost API in tools requiring canonical endpoint paths, significantly improving ecosystem compatibility while maintaining flexibility for users who prefer root-level routes.

Summary by CodeRabbit

  • New Features

    • Added a configurable base_path option to prefix all minibf HTTP endpoints (e.g., serve under /api/v0).
  • Documentation

    • Updated docs and README with examples showing base_path usage and backward-compatibility when omitted.
  • Bug Fixes / Improvements

    • Base path values are validated and produce clear configuration errors if invalid.
    • Improved runtime diagnostics when router construction fails.

Review Change Stack

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds optional base_path configuration to minibf, allowing endpoints to nest under a configurable prefix like /api/v0 for Blockfrost OpenAPI compliance. Configuration is validated during router construction, which now returns a fallible Result with descriptive configuration errors.

Changes

Base path prefix configuration and routing

Layer / File(s) Summary
Configuration contract and error types
crates/core/src/config.rs, crates/core/src/lib.rs
MinibfConfig adds an optional base_path: Option<String> field (deserialized with serde), and ServeError gains a ConfigError(String) variant for configuration validation failures.
Router building with base_path validation and nesting
crates/minibf/src/lib.rs
build_router and build_router_with_facade now return Result<Router, ServeError>. When base_path is present, validation checks that it starts with /, is not exactly / or empty, and contains no wildcards; valid paths are nested via Router::nest() with NormalizePathLayer::trim_trailing_slash() applied. When absent, trim_trailing_slash() is applied directly to the original router.
Error handling at call sites
crates/minibf/src/test_support.rs, src/bin/dolos/minibf.rs
Test support unwraps router construction with .expect(), and the CLI binary wraps the fallible call with diagnostic context ("building minibf router") for user-facing error messages.
User documentation and examples
crates/minibf/README.md, docs/content/apis/minibf.mdx
README and API documentation explain the base_path option, describe how it prefixes all routes (including /health and /metrics), document fallback behavior when omitted, and include a TOML example with base_path = "/api/v0".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • scarmuega

A rabbit hops with glee,
Base paths now dance wild and free,
Blockfrost compliance springs,
/api/v0 adorns all things! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding an optional base_path configuration to minibf. It is specific, directly related to the changeset, and reflects the primary objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@crates/core/src/config.rs`:
- Around line 572-575: Update the doc comment for the `base_path:
Option<String>` field in crates/core/src/config.rs to reflect the actual
behavior in `build_router()` (lib.rs): when `base_path` is set, all
routes—including `/health` and `/metrics`—are nested under that base path;
remove or correct the line that says "/health and /metrics remain at root" so
the comment matches the implementation (or, if you prefer the other approach,
modify `build_router()` to exclude `/health` and `/metrics` from nesting under
`base_path`).

Comment thread crates/core/src/config.rs
Comment thread crates/minibf/src/lib.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@crates/minibf/src/lib.rs`:
- Around line 442-446: The config validation for cfg.base_path is too narrow and
must also reject values missing a leading '/' (and optionally containing
wildcards) to avoid Axum's opaque panic when calling
Router::new().nest(base_path, app); update the check around cfg.base_path in
lib.rs to: ensure base_path is not empty, starts with '/', is not just "/" (as
already done), and does not contain '*' (or other invalid characters you choose
to forbid); if validation fails, panic or return a clear error message
referencing cfg.base_path so callers see a helpful configuration error before
reaching Router::nest (which is where NormalizePathLayer::trim_trailing_slash()
is applied).

Comment thread crates/minibf/src/lib.rs Outdated
Comment thread crates/minibf/src/lib.rs Outdated
Comment on lines +445 to +447
panic!(
"base_path must start with '/', must not be just '/', and must not contain wildcards; got: \"{}\"",
base_path

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we should avoid a panic and instead bubble up an error, ideally a "config" error variant.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've addressed the panic by adding a ConfigError(String) variant to ServeError in dolos-core and changing build_router to return Result<Router, ServeError>. Invalid base_path values now return Err(ServeError::ConfigError(...)) instead of panicking, propagating through Driver::run consistently with BindError and ShutdownError.

I've also rebased onto the current main to resolve the CI conflicts.

@adrian1-dot adrian1-dot force-pushed the feat/add-api-v0-base-path branch from 4da0643 to f1a5f18 Compare March 6, 2026 09:59
@scarmuega

Copy link
Copy Markdown
Member

Thanks for this — Blockfrost /api/v0 spec compliance is worth having. The branch has gone stale (the minibf crate was restructured, so there are extensive merge conflicts). Could you rebase onto current main? The core change is small enough that it should rebase cleanly once the router code is re-pointed. One open question for us: whether base_path should stay opt-in (as here) or whether we eventually default to /api/v0 for closer Blockfrost parity — for now opt-in is the right call.

@adrian1-dot adrian1-dot force-pushed the feat/add-api-v0-base-path branch 2 times, most recently from cdfa20b to b8f9a7b Compare May 24, 2026 20:39
@adrian1-dot

Copy link
Copy Markdown
Contributor Author

Rebased onto current main and squashed to a single commit. The minibf restructuring resolved cleanly — diff is now ~60 lines focused on MinibfConfig.base_path + ServeError::ConfigError.

The inline ConfigError change from the earlier review is preserved: invalid base_path values (empty, / alone, missing leading /, or containing *) return Err(ServeError::ConfigError(...)) instead of panicking.

On your open question — agreed that opt-in is the right call for now. Defaulting to /api/v0 would silently break every existing root-level integration; happy to revisit in a follow-up once there's a deprecation path.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/minibf/src/test_support.rs (1)

126-126: ⚡ Quick win

Add a regression test for the new base_path contract.

This helper now only unwraps the fallible builder. Please add an integration case that builds with base_path = Some("/api/v0".into()) and asserts prefixed routes resolve while root routes 404, plus one invalid-path case that returns ServeError::ConfigError.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/minibf/src/test_support.rs` at line 126, Add an integration test
exercising the new base_path contract since build_router_with_facade now unwraps
a fallible builder: write a test that calls the fallible builder via
build_router_with_facade(facade) with facade configured with base_path =
Some("/api/v0".into()) and assert that routes under "/api/v0/..." resolve
successfully while the same routes at root "/" return 404; also add a test case
that supplies an invalid base_path and asserts the builder fails with
ServeError::ConfigError. Locate and modify the tests around
build_router_with_facade and the test helper in test_support.rs to create these
two cases (valid prefixed routing and invalid-path config error) and assert the
expected outcomes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/minibf/src/lib.rs`:
- Around line 512-528: The base_path validation allows values ending with '/'
which will not align with Router::nest because
NormalizePathLayer::trim_trailing_slash() normalizes requests but nest uses the
prefix literally; update the logic around base_path in the function that
constructs the Router so that you canonicalize by removing any trailing '/'
(e.g. rtrim) before calling Router::nest (or alternatively return
ServeError::ConfigError if you prefer to reject trailing slashes), ensuring the
resulting Router::new().nest(canonical_base_path, app) uses the trimmed value
while still applying NormalizePathLayer::trim_trailing_slash().

---

Nitpick comments:
In `@crates/minibf/src/test_support.rs`:
- Line 126: Add an integration test exercising the new base_path contract since
build_router_with_facade now unwraps a fallible builder: write a test that calls
the fallible builder via build_router_with_facade(facade) with facade configured
with base_path = Some("/api/v0".into()) and assert that routes under
"/api/v0/..." resolve successfully while the same routes at root "/" return 404;
also add a test case that supplies an invalid base_path and asserts the builder
fails with ServeError::ConfigError. Locate and modify the tests around
build_router_with_facade and the test helper in test_support.rs to create these
two cases (valid prefixed routing and invalid-path config error) and assert the
expected outcomes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32802faf-1d2b-41d7-a521-1d5226132704

📥 Commits

Reviewing files that changed from the base of the PR and between cdfa20b and b8f9a7b.

📒 Files selected for processing (7)
  • crates/core/src/config.rs
  • crates/core/src/lib.rs
  • crates/minibf/README.md
  • crates/minibf/src/lib.rs
  • crates/minibf/src/test_support.rs
  • docs/content/apis/minibf.mdx
  • src/bin/dolos/minibf.rs
✅ Files skipped from review due to trivial changes (3)
  • src/bin/dolos/minibf.rs
  • crates/minibf/README.md
  • docs/content/apis/minibf.mdx

Comment thread crates/minibf/src/lib.rs
Adds an optional `base_path` field to `MinibfConfig` that nests all
minibf routes (including `/health` and `/metrics`) under a configurable
path prefix. Set to `/api/v0` for full Blockfrost OpenAPI compliance;
when omitted, routes remain at the root (no behavioral change).

Invalid values (empty, just `/`, missing leading `/`, or containing `*`)
return a new `ServeError::ConfigError` variant instead of panicking.
@adrian1-dot adrian1-dot force-pushed the feat/add-api-v0-base-path branch from b8f9a7b to 8f61088 Compare May 24, 2026 20:59
@adrian1-dot

Copy link
Copy Markdown
Contributor Author

Addressed both coderabbit findings in 8f61088:

  • Trailing-slash canonicalization: base_path is now trimmed of trailing / before validation/Router::nest, so "/api/v0/" and "/api/v0" behave identically.
  • Integration tests: added base_path_tests covering (1) prefixed routes resolve + root 404s when base_path is set, (2) trailing-slash input is normalized, (3) invalid inputs ("", "/", missing leading /, contains *) return ServeError::ConfigError.

All 214 minibf tests pass.

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