Skip to content
Back to projects

Project case study

Go Website Health Check REST API

A Go REST API that checks website availability, handles timeouts, stores results, and returns structured JSON health check data.

  • Go,
  • REST API,
  • JSON,
  • Postman,
  • Testing,
  • Benchmarks

Role

Sole developer

Timeline

Completed over 6 weeks

Type

Backend/API

Backend/APIFinished
Problem
Manually checking website availability is slow, inconsistent, and difficult to repeat across multiple URLs.
Approach
Built a Go REST API with URL validation, concurrent checks, timeout handling, in-memory result storage, tests, benchmarks, and Postman evidence.
Outcome
Created a finished backend/API project that demonstrates REST design, concurrency, practical error handling, and technical documentation.

Overview

The Go Website Health Check REST API is a backend project that checks whether one or more website URLs are reachable and returns structured JSON results. It was built as an individual university project and is the strongest backend/API case study in this portfolio.

The project acts like a small monitoring-style backend service rather than a simple CRUD app. A user can submit URLs, the API checks them, records the response result, stores the completed job in memory, and allows previous checks to be retrieved.

For the learning process and the trade-offs I encountered, read what I learned from building the Go REST API. The finished source code is available in the public GitHub repository.

Problem

Checking websites manually is slow and inconsistent. If several URLs need checking, each one has to be opened individually, waited on, interpreted, and recorded.

The goal was to create a backend API that could:

  • Accept multiple URLs in one request.
  • Validate user-submitted input.
  • Check websites automatically.
  • Handle slow or failing websites safely.
  • Return useful JSON results.
  • Store completed jobs for later retrieval.
  • Demonstrate testing, benchmarking, and concurrency.

API goals

The API was designed to be practical, testable, and easy to demonstrate. The main goals were:

  • Keep the API focused around clear REST endpoints.
  • Use Go’s standard library where possible.
  • Return predictable JSON responses.
  • Make URL checks concurrent because website requests are network-bound.
  • Apply timeouts so slow sites do not block the whole job.
  • Store completed check jobs in memory so results can be retrieved without adding unnecessary database complexity.
  • Add tests, benchmarks, and race detector evidence.

Endpoint table

Core endpoints exposed by the website health check API.
MethodEndpointPurpose
GET/healthConfirms the API is running.
POST/checksCreates and runs a new website health check job.
GET/checksLists completed check jobs.
GET/checks/{id}Retrieves a specific completed check job.
GET/statsReturns aggregate statistics from stored checks.

Example request

{
  "urls": ["https://example.com", "https://golang.org"],
  "timeout_ms": 3000
}

Example response shape

{
  "id": "chk_001",
  "created_at": "2026-05-20T12:00:00Z",
  "status": "completed",
  "urls": ["https://example.com", "https://golang.org"],
  "timeout_ms": 3000,
  "results": [
    {
      "url": "https://example.com",
      "success": true,
      "status_code": 200,
      "response_time_ms": 142,
      "checked_at": "2026-05-20T12:00:00Z"
    },
    {
      "url": "https://golang.org",
      "success": true,
      "status_code": 200,
      "response_time_ms": 231,
      "checked_at": "2026-05-20T12:00:00Z"
    }
  ],
  "summary": {
    "total": 2,
    "successes": 2,
    "failures": 0,
    "average_latency_ms": 186.5,
    "fastest_url": "https://example.com",
    "fastest_response_ms": 142,
    "slowest_url": "https://golang.org",
    "slowest_response_ms": 231,
    "timeout_count": 0,
    "status_classes": {
      "2xx": 2,
      "3xx": 0,
      "4xx": 0,
      "5xx": 0,
      "other": 0
    }
  }
}

Architecture

The project uses a small backend architecture:

Website health check request flow
  1. Step 1

    Client request

    A caller sends a health-check request with one or more URLs.

  2. Step 2

    HTTP handler

    The route receives the request and coordinates validation, checking and response handling.

  3. Step 3

    JSON decoding and validation

    The request body is decoded and URLs are checked for supported HTTP or HTTPS input.

  4. Step 4

    Concurrent URL checker

    Goroutines run network-bound URL checks in parallel with timeout handling.

  5. Step 5

    Result aggregation

    Individual outcomes are collected into a predictable response shape.

  6. Step 6

    In-memory storage and statistics

    Completed jobs are held in a mutex-protected map and used for aggregate stats.

  7. Step 7

    JSON response

    The API returns structured results, summaries, errors or stored job data as JSON.

This kept the project focused while still showing real backend concerns: routing, validation, JSON handling, concurrency, storage, and testing.

Data and storage approach

The project uses in-memory map-based storage rather than a database. This was a deliberate scope decision.

A database would make sense for a production monitoring tool, but for this project the goal was to prove backend API design, concurrency, validation, testing, and documentation without overbuilding the storage layer.

The in-memory store is protected with a mutex so shared data is not read and written unsafely when requests are handled concurrently.

Concurrency design

The biggest technical part of the project was checking multiple URLs concurrently.

Each submitted URL can be checked independently, so the project uses goroutines to start checks at the same time. A sync.WaitGroup waits until all checks finish before the API returns the final response.

The key benefit is that slow network-bound work does not have to happen one URL at a time. If one website is slow, other checks can still complete in parallel.

The implementation also needed to keep the JSON response predictable. The submitted URL order should remain understandable, and results need to be collected safely.

Timeout handling

Timeout handling was important because external websites can be slow, unreachable, or unreliable.

The project uses context.WithTimeout so outbound requests can be cancelled when they take too long. This prevents a single slow website from blocking the whole check job indefinitely.

Timeouts are recorded in the result so the user can see that a URL failed because it was too slow rather than because the API itself failed.

Validation and error handling

The API validates submitted URLs before checking them. It only accepts HTTP and HTTPS URLs, which prevents invalid schemes or malformed input from being treated as real website checks.

Validation helps keep the API predictable and safer to use. Instead of failing later in the request process, invalid input is rejected early with a clear response.

Testing and evidence

The project includes testing around the main backend behaviour:

  • Handler behaviour.
  • URL validation.
  • Website checker logic.
  • Stored job retrieval.
  • Statistics generation.
  • Concurrency safety checks.
  • Benchmark comparisons.

The project also uses Postman for repeatable API demonstrations. This is useful for employer review because the API can be shown through clear endpoint requests and JSON responses.

The public benchmark figures published with the project compare sequential and concurrent URL checking:

ImplementationBenchmark
Sequential103353040 ns/op
Concurrent24267751 ns/op

That was approximately a 4.3x speed-up in that project benchmark. This is a project-specific benchmark rather than a universal performance claim. Results depend on the tested URLs, network conditions, machine and benchmark setup.

The evidence is strongest as project-level technical proof rather than production proof. The repository documents tests, benchmark comparisons, race-detector/concurrency-safety checks and Postman demonstration material, but I am not presenting the API as a deployed monitoring service or claiming production uptime, traffic, security certification or employer results.

Challenges

The biggest challenge was learning how to structure concurrent code cleanly in Go.

The API needed to:

  • Start multiple checks at the same time.
  • Wait for all of them to complete.
  • Avoid unsafe shared memory access.
  • Keep responses consistent.
  • Handle slow external requests safely.
  • Keep the scope manageable.

Another challenge was deciding what not to build. A full monitoring dashboard, database, authentication, and deployment could all be future improvements, but adding them too early would distract from the backend/API learning goals.

Outcome

The result is a finished backend/API project with a public GitHub repo and clear portfolio value.

It demonstrates:

  • Go backend development.
  • REST API design.
  • JSON request and response handling.
  • Input validation.
  • Concurrent URL checking.
  • Timeout handling.
  • In-memory storage.
  • Testing and benchmarks.
  • Postman API evidence.
  • Technical decision-making.

The deliberate limitations are part of the evidence. The project stores jobs in memory, so data is not persistent across restarts. It is suitable as a focused backend/API case study, but a production monitoring tool would need persistent storage, deployment, authentication, rate limiting, alerting and operational hardening.

What I learned

This project helped me understand that backend development is not just about creating endpoints. It also involves handling unreliable input, unreliable external services, performance trade-offs, concurrency safety, and clear documentation.

I also learned that concurrency can improve performance for network-bound work, but it introduces extra responsibility. Shared state, ordering, cancellation, and testing all become more important.

Future improvements

Future improvements could include:

  • Adding a React or Next.js dashboard frontend.
  • Persisting check jobs in PostgreSQL or Supabase.
  • Deploying the API publicly.
  • Adding scheduled checks.
  • Adding authentication.
  • Adding rate limiting.
  • Adding email alerts for failed checks.
  • Improving the README with more screenshots, example responses, and benchmark evidence.

Interested in my software and web experience?

Get in touch, read the wider work history, or view the CV for a concise recruiter overview.