Home

Awesome

<div align="center"> <h1><a href="https://github.com/IHosseini/Shortify"><b>Shortify</b></a></h1> <a href="https://www.python.org"> <img src="https://img.shields.io/badge/Python-3.8+-3776AB.svg?style=flat&logo=python&logoColor=white" alt="Python"> </a> <a href="https://github.com/psf/black"> <img src="https://img.shields.io/static/v1?label=code%20style&message=black&color=black&style=flat" alt="Code Style: black"> </a> <a href="https://github.com/pre-commit/pre-commit"> <img src="https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=flat" alt="pre-commit"> </a> </div>

Table of Contents

Introduction

Shortify is a fast, fully async and reliable URL shortener RESTful API built with Python and FastAPI framework. It uses the open source MongoDB database for storing shortened URLs data and implements user registration via OAuth2 JWT authentication.

Features

Requirements

Manual installation:

Using Docker:

Setup

1. Clone the repository

git clone https://github.com/IHosseini083/Shortify.git

2. Install dependencies

⚠️ Skip this step if you want to use docker for running the application.

You need to configure Poetry to place the virtual environment in the project directory. To do so, run the following command:

poetry config --local virtualenvs.in-project true

Then, install dependencies:

poetry install

If you're on Linux, you can just skip all the above steps and run the scripts/install script:

chmod +x scripts/install  # make the script executable
./scripts/install

3. Configure environment variables

You can see the table of all environment variables below. Those marked with * are required and MUST be set before running the application. Rest of them are optional and have default values.

Note: To set any of these environment variables below, you MUST prefix them with SHORTIFY_ and then set them in your shell or in a .env file placed at the root directory (e.g. you can copy .env.example to .env). For example, to set DEBUG environment variable, you can do the following:

export SHORTIFY_DEBUG=True

Also note that ALL environment variables are CASE SENSITIVE.

FastAPI Application

NameDescriptionDefaultType
PROJECT_NAMEProject name.Shortifystring
PROJECT_VERSIONProject version.Current version of the project.string
API_V1_STRAPI version 1 prefix.v1string
DEBUGDebug mode for development.Trueboolean
CORS_ORIGINSAllowed origins for CORS.An empty list to allow all origins.list of string
USE_CORRELATION_IDUse correlation ID middleware for logging.Trueboolean
UVICORN_HOST*Host address for uvicorn server.-string
UVICORN_PORT*Port number for uvicorn server.-integer

Logging

NameDescriptionDefaultType
LOG_LEVELA logging level from the logging module.INFOstring

MongoDB

NameDescriptionDefaultType
MONGODB_URIMongoDB connection URI.mongodb://db:27017/string
MONGODB_DB_NAMEMongoDB database name.shortifystring

Superuser

NameDescriptionDefaultType
FIRST_SUPERUSER*Username of the first superuser.-string
FIRST_SUPERUSER_EMAIL*Email of the first superuser.-string
FIRST_SUPERUSER_PASSWORD*Password of the first superuser.-string

Authentication

NameDescriptionDefaultType
ACCESS_TOKEN_EXPIRE_MINUTESAccess token expiration time in minutes.1440 (24 hours)integer
SECRET_KEYSecret key for signing JWT tokens.43 random characters generated by secrets module.string

Short URLs

NameDescriptionDefaultType
URL_IDENT_LENGTHLength of the shortened URL ID.7integer

4. Run the application

After setting all the required and optional environment variables in .env.example file, copy it to .env file so that it's usable both by Docker and Shortify app.

Run the following commands to start up the services:

Using Docker (Recommended)

This will start two containers, one for Shortify and another one for MongoDB.

docker compose up -d

Manually

An up & running MongoDB instance is required and SHORTIFY_MONGODB_URI environment variable must be handled accordingly because its default value is only compatible with Docker installation of the app.

python -m shortify

If the SHORTIFY_DEBUG environment variable is set to True, the application will be run in debug mode and will be reloaded automatically on code changes.

Now your application is available at http://{SHORTIFY_UVICORN_HOST}:{SHORTIFY_UVICORN_HOST}/.

Documentation and Usage

After running the application, you can access the OpenAPI (Swagger) documentation at /api/v1/docs endpoint.

Project Structure, Modifications and Best Practices

Structure of shortify folder containing main files and folders of the application is consistent and straightforward and just by looking at module names it gives you an idea of what's inside it!

./shortify
│    __init__.py
│    __main__.py        # Runs the development server
├─── app                # Primary app folder
│   │    main.py        # Contains FastAPI application and its settings
│   │    __init__.py    # Contains project version variable
│   ├─── api            # All API views/routes are here
│   ├─── core           # Core configs and utils for the application
│   ├─── db             # Database initialization and session (if needded)
│   ├─── middlewares    # ASGI middlewares for FastAPI application
│   ├─── models         # Database models
│   ├─── schemas        # Pydantic schemas
│   ├─── static         # Static files served at /static endpoint
│   ├─── utils          # Utilites used by the API

Creating new API routes

To create new API routes and add them to your main application, you need to create new fastapi.APIRouter instances or use the existing ones depending on the endpoint you need to implement. All API routers for API version one (v1) are located at shortify/app/api/v1/endpoints folder and then grouped together in shortify/app/api/v1/__init__.py file by including them in a separate fastapi.APIRouter instance that will be added to main app:

# shortify/app/api/v1/__init__.py
from fastapi import APIRouter

from shortify.app.api.v1.endpoints import auth, urls, users
from shortify.app.core.config import settings

router = APIRouter(prefix=f"/{settings.API_V1_STR}")
router.include_router(auth.router, prefix="/auth", tags=["Authentication"])
router.include_router(users.router, prefix="/users", tags=["Users"])
router.include_router(urls.router, prefix="/urls", tags=["URLs"])

Now let's say you want to add a new router for statistics about short URLs and then add it to your main app, this is how you're going to do it:

  1. Create new module named stats.py in shortify/app/api/v1/endpoints package.
  2. Create an fastapi.APIRouter instance in stats.py module.
# shortify/app/api/v1/endpoints/stats.py
from fastapi import APIRouter

router = APIRouter()
  1. E.g. Create an API route to get most visited short URLs in descending order
# shortify/app/api/v1/endpoints/stats.py
from fastapi import APIRouter
from typing import List
from shortify.app.models import ShortUrl

router = APIRouter()


@router.get("/most-visited")
async def get_most_visited_urls(skip: int = 0, limit: int = 10) -> List[ShortUrl]:
    # Sort URLs in descending order based on their views
    return await ShortUrl.find(skip=skip, limit=limit).sort(-ShortUrl.views).to_list()

You could also implement the route using class-based views decorator but since we don't have any dependencies or code duplications, this approach was simply enough for our use-case.

  1. Finally, add the newly created router to the main router for version one
# shortify/app/api/v1/__init__.py
from fastapi import APIRouter

from shortify.app.api.v1.endpoints import stats
from shortify.app.core.config import settings

router = APIRouter(prefix=f"/{settings.API_V1_STR}")
# Other routers omitted for brevity
router.include_router(stats.router, prefix="/stats", tags=["Statistics"])
  1. That's it! now you can see the created endpoint in your API docs.

FastAPI Best Practices

You want to extend the application with the best practices available? check out the below repositories:

Stack

Frameworks and technologies used in Shortify

License

This project is licensed under the terms of the GPL-3.0 license.

<p align="center">&mdash; ⚡ &mdash;</p>