Browsable API Interface
Browsable API Interface
Section titled “Browsable API Interface”Surkyl Server provides a self-documenting, interactive API browser interface. When users access API endpoints through a web browser, they receive an HTML interface with API documentation and interactive testing capabilities. Standard API clients continue to receive JSON/raw responses automatically.
Overview
Section titled “Overview”The browsable API system provides:
- Automatic Content Negotiation - Detects browser vs API client requests
- Interactive Testing Interface - Test APIs directly from the browser
- OpenAPI Integration - Pulls metadata from your OpenAPI specification
- Zero Breaking Changes - Existing API clients work unchanged
- Surkyl Branding - Dark theme with Surkyl brand colors
Accessing the API Browser
Section titled “Accessing the API Browser”Browser Access
Section titled “Browser Access”When you visit any API endpoint in a web browser:
- Root URL (
/) → Shows the API index with all available endpoints - Any endpoint (
/status,/health) → Opens the interactive testing interface - Manual override → Use
?format=htmlto force HTML view
API Client Access
Section titled “API Client Access”Standard API clients (curl, etc.) automatically receive JSON:
# Returns JSON responsecurl http://localhost:64001/status
# Force JSON even in browsercurl http://localhost:64001/status?format=jsonContent Negotiation Priority
Section titled “Content Negotiation Priority”- Query Parameter (highest priority):
?format=json|html - Accept Header:
Accept: text/htmlvsAccept: application/json - Default: JSON for API clients
Features
Section titled “Features”Interactive Testing Interface
Section titled “Interactive Testing Interface”The API testing interface includes:
- URL Bar - Method selector and editable URL
- Request Builder - Tabs for Params, Headers, Body, and Info
- Query Parameters - Add/remove key-value pairs with checkboxes
- Headers Editor - Customize request headers
- Request Body - JSON editor for POST/PUT/PATCH requests
- Response Viewer - Syntax-highlighted JSON with copy functionality
- Copy as cURL - Export request as a cURL command
API Root Page
Section titled “API Root Page”The root page (/) displays:
- Server name and version (when
show_versionis enabled) - Total number of endpoints
- Quick links to OpenAPI specs
- Grid of all available endpoints with:
- HTTP method badges (GET, POST, PUT, DELETE, PATCH)
- Endpoint path
- Summary description
- Tags/categories
Configuration
Section titled “Configuration”Show/Hide Version Information
Section titled “Show/Hide Version Information”Control version display via server settings:
server: show_version: true # Set to false to hide version in UIWhen show_version is false:
- Version number is hidden in the UI header
- Version stat card is removed from root page
- JSON responses respect this setting as well
Developer Guide: Making Handlers Compliant
Section titled “Developer Guide: Making Handlers Compliant”To ensure your API handlers work with the browsable API system, follow these guidelines:
1. Add the NegotiatedFormat Extractor
Section titled “1. Add the NegotiatedFormat Extractor”Every handler that should support browsable API must accept the NegotiatedFormat extractor:
use crate::browsable::NegotiatedFormat;
pub async fn my_handler( // ... other extractors format: NegotiatedFormat, // Add this parameter) -> impl IntoResponse { // handler logic}2. Wrap Response with BrowsableResponse
Section titled “2. Wrap Response with BrowsableResponse”Instead of returning Json<T>, wrap your response with BrowsableResponse:
use crate::browsable::BrowsableResponse;
pub async fn status_handler( server_settings: Extension<Arc<ServerSettings>>, format: NegotiatedFormat,) -> impl IntoResponse { let response = StatusResponse { status: ServerStatus::Alive, message: "The server is running!".to_string(), // ... other fields };
// Wrap with BrowsableResponse BrowsableResponse::from_path(response, "/status", "GET", format) .with_show_version(server_settings.server.show_version)}3. Register Endpoint in OpenAPI Spec
Section titled “3. Register Endpoint in OpenAPI Spec”Add your endpoint to src/openapi.yml so it appears in the browsable interface:
paths: /my-endpoint: get: tags: - MyCategory summary: Brief description of what this does description: Detailed explanation of the endpoint functionality operationId: myEndpointOperation responses: '200': description: Success response content: application/json: schema: $ref: '#/components/schemas/MyResponseSchema'4. Complete Example
Section titled “4. Complete Example”Here’s a full example of a compliant handler:
use crate::browsable::{BrowsableResponse, NegotiatedFormat};use crate::config::server::ServerSettings;use axum::{Extension, response::IntoResponse};use std::sync::Arc;
#[derive(serde::Serialize, schemars::JsonSchema)]pub struct MyResponse { pub data: String, pub count: i32,}
pub async fn my_data_handler( Extension(server_settings): Extension<Arc<ServerSettings>>, format: NegotiatedFormat,) -> impl IntoResponse { let response = MyResponse { data: "Hello from API".to_string(), count: 42, };
BrowsableResponse::from_path(response, "/my-data", "GET", format) .with_show_version(server_settings.server.show_version)}5. Ensure Response is Serializable
Section titled “5. Ensure Response is Serializable”Your response type must implement Serialize:
use serde::Serialize;use schemars::JsonSchema;
#[derive(Serialize, JsonSchema)] // Both traits requiredpub struct MyResponse { pub field1: String, pub field2: Option<i32>,}6. Handle Non-JSON Responses
Section titled “6. Handle Non-JSON Responses”For handlers that return non-JSON (like plain text), check the format preference manually:
pub async fn root_handler( server_settings: Extension<Arc<ServerSettings>>, format: NegotiatedFormat,) -> impl IntoResponse { if format.wants_html { return crate::browsable::response::render_api_root( format, server_settings.server.show_version, ); }
// Return plain text for API clients Response::builder() .status(StatusCode::OK) .header(header::CONTENT_TYPE, "text/plain") .body(Body::from("Server Name")) .unwrap()}Best Practices
Section titled “Best Practices”- Always add
NegotiatedFormatextractor to browsable handlers - Use
BrowsableResponse::from_path()for automatic metadata loading - Call
.with_show_version()to respect configuration - Document endpoints in OpenAPI spec for rich metadata
- Use
SerializeandJsonSchemaderives on response types - Test both browser and API client access
DON’T:
Section titled “DON’T:”- Return raw
Json<T>withoutBrowsableResponsewrapper - Forget to add endpoint to
openapi.yml - Hardcode version strings - use
MANIFEST - Ignore the
show_versionsetting
Architecture
Section titled “Architecture”Module Structure
Section titled “Module Structure”src/browsable/├── mod.rs # Module exports├── extractor.rs # NegotiatedFormat content negotiation├── metadata.rs # Endpoint metadata registry (loads from OpenAPI)└── response.rs # BrowsableResponse wrapper
templates/├── api_browser.html # Interactive testing interface└── api_root.html # API index pageHow Content Negotiation Works
Section titled “How Content Negotiation Works”- Request arrives at handler
- NegotiatedFormat extractor parses Accept header and query params
- Handler creates response data
- BrowsableResponse wrapper checks
format.wants_html - If browser: Renders HTML template with response data
- If API client: Returns raw JSON
Metadata Registry
Section titled “Metadata Registry”On server startup, the metadata registry loads from openapi.yml:
// In main.rslet openapi_yaml = include_str!("openapi.yml");init_registry(openapi_yaml)?;This provides:
- Endpoint summaries and descriptions
- Tags for categorization
- Request/response schemas
- Allowed HTTP methods
Troubleshooting
Section titled “Troubleshooting”Browser shows JSON instead of HTML
Section titled “Browser shows JSON instead of HTML”- Check if endpoint has
NegotiatedFormatextractor - Verify handler uses
BrowsableResponsewrapper - Try explicit
?format=htmlparameter
Endpoint not appearing in sidebar
Section titled “Endpoint not appearing in sidebar”- Ensure endpoint is registered in
openapi.yml - Verify OpenAPI syntax is valid
- Check that metadata registry initialized without errors
Version showing when it shouldn’t
Section titled “Version showing when it shouldn’t”- Verify
.with_show_version(server_settings.server.show_version)is called - Check config file has
show_version: false - Restart server after config change
Related Documentation
Section titled “Related Documentation”- Stack Overview - Axum and SQLx patterns
- API Documentation - OpenAPI endpoints
- Configuration - Server settings