Skip to content
This repository was archived by the owner on May 19, 2026. It is now read-only.

gftf2011/short-link-python

Repository files navigation

short-link-python

URL shortener API (FastAPI) back-end by Postgres and Redis, with a batch worker that pre-generates short codes into a dedicated Redis instance.

This code is for study purposes ONLY !

Architecture

Component Role
API Create and resolve short links; cache reads/writes via Redis
Postgres Durable storage (short_link table, partitioned by month)
Redis (cache) Hot path for redirects
Redis (codes) Pool of generated short codes consumed on create
Batch Fills the codes Redis from a counter (runs as a Compose service locally)
Proxy Nginx front door in local dev (:8080 → API :8000)

Requirements

  • Python 3.14+
  • Poetry
  • Docker + Docker Compose (recommended for local dev)

Quick start (recommended)

Bring up the full local stack via the Makefile:

make startlocal

This will:

  • Copy docker/dev/env.example.env (host tools use DATABASE_URL on 127.0.0.1:5432)
  • Build and start Postgres, both Redis instances, API, proxy, and batch (docker/dev/docker-compose.yml)
  • Wait for Postgres, then run alembic upgrade head and scripts/create_short_link_partitions.py --ahead 2 on the host

The API container entrypoint also runs migrations and partition creation before starting Uvicorn.

Stop and clean up:

make stoplocal

Install to run (tests, Alembic, scripts)

With the stack running:

poetry install

Ports

Service Default URL / port
Proxy (recommended) http://localhost:8080 (PROXY_PORT)
API (direct) http://localhost:8000 (API_PORT)
Postgres localhost:5432
Redis cache localhost:6379
Redis codes localhost:6380

API

Routes are under /v1. Use the proxy in local dev unless you need the API directly.

Health

GET /v1/health

curl -sS "http://localhost:8080/v1/health"

Returns {"status":"ok"} with HTTP 200.

Create a short link

POST /v1/shortlink

curl -sS -X POST "http://localhost:8080/v1/shortlink" \
  -H "content-type: application/json" \
  -d '{"url":"https://example.com","expires_at":"2026-12-31T00:00:00Z"}'

Optional fields:

  • expires_at — ISO-8601 UTC timestamp; defaults to ~2 weeks from now when omitted.
  • alias — custom short code (max 32 characters, base62 rules); when set, the code is taken from the request instead of the Redis codes pool.

Response envelope:

  • {"status":201,"data":{"code":"<code>"}} on success (default codes are 7 characters)
  • {"status":400,"data":{"error":"..."}} on validation failure

Example with a custom alias:

curl -sS -X POST "http://localhost:8080/v1/shortlink" \
  -H "content-type: application/json" \
  -d '{"url":"https://example.com","alias":"my-link"}'

Resolve a short link

GET /v1/shortlink/{code}

On success the API returns HTTP 302 with a Location header (not a JSON body). Errors return JSON:

  • {"status":400,"data":{"error":"..."}} for invalid codes
  • {"status":404,"data":{"error":"..."}} when not found
# Show status and Location without following the redirect
curl -sS -D - -o /dev/null "http://localhost:8080/v1/shortlink/ABC1234"
# Follow redirect in the browser or with curl -L
curl -sS -L "http://localhost:8080/v1/shortlink/ABC1234"

Database partitions

short_link is range-partitioned by created_at (monthly). Alembic defines create_short_link_partition(); creating partitions is a separate step:

make upgradelocal
poetry run python scripts/create_short_link_partitions.py --ahead 2

make startlocal and the API container entrypoint run both automatically. For a specific month:

poetry run python scripts/create_short_link_partitions.py --month 2026-05

Migrations (Alembic)

Apply all migrations:

make upgradelocal

Create a new autogenerated revision:

make revisionlocal m="describe change"

Tests

make unittest
make integrationtest
make e2etest

Integration and e2e tests use Testcontainers (TESTCONTAINERS_RYUK_DISABLED=true is set in the Makefile).

Formatting

make lintformat

Load / stress tests (Locust)

make loadtestcreate
make loadtestredirect
make stresstestcreate
make stresstestredirect

Targets the proxy at http://localhost:8080 by default (see locust/).

Infrastructure (Terraform)

Terraform runs in Docker via docker/terraform/docker-compose.yml (TF_WORKSPACE=staging):

Target Purpose
make tfsetupinit / tfsetupvalidate / tfsetupapply Shared setup (infra/setup)
make tfdeployinit / tfdeployvalidate / tfdeployapply App deploy (infra/deploy)

See infra/setup and infra/deploy for AWS resources (ECS, RDS, load balancer, EventBridge batch schedule, etc.).

About

URL shortener API (FastAPI) back-end by Postgres and Redis, with a batch worker that pre-generates short codes into a dedicated Redis instance.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors