Skip to content

Latest commit

 

History

History
396 lines (308 loc) · 13.5 KB

File metadata and controls

396 lines (308 loc) · 13.5 KB

ThreadBox REST API (FastAPI + MongoDB)

A simple URL shortener backend.

  • POST /api/v1/urls/shorten - submit a long URL, get back a short code + short URL
  • GET /{short_code} - redirects to the original long URL
  • GET /api/v1/urls/{short_code} - view metadata about a short URL (no redirect)
  • GET /health - health check
  • GET /docs - interactive Swagger UI (auto-generated by FastAPI)

Project Overview

image

Project structure

url-shortener/
├── app/
│   ├── main.py              # App creation, middleware, router wiring, DB lifecycle
│   ├── core/
│   │   ├── config.py        # Settings loaded from .env
│   │   └── database.py      # MongoDB (Motor) connection management
│   ├── models/
│   │   └── url.py           # DB document shape
│   ├── schemas/
│   │   └── url.py           # Request/response (Pydantic) schemas
│   ├── routers/
│   │   ├── url.py           # /api/v1/urls/* routes
│   │   └── redirect.py      # /{short_code} redirect route
│   ├── services/
│   │   └── url_service.py   # Business logic
│   ├── middleware/
│   │   └── logging.py       # Request logging middleware
│   └── utils/
│       └── shortcode.py     # Short code generator
├── .env                     # Template - copy to .env
├── docker-compose.yml
├── Dockerfile
└── pyproject.toml

Setup (MUST need Manual Changes)

1. Create your .env file

This project ships only an .env.example (safe to commit). You must copy it to a real .env file, which is git-ignored and holds your actual secrets.

cp .env.example .env

You generally do not need to change anything inside .env to run this locally with Docker Compose - the defaults already match each other (the Mongo username/password in MONGO_URI match MONGO_ROOT_USER / MONGO_ROOT_PASSWORD). But you should still open .env and understand it:

Variable What it means When to change it
BASE_URL Used to build the short URL returned to clients, e.g. http://localhost:8000/aZ3kD9 Change to your real domain when deploying (e.g. https://short.mydomain.com)
SHORT_CODE_LENGTH How many characters in each short code Increase (e.g. to 8) if you expect millions of URLs
MONGO_URI Full MongoDB connection string Change if you use MongoDB Atlas (cloud) or a different local setup - see below
MONGO_DB_NAME Database name inside MongoDB Usually fine as-is
MONGO_ROOT_USER / MONGO_ROOT_PASSWORD Credentials docker-compose uses to create the MongoDB root user Change these to your own password before deploying anywhere public. If you change them, you must also update MONGO_URI to match.
ALLOWED_ORIGINS CORS - which frontend origins can call this API Change from * to your actual frontend URL (e.g. http://localhost:3000) once you build a frontend

Important: if you change MONGO_ROOT_USER or MONGO_ROOT_PASSWORD, you must update the username/password portion of MONGO_URI to match, e.g.:

MONGO_ROOT_USER=my_new_user
MONGO_ROOT_PASSWORD=my_new_pass
MONGO_URI=mongodb://my_new_user:my_new_pass@mongodb:27017

2. Choose how you run MongoDB

You have 3 options - pick ONE:

Option A - Docker Compose (recommended, easiest): MongoDB runs automatically as a container. Keep MONGO_URI as:

MONGO_URI=mongodb://mongo_user:mongo_pass@mongodb:27017

(mongodb here is the docker-compose service name - not a typo, and not localhost, since the app talks to Mongo over the Docker network.)

Option B - MongoDB installed locally on your machine (no Docker for Mongo): Change MONGO_URI in .env to:

MONGO_URI=mongodb://localhost:27017

(No username/password needed if you haven't set up auth locally.)

Option C - MongoDB Atlas (free cloud database):

  1. Create a free cluster at https://www.mongodb.com/cloud/atlas
  2. Get the connection string Atlas gives you (looks like mongodb+srv://user:pass@cluster0.xxxxx.mongodb.net)
  3. Paste it into .env as MONGO_URI
  4. In Atlas, whitelist your IP address (Network Access tab) or allow access from anywhere (0.0.0.0/0) for local testing.

Running the project

Option 1: Docker Compose (recommended)

This starts both the API and MongoDB together.

docker compose up --build

Then visit:

To stop:

docker compose down

To stop AND delete the MongoDB data volume (fresh start):

docker compose down -v

Option 2: Run locally with uv (without Docker for the app)

You still need MongoDB running somewhere (see Option B or C above).

  1. Install uv if you don't have it:

    curl -LsSf https://astral.sh/uv/install.sh | sh

    (Windows: see https://docs.astral.sh/uv/getting-started/installation/)

  2. Install dependencies:

    uv sync
  3. Make sure your .env file exists (step 1 above) and MONGO_URI points to a MongoDB instance you can actually reach (e.g. mongodb://localhost:27017 if running Mongo locally, NOT mongodb://mongodb:27017 - that hostname only resolves inside docker-compose's network).

  4. Run the app:

    uv run uvicorn app.main:app --reload
  5. Visit http://localhost:8000/docs


Testing the API

1. Shorten a URL

curl -X POST http://localhost:8000/api/v1/urls/shorten \
  -H "Content-Type: application/json" \
  -d '{"original_url": "https://www.google.com/search?q=fastapi"}'

Response:

{
  "original_url": "https://www.google.com/search?q=fastapi",
  "short_code": "aZ3kD9",
  "short_url": "http://localhost:8000/aZ3kD9",
  "created_at": "2026-07-22T10:00:00Z"
}

2. Use the short URL (redirects)

Open in a browser, or:

curl -iL http://localhost:8000/aZ3kD9

3. Check metadata (click count, etc.) without redirecting

curl http://localhost:8000/api/v1/urls/aZ3kD9

Notes on design choices

  • motor is used as the async MongoDB driver so it plays nicely with FastAPI's async request handlers (no blocking calls).
  • Unique index on short_code is created automatically on startup, and the service layer retries a few times on the rare chance of a collision.
  • secrets (not random) generates short codes, since it's cryptographically secure and non-guessable.
  • Redirect status 307 (Temporary Redirect) is used instead of 301 (Permanent) so that browsers/tools don't aggressively cache the redirect forever - useful if you ever want to change where a short code points.

MongoDB Atlas setup — full steps

1. Create account

Go to https://www.mongodb.com/cloud/atlas/register. Sign up (Google/email). Free, no card required for free tier.

2. Create free cluster

  • Click Create / Build a Database
  • Select M0 Free tier
  • Provider: AWS/GCP/Azure — any, doesn't matter for this use case
  • Region: pick closest to where FastAPI Cloud runs your app (any is fine, latency difference is small)
  • Cluster name: default Cluster0 works, or rename
  • Click Create Deployment

3. Create database user

Prompted automatically after cluster creation, or go to Database Access (left sidebar):

  • Click Add New Database User
  • Authentication Method: Password
  • Username: pick one, e.g. url_shortener_app
  • Password: click Autogenerate Secure Password and copy it now — shown once
  • Database User Privileges: Read and write to any database
  • Click Add User

4. Network access — allow all IPs

Go to Network Access (left sidebar):

  • Click Add IP Address
  • Click Allow Access From Anywhere (fills in 0.0.0.0/0)
  • Click Confirm

Required because FastAPI Cloud egress IPs aren't fixed/listable. Auth (step 3) still gates access — this only controls which IPs can attempt a connection.

5. Get connection string

  • Go to Database (left sidebar) → click Connect on your cluster
  • Choose Drivers
  • Driver: Python, Version: 3.12 or later
  • Copy the string shown, format:
mongodb+srv://<username>:<password>@cluster0.xxxxx.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0
  • Replace <username> and <password> with values from step 3
  • If password has special characters (@, :, /, %), URL-encode them (e.g. @%40)

6. Set database name

Connection string above doesn't specify a database name. Add it after the host, before the ?:

mongodb+srv://url_shortener_app:yourpassword@cluster0.xxxxx.mongodb.net/url_shortener?retryWrites=true&w=majority&appName=Cluster0

This matches MONGO_DB_NAME=url_shortener from your .env.example. Database itself doesn't need pre-creation — MongoDB creates it on first write.

7. Test connection locally before deploying

cd url-shortener
cp .env.example .env

Edit .env, set:

MONGO_URI=mongodb+srv://url_shortener_app:yourpassword@cluster0.xxxxx.mongodb.net/url_shortener?retryWrites=true&w=majority&appName=Cluster0

Run:

uv run fastapi dev

Check terminal for Successfully connected to MongoDB log line (from app/core/database.py). If connection fails, error will show at startup — check password encoding and Network Access setting.

8. Push same value to FastAPI Cloud

uv run fastapi cloud env set --secret MONGO_URI "mongodb+srv://url_shortener_app:yourpassword@cluster0.xxxxx.mongodb.net/url_shortener?retryWrites=true&w=majority&appName=Cluster0"

--secret flag encrypts it, hides value in dashboard after creation.

Also set:

uv run fastapi cloud env set MONGO_DB_NAME "url_shortener"

(Redundant with database name in URI, but your config.py reads MONGO_DB_NAME separately — keep both in sync.)

9. Delete local .env before committing

rm .env

Already gitignored, but don't paste real credentials into any file you might commit.


Deploying to FastAPI Cloud

FastAPI Cloud (https://fastapicloud.com) is a managed hosting platform built by the FastAPI team. This project is already set up to work with it - fastapi[standard] is in the dependencies (this bundles the FastAPI Cloud CLI) and app/main.py exposes an app object, which is one of the standard locations FastAPI Cloud auto-detects.

1. Prerequisite: a FastAPI Cloud account

Sign up at https://fastapicloud.com if you haven't already. At the time of writing, new accounts may go through a short waitlist - the CLI will prompt you with a "Join the waiting list" option if so.

2. Handle MongoDB first (important - read this before deploying)

FastAPI Cloud hosts your FastAPI application process only - it does not run a MongoDB container alongside it the way docker-compose.yml does locally. You need a MongoDB instance reachable over the public internet. The easiest option is a free MongoDB Atlas cluster:

  1. Create a free cluster at https://www.mongodb.com/cloud/atlas
  2. Under Network Access, add 0.0.0.0/0 ("allow access from anywhere"). This is necessary because FastAPI Cloud's outbound IP addresses aren't fixed/predictable, so you can't allowlist a specific IP. (Your database still requires the username/password in the connection string - this setting only controls which IPs are allowed to attempt a connection.)
  3. Under Database Access, create a database user and password.
  4. Copy the connection string Atlas gives you - it looks like: mongodb+srv://<user>:<password>@cluster0.xxxxx.mongodb.net

3. Log in and deploy

From the project root:

uv run fastapi login

This opens your browser to authenticate.

uv run fastapi deploy

The CLI will package your code (respecting .gitignore and .fastapicloudignore), upload it, and build + deploy it. You'll get a URL like:

https://url-shortener.fastapicloud.dev

4. Set your real environment variables

Your local .env file is never uploaded (it's git-ignored), so you need to set the same variables in FastAPI Cloud. Do this via the CLI:

Manage secret/non-secret environment variables through the dashboard at

https://dashboard.fastapicloud.com

Click Your app (url-shortener-api) > Environment Variables tab > Import .env

After setting/changing environment variables, redeploy so the app picks them up:

uv run fastapi deploy

5. Verify it's live

https://url-shortener.fastapicloud.dev/health
https://url-shortener.fastapicloud.dev/docs

Check real-time logs any time from the dashboard: Apps → your app → Logs.

6. Connect your frontend to it

If you're using the Threadbox frontend from this same set of deliverables, update its js/config.js:

const CONFIG = {
  API_BASE_URL: "https://url-shortener.fastapicloud.dev",
};

And make sure ALLOWED_ORIGINS (step 4 above) matches wherever you deploy that frontend - otherwise the browser will block the requests with a CORS error even though the API itself works fine.

Notes

  • You don't need Dockerfile or docker-compose.yml for FastAPI Cloud - they're excluded via .fastapicloudignore and only needed for local Docker-based development. Keep using them locally if you like; both paths work off the same codebase.
  • fastapi deploy can be re-run any time you push changes - each run redeploys the current code.
  • For automatic deploys on every git push, see FastAPI Cloud's GitHub integration docs: https://fastapicloud.com/docs/source-control/github-integration