title: "Navius API Integration Guide" description: "A comprehensive guide for integrating external APIs with Navius applications, including best practices, caching strategies, and testing approaches" category: guides tags:
- api
- caching
- integration
- performance
- testing related:
- ../reference/api/README.md
- ../guides/features/authentication.md
- ../guides/development/testing.md
- ../guides/features/caching.md last_updated: March 27, 2025 version: 1.0
Navius API Integration Guide
This guide explains how to integrate external APIs into your Navius application using the built-in API resource abstraction.
Overview
Navius makes it easy to integrate with external APIs by providing:
- 🔄 Automatic client generation from OpenAPI schemas
- 🛡️ Built-in resilience patterns for reliable API calls
- 💾 Intelligent caching to reduce load on downstream APIs
- 🔍 Type-safe data transformation using Rust's powerful type system
- 📊 Detailed metrics and logging for API calls
Adding an API Integration
Automated Method (Recommended)
The easiest way to add a new API integration is to use the provided script:
./scripts/add_api.sh <api_name> <api_url> <schema_url> [endpoint_path] [param_name]
For example:
./scripts/add_api.sh petstore https://petstore.swagger.io/v2 https://petstore.swagger.io/v2/swagger.json pet id
This will:
- Generate API client code from the OpenAPI schema
- Create handler functions for the specified endpoint
- Configure routes for the new API
- Add the API to the registry
Manual Method
If you prefer to add an API integration manually:
- Create an API client:
#![allow(unused)] fn main() { pub struct PetstoreClient { base_url: String, http_client: Client, } impl PetstoreClient { pub fn new(base_url: &str) -> Self { Self { base_url: base_url.to_string(), http_client: Client::new(), } } pub async fn get_pet(&self, id: i64) -> Result<Pet, ApiError> { let url = format!("{}/pet/{}", self.base_url, id); let response = self.http_client .get(&url) .send() .await .map_err(|e| ApiError::RequestFailed(e.to_string()))?; if !response.status().is_success() { return Err(ApiError::ResponseError( response.status().as_u16(), format!("API returned error: {}", response.status()), )); } response .json::<Pet>() .await .map_err(|e| ApiError::DeserializationError(e.to_string())) } } }
- Create models:
#![allow(unused)] fn main() { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Pet { pub id: i64, pub name: String, pub status: String, #[serde(skip_serializing_if = "Option::is_none")] pub category: Option<Category>, #[serde(skip_serializing_if = "Vec::is_empty", default)] pub tags: Vec<Tag>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Category { pub id: i64, pub name: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Tag { pub id: i64, pub name: String, } }
- Implement API resource trait:
#![allow(unused)] fn main() { use navius::core::api::{ApiResource, ApiError}; impl ApiResource for Pet { type Id = i64; fn resource_type() -> &'static str { "pet" } fn api_name() -> &'static str { "Petstore" } } }
- Create handler functions:
#![allow(unused)] fn main() { use navius::core::api::{create_api_handler, ApiHandlerOptions}; pub async fn get_pet_handler( State(state): State<AppState>, Path(id): Path<i64>, ) -> Result<Json<Pet>, AppError> { // Create API handler with reliability features let handler = create_api_handler( |state, id| async move { let client = &state.petstore_client; client.get_pet(id).await }, ApiHandlerOptions { use_cache: true, use_retries: true, max_retry_attempts: 3, cache_ttl_seconds: 300, detailed_logging: true, }, ); handler(State(state), Path(id)).await } }
- Add routes:
#![allow(unused)] fn main() { // In your router setup let api_routes = Router::new() .route("/pet/:id", get(get_pet_handler)); }
API Resource Abstractions
The ApiResource
trait provides the foundation for the API abstraction:
#![allow(unused)] fn main() { pub trait ApiResource: Sized + Send + Sync + 'static { type Id: Display + Eq + Hash + Clone + Send + Sync + 'static; fn resource_type() -> &'static str; fn api_name() -> &'static str; } }
This allows the framework to automatically provide:
- Consistent caching of API responses
- Unified error handling
- Standardized logging patterns
- Metrics collection for API calls
- Retry logic with backoff
Reliability Patterns
Navius implements several reliability patterns for API integrations:
Caching
API responses are automatically cached using the configured cache implementation:
#![allow(unused)] fn main() { // Configure caching options ApiHandlerOptions { use_cache: true, cache_ttl_seconds: 300, // Cache for 5 minutes // ... } }
Retry Logic
Failed API calls can be automatically retried with exponential backoff:
#![allow(unused)] fn main() { // Configure retry options ApiHandlerOptions { use_retries: true, max_retry_attempts: 3, // ... } }
Circuit Breaking
Navius implements circuit breaking to prevent cascading failures:
#![allow(unused)] fn main() { // Enable circuit breaking let circuit_breaker = CircuitBreaker::new( "petstore", CircuitBreakerConfig { success_threshold: 2, timeout_ms: 1000, half_open_timeout_ms: 5000, }, ); // Apply to client let client = PetstoreClient::new("https://petstore.swagger.io/v2") .with_circuit_breaker(circuit_breaker); }
Handling Errors
Navius provides a standardized error handling pattern for API calls:
#![allow(unused)] fn main() { // In your AppError implementation #[derive(Debug, Error)] pub enum AppError { #[error("API Error: {0}")] Api(#[from] ApiError), // Other error types... } // ApiError is provided by the framework #[derive(Debug, Error)] pub enum ApiError { #[error("Request failed: {0}")] RequestFailed(String), #[error("Response error ({0}): {1}")] ResponseError(u16, String), #[error("Deserialization error: {0}")] DeserializationError(String), #[error("Resource not found")] NotFound, #[error("Circuit open")] CircuitOpen, #[error("Request timeout")] Timeout, } }
Advanced Configuration
Manual Cache Control
You can manually control caching for specific scenarios:
#![allow(unused)] fn main() { // Force refresh from the source API let handler = create_api_handler( |state, id| async move { let client = &state.petstore_client; client.get_pet(id).await }, ApiHandlerOptions { use_cache: true, force_refresh: true, // Skip cache and update it cache_ttl_seconds: 300, // ... }, ); }
Custom Cache Keys
For complex scenarios, you can provide custom cache key generation:
#![allow(unused)] fn main() { // Custom cache key generation let handler = create_api_handler_with_options( |state, id| async move { let client = &state.petstore_client; client.get_pet(id).await }, |id| format!("custom:pet:{}", id), // Custom cache key ApiHandlerOptions { use_cache: true, // ... }, ); }
Custom Response Transformation
Transform API responses before returning them:
#![allow(unused)] fn main() { // Transform the API response let handler = create_api_handler_with_transform( |state, id| async move { let client = &state.petstore_client; client.get_pet(id).await }, |pet| { // Transform the pet before returning PetResponse { id: pet.id, name: pet.name, status: pet.status, // Additional transformations... } }, ApiHandlerOptions { // ... }, ); }
Testing API Integrations
Navius provides utilities for testing API integrations:
#![allow(unused)] fn main() { use navius::test::api::{MockApiClient, ResponseBuilder}; #[tokio::test] async fn test_pet_handler() { // Create mock API client let mut mock_client = MockApiClient::new(); // Configure mock response mock_client.expect_get_pet() .with(eq(1)) .times(1) .returning(|_| { Ok(Pet { id: 1, name: "Rex".to_string(), status: "available".to_string(), category: None, tags: vec![], }) }); // Create test app with mock client let app = test::build_app().with_api_client(mock_client).await; // Test the handler let response = app .get("/pet/1") .send() .await; assert_eq!(response.status(), StatusCode::OK); let pet: Pet = response.json().await; assert_eq!(pet.id, 1); assert_eq!(pet.name, "Rex"); } }
Performance Considerations
When integrating APIs, consider:
- Caching Strategy: Choose appropriate TTL values based on data freshness requirements
- Batch Operations: Use batch endpoints where available instead of multiple single-item calls
- Concurrent Requests: Use
futures::future::join_all
for parallel API calls - Response Size: Request only the fields you need if the API supports field filtering
- Timeouts: Configure appropriate timeouts to prevent blocking application threads
Conclusion
Navius provides a comprehensive API integration framework that makes it easy to connect to external services while maintaining resilience, performance, and code quality. By using the API resource abstraction pattern, you can ensure consistent patterns for all API integrations in your application.
For more complex scenarios or custom integrations, you can extend the framework's base components to implement domain-specific functionality while still benefiting from the built-in reliability features.
Related Documents
- Installation Guide - How to install the application
- Development Workflow - Development best practices