How I built Primely — a URL Shortner
Author: Elliot Otoijagha
Date: Tuesday 19 May 2026
Subscribe to get new articles delivered straight to your inbox
How I built Primely — a URL Shortner
May 19, 2026
A Little Backstory
I have wanted to learn backend development since last year, but I had just started college and the adjustment was taking a real mental toll, so picking up something new on top of that was not realistic. I finally started in February this year. I already had confidence in my frontend skills, and learning backend felt like the natural next step toward becoming the kind of software engineer I want to be.
I already knew Python — it was actually my first language — so picking it back up was not hard. The next decision was choosing a framework. I considered Django but it felt too feature-heavy for where I was and what I needed. That is not a knock on Django; it is great at what it does. I just did not want to be managing things I was not going to use. I went with FastAPI. From what I had heard, it is fast — benchmarks put it close to Go — and it has a clean, approachable learning curve. I started with a YouTube video, did not get much from it, then moved to the official FastAPI tutorial docs and followed those instead. By the end I had a small CRUD app with authentication and I was satisfied — for about 24 hours — before I decided I needed to build something real.
That is how Primely was born.
What Is Primely?
Primely is a URL shortener with safety scanning built in. Users register, submit a long URL with an expiry period, and get back a short link — but only after the URL has been scanned and cleared by Google Safe Browsing API. The stack is FastAPI on the backend, PostgreSQL for the database, and TanStack Start on the frontend.
Authentication
The authentication system uses JWT tokens with a refresh token rotation strategy. On a successful login or registration, the client receives an access token — stored in memory via Zustand — and a refresh token sent as an HttpOnly cookie. The access token expires after 15 minutes; the refresh token lasts 7 days.
When the access token expires, the frontend quietly calls the refresh endpoint. On success, a new access token and refresh token are issued, and the old refresh token is marked as revoked in the database, preventing reuse.
This part took me the longest by far. I had no prior knowledge of how refresh token rotation was supposed to work end-to-end, and I was doubting if it was really necessary. It is also the part I am most proud of getting right.
How Primely Shortens a URL
When an authenticated user submits a URL, it is saved to the database with a status of `scanning`. Every 10 seconds, a background scheduler picks up the first 500 links with a status of `scanning` or `failed` — failed meaning a previous scan attempt did not complete — and sends them as a batch to the Google Safe Browsing API. The API returns any URLs it identifies as malicious, and each link's status is then updated to either `active`, `malicious`, or `failed`.
On the frontend, the app polls the server every 5 seconds until all of the user's links have left the `scanning` or `failed` state.
For the scheduler I used APScheduler, an in-process solution, instead of the more conventional Redis and Celery setup. For a project at this scale, adding two extra services felt like unnecessary overhead. APScheduler handled the requirement cleanly.
Once a URL is marked active, a short code is generated — the first 8 characters of the MD5 hash of the URL combined with a salt — and the short link is ready to use.
The Redirect Flow
When someone clicks a short link, the server looks up the short code, checks that the link is active and has not expired, then issues a redirect to the original URL. If the link is expired or was flagged as malicious, the user gets an appropriate error page instead.
Analytics
Every click triggers a FastAPI background task that records the click location, IP address, and user agent to the database. Nothing complex, but enough to give users a clear picture of their link performance.
What I Learned
The auth system taught me the most. I went from barely understanding JWT flows to implementing token rotation with revocation — that was not a small gap to close. The Safe Browsing integration also pushed me to think about background processing and what the right tool for the job actually is, rather than just reaching for whatever is most popular.
If I were to build this again, I would probably introduce a proper task queue from the start. APScheduler works, but as the number of links scales, the batch approach will start to show its limits.
Primely is just a project I built to learn backend properly, and it has already done what I needed it to do — prove to myself that I can build a complete backend system from scratch.
As time goes on, I have no choice but to become better.
You can check it out here: (https://primelyyy.vercel.app)
And here's my portfolio link shortened by Primely
(https://primelyyy.vercel.app/r/be53b237)
Subscribe to get new articles delivered straight to your inbox