A modern, secure, and extensible URL shortener service built with FastAPI, SQLAlchemy (async), Alembic, and PostgreSQL.
This project demonstrates robust data migration workflows using Alembic for schema and data changes, SQLAlchemy for ORM mapping, and PostgreSQL as the database backend. The migration process is automated and version-controlled, making it easy to evolve your database schema safely.
Below is a visual representation of the Alembic migration history for this project:
<base>
|
|-- a9a344a9be94_first_commit
|
|-- b4d5b9d35e8d_add_default_to_created_at_and_updated_at
|\
| \
| |-- branch1_branch_from_b4d5b9d35e8d
| |-- d8157a9990d3_update_db
| /
|/
|-- c420bea9650f_merge_branch1_and_d8157a9990d3 (mergepoint)
Each migration file represents a step in the evolution of the database schema. The mergepoint shows where branches were merged together.
Suppose you want to split a username column into first_name and last_name columns. The migration steps are:
- Create new columns: Add
first_nameandlast_nameto theuserstable. - Migrate data: Use SQL in the Alembic migration to split the
usernameinto the new columns. - Drop old column: Remove the
usernamecolumn after data migration. - Update models: Change your SQLAlchemy models to match the new schema.
- Update application logic: Refactor authentication and user management to use the new fields.
def upgrade():
op.add_column('users', sa.Column('first_name', sa.String(length=56), nullable=True))
op.add_column('users', sa.Column('last_name', sa.String(length=56), nullable=True))
op.execute('''
UPDATE users
SET
first_name = CASE
WHEN POSITION(' ' IN username) > 0 THEN SUBSTRING(username FROM 1 FOR POSITION(' ' IN username) - 1)
ELSE username
END,
last_name = CASE
WHEN POSITION(' ' IN username) > 0 THEN SUBSTRING(username FROM POSITION(' ' IN username) + 1)
ELSE NULL
END
WHERE username IS NOT NULL;
''')
op.drop_column('users', 'username')class User(Base):
__tablename__ = 'users'
id = Column(String(512), primary_key=True, index=True)
email = Column(String(128), unique=True, index=True)
first_name = Column(String(56))
last_name = Column(String(56))
# ... other fields ...SELECT id, first_name, last_name FROM users;| Old Field | New Field(s) | Migration Logic |
|---|---|---|
| username | first_name, last_name | Split on first space, assign to first/last name |
| Unchanged | ||
| hash_password | hash_password | Unchanged |
- Problem: Long URLs are hard to share, remember, and manage. There is a need for a simple, secure, and user-friendly way to shorten URLs, track usage, and manage bookmarks.
- Solution: This project provides a robust API for user registration, authentication, bookmark (URL) creation, redirection, and analytics, with a focus on clean code, security, and extensibility.
- User registration and JWT-based authentication
- Create short URLs (bookmarks) for any valid URL
- Redirect to the original URL using the short code
- Track visit counts for each bookmark
- Get all bookmarks for a user
- Get, delete bookmarks by ID
- Async database operations for high performance
- CORS support and enhanced logging
- Backend: FastAPI, SQLAlchemy (async), Alembic
- Database: PostgreSQL
- Auth: JWT (JSON Web Tokens)
- Other: Pydantic, passlib (bcrypt), requests (for testing)
src/
src/
routes/
auth.py # Auth endpoints (register, login)
bookmarks.py # Bookmark CRUD endpoints
redirects.py # Redirection endpoint
models/ # SQLAlchemy models
schemas/ # Pydantic schemas
utils/ # Auth, shortener, helpers
main.py # FastAPI app entrypoint
alembic/ # DB migrations
requirements.txt # Python dependencies
-
Register a User
POST /auth/register- Body:
{ "username": ..., "email": ..., "password": ... } - Registers a new user. Returns user info.
-
Login
POST /auth/login- Body:
username,password(form data) - Returns JWT access token.
-
Create a Bookmark (Shorten URL)
POST /bookmarks/create- Headers:
Authorization: Bearer <token> - Body:
{ "original_url": "https://..." } - Returns bookmark info with short code.
-
Redirect
GET /<short_code>- Redirects to the original URL and increments visit count.
-
Get All Bookmarks
GET /bookmarks/get/all- Headers:
Authorization: Bearer <token> - Returns all bookmarks for the user.
-
Get Bookmark by ID
GET /bookmarks/get/{bookmark_id}- Headers:
Authorization: Bearer <token> - Returns bookmark details.
-
Delete Bookmark
DELETE /bookmarks/delete/{bookmark_id}- Headers:
Authorization: Bearer <token> - Deletes the bookmark if owned by the user.
-
Clone the repo and install dependencies:
git clone <repo-url> cd FASTAPI_SQL python -m venv venv venv\Scripts\activate # On Windows pip install -r requirements.txt
-
Configure your database in
src/config.py -
Run Alembic migrations:
alembic upgrade head
-
Start the server:
uvicorn src.main:app --reload
-
Visit the docs:
See test_login.py for a full example of registration, login, bookmark creation, and retrieval.
- The project is modular and ready for contributions.
- You can add features like custom domains, analytics, admin panel, etc.
- Please open issues or pull requests for suggestions and improvements.

