← Back to projects

Case study · Java REST API

URL Shortener & Analytics API

A Spring Boot REST API for creating short links, resolving them through HTTP 302 redirects, tracking clicks, managing expiration and deactivation, and persisting link state in PostgreSQL.

URL Shortener · redirect and analytics flow
01 · InputLong URLvalidated HTTP/HTTPS
→
02 · CodeSecureRandom7-character code
→
03 · DataPostgreSQLindexed short code
→
04 · AccessShort URLlookup by code
→
05 · HTTP302 Redirectoriginal destination
AnalyticsClick countlast accessed time
→
LifecycleExpirationscheduled cleanup
→
ControlDeactivatemanual API action
Redirect resolution checks active/expiry state, updates analytics transactionally, then returns the original URL for a 302 response.

The problem

Turn a long URL into a short, resolvable identifier while preserving link state and analytics.

The service needs to create unique short codes, resolve them quickly, reject expired or inactive links, update analytics, and keep lifecycle rules separate from HTTP handling.

REST API flow

Keep creation, redirect, analytics, and deactivation as clear API workflows.

ControllerHTTP requests
→
Servicelink workflows
→
RepositoryJPA lookup
→
PostgreSQLpersistent links
01

Create

POST /api/links validates a URL, generates a short code, applies optional expiry, and persists the link.

02

Redirect

GET /{code} resolves the link and returns an HTTP 302 redirect when the short link is valid and active.

03

Analytics & lifecycle

Separate endpoints return analytics and manually deactivate links, while a scheduler handles expired active links.

Short-code generation

Generate compact codes without relying on predictable sequential IDs.

The generator uses Java SecureRandom and a Base62-style alphabet containing 0-9, A-Z, and a-z. Each code is seven characters long.

SecureRandom

Each position is selected using Java's SecureRandom rather than a sequential counter.

Collision checking

Before persistence, the service checks whether the generated code already exists and retries when a collision is found.

Validation

Only HTTP/HTTPS URLs with a valid host are accepted before a short link is created.

Persistence & analytics

Optimize the read-heavy lookup path while keeping link lifecycle data together.

Indexed lookup

The JPA entity defines a unique database index on short_code, which is the key lookup used by redirect requests.

Transactional analytics

Resolution runs inside a Spring transaction while click count and last-access time are updated.

Expiration

An optional expiry timestamp is stored per link, and a Spring Scheduler task periodically marks expired active links inactive.

Manual deactivation is also supported through the REST API. Once a link is inactive or expired, redirect attempts are rejected.

Testing & delivery

Test service behaviour and package the API with its database environment.

The repository uses JUnit 5 and Mockito for service tests, including short-code generation/persistence and expired-link rejection. Docker and Docker Compose package the Spring Boot service with PostgreSQL, and the Docker build runs the Maven test/package lifecycle before producing the runtime image.

Concurrency & future scalability

The current click counter is intentionally simple, and that creates a clear next scaling problem.

The current implementation reads the entity, increments clickCount, and writes the changed state inside a transaction. Under heavier concurrent redirect traffic, a simple read-modify-write counter can lose increments unless the update strategy is made concurrency-safe. A production version could use an atomic database increment, move hot redirect lookups behind a cache, and process analytics asynchronously so redirects are not coupled to analytics writes.

Possible future improvements include a cache layer such as Redis, rate limiting, migrations, expanded integration tests, and asynchronous analytics. None of those are implemented in the current repository.

Back to portfolio

Explore all projects

View projects →