PulseGuard API is an ASP.NET Core Web API portfolio project for monitoring website and API health endpoints. Its intended responsibility is to record uptime history and identify repeated check failures so they can become actionable alerts.
Current status: MVP backend stage. PulseGuard API includes Swagger, PostgreSQL persistence, JWT authentication, user-owned monitors, scheduled health checks, persisted alerts, a user-scoped dashboard, Docker Compose local development, automated tests, GitHub Actions CI, and a manual AWS EC2 deployment guide. External alert delivery and production deployment automation are not implemented yet.
PulseGuard API provides a backend foundation for defining health monitors, executing checks, storing their outcomes, and exposing a service's current and historical status.
- Framework: ASP.NET Core Web API (.NET 8)
- Language: C#
- API documentation: Swagger / OpenAPI via Swashbuckle
- Persistence: Entity Framework Core 8 with the Npgsql PostgreSQL provider
- Authentication: JWT bearer tokens with ASP.NET Core password hashing
- Health checks: Hosted background service using
HttpClientFactory - Containerisation: Docker and Docker Compose
- Testing: xUnit, FluentAssertions, ASP.NET Core integration testing, and EF Core InMemory
- Continuous integration: GitHub Actions
- Architecture: Controller, service, repository, data, model, DTO, and configuration layers
The following are intentionally not part of the current implementation: Redis and external alert delivery.
flowchart LR
Client[Swagger or API client] --> Controllers[ASP.NET Core controllers]
Controllers --> Services[Application services]
Services --> Repositories[Repositories]
Repositories --> DbContext[EF Core AppDbContext]
DbContext --> Database[(PostgreSQL)]
Worker[Health check background worker] --> HttpClient[HttpClientFactory]
HttpClient --> Endpoints[Monitored websites and APIs]
Worker --> AlertState[Alert state service]
Worker --> DbContext
AlertState --> DbContext
The diagram has two main flows: requests made by a user and automatic checks made by the application.
For example, when an authenticated user creates a monitor from Swagger:
- Swagger or an API client sends an HTTP request such as
POST /api/monitors. - An ASP.NET Core controller receives the request. Controllers are responsible for HTTP concerns: routes, request validation, response status codes, and authentication requirements.
- The controller passes the work to an application service. Services contain the business rules, such as ensuring that a user can access only their own monitors.
- The service uses a repository to read or write monitor, alert, or check data.
- The repository uses EF Core's
AppDbContext, which converts C# queries and entities into SQL. - PostgreSQL stores the persistent data: users, monitors, monitor checks, and alerts.
This separation keeps HTTP code, business rules, and database access from being mixed together in one controller.
The hosted health-check background worker starts when the API starts. It is not triggered by Swagger and does not need a user request.
- The worker finds enabled monitors whose
checkIntervalSecondsis due. - It uses
HttpClientFactoryto send an HTTPGETrequest to each monitored website or API URL. - The monitored endpoint returns a response, such as HTTP
200,404,500, or a timeout/connection error. - The worker compares the outcome with the monitor's expected status code and timeout settings.
- It saves a
MonitorCheckresult throughAppDbContext, including success/failure, status code, response time, and any error message.
After each health check, the worker passes the result to the alert-state service:
- The service counts the monitor's most recent consecutive failures.
- At three consecutive failures, it creates one
OPENalert. Further failures update that same alert instead of creating duplicates. - A user can acknowledge an open alert, changing it to
ACKNOWLEDGED. - When a later health check succeeds, an
OPENorACKNOWLEDGEDalert changes toRESOLVEDautomatically. - Each alert-state change is saved to PostgreSQL through
AppDbContext.
In short: users manage monitoring settings through the API, while the background worker continuously checks those settings and records the system's health automatically.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Returns the API status and the current UTC timestamp. |
POST |
/api/auth/register |
Creates a user account and returns a JWT. |
POST |
/api/auth/login |
Authenticates a user and returns a JWT. |
POST |
/api/monitors |
Creates a persisted monitor. |
GET |
/api/monitors |
Lists persisted monitors. |
GET |
/api/monitors/{id} |
Retrieves a monitor by ID. |
PUT |
/api/monitors/{id} |
Replaces a monitor's editable settings. |
DELETE |
/api/monitors/{id} |
Deletes a monitor. |
GET |
/api/monitors/{id}/checks |
Retrieves health-check history for an owned monitor. |
GET |
/api/alerts |
Lists alerts for monitors owned by the authenticated user. |
GET |
/api/alerts/{id} |
Retrieves an owned alert. |
PUT |
/api/alerts/{id}/acknowledge |
Acknowledges an open owned alert. |
GET |
/api/dashboard/summary |
Returns the authenticated user's monitor, alert, and recent-check summary. |
Example response:
{
"status": "Healthy",
"timestampUtc": "2026-06-20T12:34:56.789Z"
}Swagger UI is available at /swagger while the API is running.
See the AWS EC2 deployment plan for a beginner-friendly, manual Docker Compose deployment guide. It documents the required AWS resources, environment variables, security-group rules, migrations, and a future RDS path; it does not add deployment automation.
- Register users and issue JWT bearer tokens using hashed passwords.
- Create, list, retrieve, update, and delete user-owned monitors persisted in PostgreSQL.
- Run due checks for enabled monitors and persist status, latency, and failure details.
- Open, acknowledge, and automatically resolve monitor failure alerts.
- Summarise monitor health, alert counts, and recent activity for the authenticated user.
- Configure a monitor name, endpoint URL, check interval, and active state.
- Run automated unit and integration tests for authentication, ownership, alert state changes, and dashboard summaries.
- Build and test the solution automatically on pushes and pull requests to
main. - Document a manual AWS EC2 deployment using Docker Compose, with a future RDS migration path.
- Alert delivery through email, webhooks, Slack, or Discord.
- Redis-backed caching or job coordination where justified.
- HTTPS through a reverse proxy and production deployment automation.
- Managed AWS database infrastructure with Amazon RDS.
- Uptime reporting, performance summaries, teams, and role-based access.
- .NET 8 SDK
- PostgreSQL 14 or later
Create a local database and user:
sudo -u postgres psql
CREATE USER pulseguard_user WITH PASSWORD 'replace-with-a-local-password';
CREATE DATABASE pulseguard OWNER pulseguard_user;
\qSet the connection string for the current terminal. This overrides the placeholder value in appsettings.json and avoids committing a real password:
export ConnectionStrings__PulseGuardDatabase='Host=localhost;Port=5432;Database=pulseguard;Username=pulseguard_user;Password=replace-with-a-local-password'On Ubuntu, PostgreSQL's default local peer authentication can be used instead of a password. After creating a matching PostgreSQL role and database, use the Unix socket connection string:
sudo -u postgres createuser --login "$USER"
sudo -u postgres createdb --owner="$USER" pulseguard
export ConnectionStrings__PulseGuardDatabase="Host=/var/run/postgresql;Port=5432;Database=pulseguard;Username=$USER"appsettings.json contains a development-only JWT key so the project can run locally. Override it for any environment that is shared or deployed:
export Jwt__Key="$(openssl rand -base64 48)"Do not commit a real JWT signing key.
Install the EF Core CLI tool once (the major version must match EF Core 8):
dotnet tool install --global dotnet-ef --version 8.0.11Apply the committed database migration:
dotnet ef database updateCreate a new migration after changing an EF Core model:
dotnet ef migrations add DescriptiveMigrationName- Docker Engine with either the Docker Compose plugin (
docker compose) or the legacy CLI (docker-compose)
Start the API and PostgreSQL containers:
docker compose up --buildFor Docker Compose 1.x, use the equivalent command:
docker-compose up --buildThe API uses the Compose service name postgres to connect to PostgreSQL. PostgreSQL is exposed on host port 5433 to avoid conflicts with a local PostgreSQL instance on port 5432.
Apply EF Core migrations from the host after the containers are running:
ConnectionStrings__PulseGuardDatabase='Host=localhost;Port=5433;Database=pulseguard;Username=pulseguard;Password=pulseguard_dev_password' dotnet ef database updateOpen Swagger at http://localhost:8080/swagger.
Stop the containers:
docker compose downFor Docker Compose 1.x:
docker-compose downTo also delete the Docker PostgreSQL volume and all local container data:
docker compose down --volumesFor Docker Compose 1.x:
docker-compose down -vdotnet restore
dotnet build
dotnet runOpen the following URLs after the server starts:
- Swagger UI:
http://localhost:5054/swagger - Health endpoint:
http://localhost:5054/api/health
To call the health endpoint from a terminal:
curl http://localhost:5054/api/healthRun the backend test suite from the repository root:
dotnet test PulseGuard.slnThe tests use an in-memory database and cover authentication, monitor ownership, alert state transitions, dashboard aggregation, and basic API behavior without requiring PostgreSQL or Docker.
The suite currently validates:
GET /api/healthreturns a successful response.- Registering and logging in a user returns a JWT access token.
- Protected monitor endpoints reject requests without a JWT token.
- A user cannot retrieve a monitor owned by another user.
- Three consecutive failed checks create one
OPENalert; a later successful check resolves it. - Acknowledging an alert changes its status to
ACKNOWLEDGEDand records the acknowledgement time. - Dashboard totals and average response time are calculated from the current user's monitors, checks, and alerts only.
For the name and result of each test, run:
dotnet test PulseGuard.sln --logger "console;verbosity=detailed"- Open
http://localhost:5054/swagger. - Call
POST /api/auth/registerwith an email and a password of at least eight characters. - Copy
accessTokenfrom the response. - Click Authorize, enter the token, and confirm.
- Call the protected
/api/monitorsendpoints. Each user can access only their own monitors.
The background worker starts with the API. It polls every 10 seconds by default and performs a check only when an enabled monitor's checkIntervalSeconds is due. Each check sends an HTTP GET request and compares its response status with expectedStatusCode.
Monitor create and update requests support these health-check settings:
{
"checkIntervalSeconds": 60,
"timeoutSeconds": 10,
"expectedStatusCode": 200,
"isActive": true
}Set HealthCheckWorker__PollingIntervalSeconds to change the local polling frequency. This does not override the per-monitor checkIntervalSeconds setting.
An alert opens after three consecutive failed checks for the same monitor. Only one active alert can exist per monitor: additional failures update its failureCount instead of creating duplicates. A successful check automatically changes an OPEN or ACKNOWLEDGED alert to RESOLVED.
Alert statuses are OPEN, ACKNOWLEDGED, and RESOLVED. Alert endpoints return only alerts that belong to the authenticated user's monitors.
GET /api/dashboard/summary returns counts for the authenticated user's monitors and alerts, the latest status of each monitor, average response time from the latest checks, and up to ten recent alerts and checks. A monitor is UNKNOWN until it has a check result.
- Create ASP.NET Core Web API foundation and health endpoint.
- Enable Swagger / OpenAPI documentation.
- Define the initial monitor model and in-memory CRUD endpoints.
- Add PostgreSQL persistence and the initial EF Core migration.
- Add JWT authentication and user-owned monitor access.
- Add scheduled health checks and persisted check history.
- Add consecutive-failure alerts with acknowledgement and automatic resolution.
- Add a user-scoped dashboard summary API.
- Add Docker support for local development.
- Add automated tests for services and API endpoints.
- Add GitHub Actions CI pipeline.
- Add AWS deployment documentation.
PulseGuard API demonstrates backend engineering skills relevant to Backend Engineer, Software Engineer, and C# .NET Developer roles. The project covers REST API design, layered architecture, PostgreSQL data modelling, JWT security, background processing, reliability-oriented alert workflows, testing, CI, Docker-based local development, and production-minded deployment planning.