Educational project demonstrating best practices for consuming external APIs without compromising your application's stability.
This project implements a ZIP code lookup service that consumes the public ViaCEP API, demonstrating resilience techniques, caching, auditing, and error handling when integrating with external APIs.
Show how to build a resilient API that:
- ✅ Doesn't break when the external API is offline
- ✅ Doesn't freeze when the external API is slow
- ✅ Reduces costs with intelligent caching
- ✅ Audits all external calls
- ✅ Returns standardized and informative errors
- 🔍 ZIP Code Lookup via ViaCEP API
- 💾 Local cache in MySQL to reduce external calls
- ⏱️ Configurable timeout (10s) to prevent freezing
- 📊 Complete auditing of all external calls (success and failure)
- ✔️ Robust input validation (8-digit ZIP code)
- 📦 Standardized responses with
ApiResponse<T>andApiErrorResponse - 🔄 AutoMapper for entity conversion
- 📖 Swagger/OpenAPI for interactive documentation
- .NET 8.0 - Modern web framework
- ASP.NET Core Web API - RESTful API construction
- Entity Framework Core 8.0 - ORM for data access
- MySQL 8.0 - Relational database
- AutoMapper 12.0 - Object-to-object mapping
- Polly 10.0 - Resilience policies (retry, timeout)
- Swagger/OpenAPI - API documentation
ApiConsumer/
├── Controllers/
│ └── CepController.cs # HTTP Endpoints
├── Services/
│ └── CepService.cs # Business logic and API consumption
├── Context/
│ ├── AppDbContext.cs # EF Core configuration
│ └── AppDbContextFactory.cs # Factory for migrations
├── Entities/
│ ├── Cep.cs # ZIP Code model
│ └── ExternalApiCall.cs # Call auditing
├── DTOs/
│ ├── Responses/
│ │ ├── CepResponseDTO.cs # Response DTO
│ │ ├── ApiResponse.cs # Response wrapper
│ │ └── ApiErrorResponse.cs # Error model
│ └── External/
│ └── ViaCepResponseDTO.cs # ViaCEP API DTO
├── Mappings/
│ └── CepProfile.cs # AutoMapper profiles
└── Migrations/ # EF Core migrations
Before starting, make sure you have:
- .NET SDK 8 or higher
- MySQL 8.0 or Docker
- Git
- IDE: JetBrains Rider, Visual Studio 2022, or VS Code
git clone https://github.com/renanzitoo/ApiConsumer.git
cd ApiConsumerEdit appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Port=3306;Database=apiconsumer;User=root;Password=your-password;"
}
}docker run --name mysql-apiconsumer -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=apiconsumer -p 3306:3306 -d mysql:8.0dotnet ef database updatedotnet restore
dotnet runAPI available at: https://localhost:5001 or http://localhost:5000
GET /api/cep/{cep}
Looks up a ZIP code and returns address data.
Request:
GET /api/cep/01001000Response:
{
"success": true,
"data": {
"cepCode": "01001000",
"street": "Praça da Sé",
"neighborhood": "Sé",
"city": "São Paulo",
"state": "SP",
"uf": "SP"
},
"error": null
}Request:
GET /api/cep/123Response:
{
"success": false,
"data": null,
"error": {
"code": "INVALID_CEP",
"message": "Invalid ZIP code. Please provide 8 digits."
}
}Request:
GET /api/cep/99999999Response:
{
"success": false,
"data": null,
"error": {
"code": "CEP_NOT_FOUND",
"message": "ZIP code not found."
}
}Response:
{
"success": false,
"data": null,
"error": {
"code": "CEP_SERVICE_UNAVAILABLE",
"message": "ZIP code service unavailable at the moment."
}
}https://localhost:5001/swagger
curl -X GET "https://localhost:5001/api/cep/01001000" -H "accept: application/json"Invoke-RestMethod -Uri "https://localhost:5001/api/cep/01001000" -Method Getfetch('https://localhost:5001/api/cep/01001000')
.then(response => response.json())
.then(data => console.log(data));- Repository Pattern - Data access via
AppDbContext - DTO Pattern - Separation of entities and models
- Dependency Injection - Native ASP.NET Core DI
- Exception Handling - Error-specific exceptions
- Cache-Aside Pattern - Cache checked before API
- Audit Log Pattern - External call logging
- ⏱️ 10-second timeout - Prevents freezing
- 💾 Local cache - Reduces external API dependency
- 📝 Isolated auditing - Log failures don't break the flow
- ✔️ Data validation - Prevents invalid data in the database
- 🔄 Retry Policy (Polly) - Automatically retries on failures
| Column | Type |
|---|---|
| Id | GUID |
| CepCode | VARCHAR(8) |
| Street | VARCHAR(200) |
| Neighborhood | VARCHAR(100) |
| City | VARCHAR(100) |
| State | VARCHAR(2) |
| UF | VARCHAR(2) |
| Column | Type |
|---|---|
| Id | GUID |
| Provider | VARCHAR(50) |
| Endpoint | VARCHAR(500) |
| RequestKey | VARCHAR(100) |
| Success | BOOLEAN |
| ResponseStatusCode | INT |
| ResponseContent | TEXT |
| ResponseTimeInMilliseconds | INT |
| RequestedAt | DATETIME |
- ✅ External API consumption with
HttpClient - ✅ Resilience and error handling
- ✅ Local caching for optimization
- ✅ Async/Await patterns
- ✅ Entity Framework Core with MySQL
- ✅ AutoMapper for DTOs
- ✅ Data validation
- ✅ Call auditing
- ✅ RESTful patterns
- ✅ Dependency injection
- Add
ILoggerfor structured observability - Implement Circuit Breaker with Polly
- Create unit tests (xUnit)
- Integration tests
- Health checks
- Metrics (Prometheus)
- Containerize the application
- CI/CD with GitHub Actions
- Rate limiting
Renan Costa
GitHub: renanzitoo
⭐ If this project was useful to you, consider giving it a star!