DevHabit API is a versioned RESTful web service built with .NET 9 that helps users track personal habits and routines. It provides secure JWT-based authentication, background job processing, GitHub integration, and structured observability through OpenTelemetry. The system use PostgreSQL, Docker, and best practices in validation, testing, and deployment. It supports local development, CI/CD pipelines, and containerized production environments.
This project was built using the latest ASP.NET Core features and best practices.
- .NET 9 β Modern framework for building scalable web APIs
- PostgreSQL β Open-source relational database
- Entity Framework Core β ORM for data access
- Docker β Containerization and local infrastructure
- Swagger (Swashbuckle) β OpenAPI interactive documentation
- Scalar β Alternative interactive API explorer
- FluentValidation β Model validation framework
- Quartz.NET β Background jobs and scheduling
- Polly (via Microsoft.Extensions.Http.Resilience) β Fault-handling and resiliency
- Refit β Typed REST API clients
- CsvHelper β CSV import/export utilities
- OpenTelemetry β Distributed tracing and observability
- .NET Aspire β Simplified distributed application model with OpenTelemetry support
- Seq β Optional centralized log aggregation
- Azure Monitor β Telemetry integration (optional)
- xUnit β Unit testing framework
- NSubstitute β Friendly mocking for .NET
- Testcontainers β Integration testing with containers
- WireMock.Net β HTTP stubbing and mocking
- SonarAnalyzer β Static code analysis
- Filtering, sorting, pagination, and data shaping on resource collections
- Content negotiation and HATEOAS support
- API versioning with media type support
- Typed HTTP clients via Refit
- Interactive API docs with Swagger and Scalar
- PostgreSQL with EF Core and naming conventions
- Background jobs with Quartz
- GitHub integration for external data or automation
- File-based data import (CSV support)
- JWT-based authentication and authorization
- CORS policy configuration
- Secure storage of GitHub api keys via encryption
- OpenTelemetry-based distributed tracing
- Health checks support
- HTTP resiliency patterns via Polly (.NET Resilience)
- Azure Monitor (optional integration)
- Logging with .Net Aspire or Seq
- Unit testing with xUnit and NSubstitute
- Integration testing with Testcontainers and PostgreSQL
- HTTP mocking via WireMock.Net
- Code coverage with Coverlet
- Local development support with Docker Compose
- Centralized package versioning with MSBuild
- Dockerized production image support
Make sure you have .NET CLI installed on your system. You can check if it's available by running:
dotnet --versionThis should print the installed version of the .NET CLI. If it's not installed, download it from the official .NET site.
To verify which SDK versions are installed:
dotnet --list-sdksImportant
The minimum .NET SDK version required is 9.0.0
Additionally, the project uses Docker for running supporting services (e.g., PostgreSQL, pgAdmin, Seq). Youβll need:
- Docker: Recommended to install Docker Desktop.
- Docker Compose: Typically included with Docker Desktop.
To check that Docker is installed and running:
docker --version
docker compose versionIf these commands fail or return errors, refer to the Docker installation guide.
To get started, clone the repository and set up the environment configuration:
- Clone the repository:
git clone https://github.com/jaimejaramilloperez/dev-habit.git- Navigate to the project directory:
cd dev-habit- Generate and trust the HTTPS development certificate:
dotnet dev-certs https -ep ./src/DevHabit.Api/aspnetapp.pfx -p Test1234!
dotnet dev-certs https --trust- Copy the environment template and configure it:
cp .env.template .env
# Edit the .env file as neededAfter installation, you're ready to run the app either locally or using Docker. See the Local Development or Docker sections for details.
Set up your environment to run the DevHabit API either locally or with Docker, depending on your workflow.
Note
The configuration values shown (e.g., passwords, ports, keys, connection strings) are provided for demonstration purposes only. You are free to modify them as needed β especially for production environments.
This project supports distributed tracing and telemetry via either .NET Aspire Dashboard or Seq.
Depending on which one you choose, you must configure the following environment variables:
OTEL_EXPORTER_OTLP_ENDPOINTOTEL_EXPORTER_OTLP_PROTOCOL
For .NET Aspire:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:18889
OTEL_EXPORTER_OTLP_PROTOCOL=grpcFor Seq:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:5341/ingest/otlp
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobufTip
If the API runs inside a Docker container, localhost refers to the container itself. In that case, replace localhost with the service name defined in your Docker network (e.g., devhabit.seq or devhabit.aspire-dashboard).
You can switch between them by updating your .env or Docker environment files.
You can run the API locally using the .NET CLI and supporting services (PostgreSQL, pgAdmin, Seq) via Docker Compose.
- Configure user secrets:
Sensitive values should be stored securely using user secrets:
{
"ConnectionStrings:Database": "Server=localhost;Port=5432;Database=devhabit;Username=devhabit;Password=123456;",
"Jwt:Key": "HTycXOjdDRfrtNYzQQbkx2L7ncCEe2989cWH6yrTFdSPRmFFe4K9qmbnjHJBRGHfaeRKvDEWzaS",
"Encryption:Key": "Ubf/RatKuzJ4p8Fc9nr9LKZFV5L8CjIZZCqcFlYZeEo="
}- Update
appsettings.Development.json
{
"ConnectionStrings": {
"Database": "your-database-connection-string-here"
},
"Jwt": {
"Key": "your-secret-key-here-that-should-also-be-fairly-long",
"Issuer": "dev-habit.api",
"Audience": "dev-habit.app",
"ExpirationInMinutes": 30,
"RefreshTokenExpirationInDays": 7
},
"Encryption": {
"Key": "your-secret-key-here-that-should-also-be-exactly-32-bytes-or-44-characters-in-base64-long"
},
"Cors": {
"AllowedOrigins": [
"http://localhost:3000"
]
},
"Jobs": {
"ScanIntervalInMinutes": 50
},
"GitHub": {
"BaseUrl": "https://api.github.com"
},
"OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:18889",
"OTEL_EXPORTER_OTLP_PROTOCOL": "grpc",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Information"
}
}
}- Configure environment variables (
.envfile):
# postgresql
POSTGRES_PORT="5432"
POSTGRES_DB="devhabit"
POSTGRES_USER="spartan"
POSTGRES_PASSWORD="123456"
# pgadmin
PGADMIN_EMAIL="user@mail.com"
PGADMIN_PASSWORD="123456"
# seq
SEQ_PASSWORD="12345678"- Start docker services:
docker compose up -d- Run the API:
dotnet run --project src/DevHabit.Api
# or with HTTPS
dotnet run --launch-profile https --project src/DevHabit.ApiThis mode runs both the application and services in Docker containers using a development image.
- Create or adjust
.env.docker-debug:
# docker
DOCKER_REGISTRY=""
DOCKER_IMAGE_TAG="dev"
DOCKER_BUILD_TARGET="dev"
DOCKER_ASPNETCORE_BUILD_CONFIGURATION="Debug"
# app
ENVIRONMENT="Development"
HTTP_PORT="5000"
HTTPS_PORT="5001"
CERTIFICATE_PATH="/https/aspnetapp.pfx"
CERTIFICATE_PASSWORD="Test1234!"
ENCRYPTION_KEY="Ubf/RatKuzJ4p8Fc9nr9LKZFV5L8CjIZZCqcFlYZeEo="
OTEL_EXPORTER_OTLP_ENDPOINT="http://devhabit.aspire-dashboard:18889"
OTEL_EXPORTER_OTLP_PROTOCOL="grpc"
# postgresql
POSTGRES_SERVER="devhabit.postgres"
POSTGRES_PORT="5432"
POSTGRES_DB="devhabit"
POSTGRES_USER="devhabit"
POSTGRES_PASSWORD="123456"
# pgadmin
PGADMIN_EMAIL="user@mail.com"
PGADMIN_PASSWORD="123456"
# seq
SEQ_PASSWORD="12345678"- Start the containers:
docker compose -f ./docker-compose.debug.yml --env-file .env.docker-debug up -dImportant
Debugging inside containers requires the vsdbg debugger. If itβs not already installed, refer to the official setup guide for instructions on how to install it manually.
If you're using Visual Studio Code, you can debug the application running inside a container in two ways:
- Containers: .NET Launch
This option builds and runs the container in debug mode and launches the debugger automatically.
Make sure you have a .env.docker-debug-image file with the following configuration:
ASPNETCORE_HTTP_PORTS=5000
ASPNETCORE_HTTPS_PORTS=5001
ASPNETCORE_Kestrel__Certificates__Default__Path=/https/aspnetapp.pfx
ASPNETCORE_Kestrel__Certificates__Default__Password=Test1234!
ConnectionStrings__Database=Server=devhabit.postgres;Port=5432;Database=devhabit;Username=devhabit;Password=123456;
OTEL_EXPORTER_OTLP_ENDPOINT=http://devhabit.aspire-dashboard:18889
OTEL_EXPORTER_OTLP_PROTOCOL=grpc- Containers .NET Attach (Preview)
This simpler option attaches the debugger to a running container β no extra configuration required beyond starting the containers as shown above in the step 2 of docker for development.
Warning
This feature is still in preview and may exhibit overall slower performance.
To build and run the API in production mode using a minimal Docker image:
- Create or adjust
.env.docker-prod:
DOTNET_USE_POLLING_FILE_WATCHER="1"
ASPNETCORE_ENVIRONMENT="Production"
ASPNETCORE_HTTP_PORTS=5000
ConnectionStrings__Database=Server=devhabit.postgres;Port=5432;Database=devhabit;Username=devhabit;Password=123456;
Jwt__Key="your-secret-key-here-that-should-also-be-fairly-long"
Jwt__Issuer="dev-habit.api"
Jwt__Audience="dev-habit.app"
Jwt__ExpirationInMinutes=30
Jwt__RefreshTokenExpirationInDays=7
Encryption__Key="Ubf/RatKuzJ4p8Fc9nr9LKZFV5L8CjIZZCqcFlYZeEo="
Cors__AllowedOrigins__0=""
Jobs__ScanIntervalInMinutes=50
GitHub__BaseUrl="https://api.github.com"
# Required
APPLICATIONINSIGHTS_CONNECTION_STRING=<your-azure-application-insights-connection-string>- Build the image:
docker buildx build \
--platform linux/amd64 \
-f src/DevHabit.Api/Dockerfile \
--target prod \
-t devhabit.api:latest .- Start the containers:
docker compose up -d- Run the container:
docker container run \
-d \
-p 5000:5000 \
--env-file .env.docker-prod \
--network dev-habit_default \
--name devhabit.api \
devhabit.api:latestThis project includes unit, integration and functional testing.
Note
Tests are located in the test/ directory.
- xUnit: Test framework.
- NSubstitute: Mocking.
- Testcontainers: Integration testing with ephemeral PostgreSQL instances.
- WireMock.Net: Mock external APIs.
- coverlet: Code coverage.
- Microsoft.AspNetCore.Mvc.Testing: End-to-end and functional tests.
dotnet testTo generate a code coverage report with HTML output, youβll need the dotnet reportgenerator tool. You can install it globally using:
dotnet tool install -g dotnet-reportgenerator-globaltoolA helper script is available at ./scripts/coverage.sh to automate the process.
Before running the script, make sure it has execution permissions:
chmod +x ./scripts/coverage.shThen run it with optional arguments:
./scripts/coverage.sh [output_dir] [verbosity]- output_dir: Directory where the report will be saved (default:
coverage) - verbosity: Verbosity level for ReportGenerator (default:
Error)
Once executed, the HTML report will be available at:
<output_dir>/index.htmlTip
If you're on Windows, you can use the PowerShell version of the script: .\scripts\coverage.ps1
DevHabit API provides interactive documentation via Swagger and Scalar, with support for versioned endpoints and JWT authentication.
Once the API is running:
- OpenAPI spec (JSON):
https://localhost:5001/swagger/1.0/swagger.json - Swagger UI:
https://localhost:5001/swagger - Scalar UI:
https://localhost:5001/scalar
Note
Replace 5001 with your actual HTTPS port if different.