DEV Community

Cover image for Simple URL shortener app in Python for Beginners (🔥 FastAPI Demo)
Rahul Yarragodula
Rahul Yarragodula

Posted on

5 4

Simple URL shortener app in Python for Beginners (🔥 FastAPI Demo)

Introduction

This is a simple url shortener application developed in Python FastAPI.

Click here to access the Github repo.

Click here to access Postman API collection.

In this tutorial we will focus mainly on 3 things

  • It's beginner friendly.
  • Focused on industry best practices.
  • Deploy to cloud.

The scope of this tutorial is to focus mostly on building APIs rather than Python basics. If you're completely new to Python I would highly encourage you to have a strong knowledge on Python basics before going into this article.

Prerequisites

  • Download and install VS code from here
  • Download and install Python from here

What is a Virtual environment ?

A virtual environment is an isolated environment used to keep your project dependencies independently from any other projects or system configurations. This helps to avoid dependency conflicts with different versions.

Live demo

click here to test live endpoints

Develop url shortener APIs

Setup

  • Open the VS code and create a new file named main.py in the root folder.
  • This main.py file is responsible for running your application and acts as an entry point for your app.
  • Create a Python virtual environment using this command python -m venv virtual_env_name
  • Activate your virtual environment with respective operating system commands.
# Linux
source virtual_env_name/bin/activate

# Windows
virtual_env_name\Scripts\activate.bat

#Mac
source virtual_env_name/bin/activate
Enter fullscreen mode Exit fullscreen mode
  • Install all the required dependencies in the Python virtual environment.
# add fast api package
pip install fastapi

# add web server for running fast api
pip install "uvicorn[standard]"

# add package for cleaning cache files
pip install pyclean
Enter fullscreen mode Exit fullscreen mode
  • Add below snippet to the main.py file. This snippet has two endpoints of request method type GET. One endpoint returns {"hello" : "world"} json response and other returns the dynamic parameter you passed as item_id

    from fastapi import FastAPI
    from url_shortener import router
    # Create the app
    app = FastAPI()
    @app.get("/")
    def read_root():
    return {"Hello": "World"}
    @app.get("/items/{item_id}")
    def read_item(item_id: int):
    return {"item_id": item_id}
  • Run the application using the below command. The default host url is http://localhost:8000

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode
  • Open the default endpoint and if you see something like this, then you have successfully 🔥 deployed your application locally.

sample-fastapi-url.PNG

  • Similar to GET methods you can define any supported request methods you need.
  • Remove the two methods you have added i.e read_root, read_item and it's finally time to implement the actual url shortener endpoints.

Create url shortener endpoints.

  • Create the below files and directories in the root folder.
# folder : 
url_shortener

# files
url_shortener/__init__.py
url_shortener/database.py
url_shortener/router.py
url_shortener/models.py
Enter fullscreen mode Exit fullscreen mode
  • __init__.py file is used for representing the current folder as a package. So that it can be referenced in other packages.
  • router.py file is responsible for handling url shortener routes.
  • models.py is a common file for defining all the necessary models for url shorteners.
  • database.py file is responsible for handling database operations. Since there are hundreds of database options available on the internet and for the purpose of simplicity, here we mimic database operations with sleep commands and store the data in memory variables.

Define models

  • Below is the snippet for models.py. As you can see in the code, It has 2 models.
  • CreateUrlShortener model is responsible for validating creation of short URLs.
  • CreateUrlShortenerResponse model is used as a json response model.
    from pydantic import BaseModel
    # Define the model for the URL
    class CreateUrlShortener(BaseModel):
    url: str
    # Config for pydantic to validate the data in the request body
    class Config:
    orm_mode = True
    # Define the model for the URL Response
    class CreateUrlShortenerResponse(BaseModel):
    # short_url is the short url generated by the server
    short_url: str
    url: str
    # Config for pydantic to validate the data in the request body
    class Config:
    orm_mode = True
    view raw models.py hosted with ❤ by GitHub

Define database operations

  • Below is the snippet for the database.py file. It has a member variable all_data which stores all the data in memory. Here you can define your respective database instance instead of the in memory variable. As well as it has 3 functions for creating a short url, deleting a short url and fetching all the short urls.
  • This snippet has well defined comments to help you understand the logic in detail.
    import time
    class MockDBOperations:
    '''
    Created a class to mimic the database operations.
    You can use this class to implement the actual database operations.
    '''
    # Initialize the database
    def __init__(self):
    # Here I used the dictionary to store the data.
    # You can use actual database connection to store the data.
    self.all_data = {}
    # Add the data to the database
    async def add_data_to_db(self, url : str , short_url : str) -> bool :
    # added a sleep to simulate the database operation
    time.sleep(0.2)
    try:
    # Check if the url already exists in the database
    if url in self.all_data:
    # If the url already exists, return False
    return False
    else:
    # If the url does not exist, add it to the database
    self.all_data[short_url] = url
    return True
    except:
    return False
    # Delete the data from the database
    async def delete_data_from_db(self, short_url : str) -> bool :
    # added a sleep to simulate the database operation
    time.sleep(0.2)
    try:
    # Check if the url already exists in the database
    if short_url in self.all_data:
    # If the url already exists, delete it from the database
    del self.all_data[short_url]
    return True
    else:
    # If the url does not exist, return False
    return False
    except:
    return False
    # Get the data from the database
    async def fetch_all_data(self) -> dict :
    # added a sleep to simulate the database operation
    time.sleep(0.2)
    # Return the data
    return self.all_data
    view raw database.py hosted with ❤ by GitHub

Create endpoints

  • Here is the snippet for the router.py file.
  • In this we have defined an api router named url_shortener and database instance named mock_db_operations.
  • url_shortener is used for mounting these endpoints to the main router.
  • mock_db_operations is used for performing db operations.
  • It has 3 methods defined for creating, deleting, listing endpoints and 1 method for testing url redirects in action. Use the inline comments to understand the logic in detail.
    import secrets
    import string
    from fastapi import APIRouter
    from url_shortener.models import CreateUrlShortener, CreateUrlShortenerResponse
    from url_shortener.database import MockDBOperations
    from starlette.responses import RedirectResponse
    # Create the router
    url_shortener = APIRouter()
    # Create the database
    mock_db_operations = MockDBOperations()
    # Create the short url
    # This function is used to generate the short url
    # returns the CreatedUrlShortenerResponse
    @url_shortener.post("/create", response_model=CreateUrlShortenerResponse)
    async def create(shortner : CreateUrlShortener):
    # Generate a random string of 7 characters
    short_url_length = 7
    # Generate a random string of 7 characters
    res = ''.join(secrets.choice(string.ascii_uppercase + string.digits)
    for i in range(short_url_length))
    # Convert the url to string
    short_url = str(res)
    # Add the url to the database
    status = await mock_db_operations.add_data_to_db(url=shortner.url, short_url=short_url)
    # If the url is added to the database, return the short url
    if status:
    return CreateUrlShortenerResponse(short_url=short_url, url=shortner.url)
    else:
    # If the url is not added to the database, return the error message
    return CreateUrlShortenerResponse(short_url="", url="")
    # Get the urls from the database
    @url_shortener.get("/list", response_model=list[CreateUrlShortenerResponse])
    async def list():
    # Get the data from the database
    data = await mock_db_operations.fetch_all_data()
    # Create a list of CreateUrlShortenerResponse
    arr = []
    # Loop through the data
    for key, value in data.items():
    # Add the data to the list
    arr.append(CreateUrlShortenerResponse(short_url=key, url=value))
    # Return the list
    return arr
    # Delete the url from the database
    @url_shortener.delete("/delete/{short_url}")
    async def delete_short_url(short_url : str):
    # Delete the url from the database
    status = await mock_db_operations.delete_data_from_db(short_url = short_url)
    # If the url is deleted from the database, return the status
    if status:
    return {"message": "Successfully deleted"}
    else:
    # If the url is not deleted from the database, return the error message
    return {"message": "Failed to delete"}
    # Redirect the user to the original url
    @url_shortener.get("/test/{short_url}")
    async def test(short_url : str):
    # Get the url from the database
    data = await mock_db_operations.fetch_all_data()
    # Check if the url exists in the database
    if short_url in data:
    # redirect to this url
    url = data[short_url]
    # return the redirect response
    response = RedirectResponse(url=url)
    return response
    else:
    # return the error message
    return {"message": "Failed to fetch"}
    view raw router.py hosted with ❤ by GitHub

Update main file

  • Below is the snippet for the main.py file which has the url router mounted i.e url_shortener.
    from fastapi import FastAPI
    from url_shortener import router
    # Create the app
    app = FastAPI()
    # Add the router
    app.include_router(router.url_shortener, prefix="/url-shortener")
    view raw main.py hosted with ❤ by GitHub

Run the application locally using below command

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode
  • Use this postman collection as API specs and test the endpoints.
  • Below are the few snapshots of API requests and responses in action using Postman.

Create url shortener snapshot

create-short-url.PNG

List all short urls

list-short-urls.PNG

Test redirects in action

test-redirect.PNG

Delete short URLs

delete-short-url.PNG

Deploy to cloud

Know how to deploy this repo to Heroku ? Click here to find out right way

Summary

Awesome 🔥, you have successfully completed this tutorial. I would 💝 to hear your feedback and comments on the great things you're gonna build with this. If you are struck somewhere feel free to comment. I am always available.

Please find the complete code at github

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

Cloudinary image

Optimize, customize, deliver, manage and analyze your images.

Remove background in all your web images at the same time, use outpainting to expand images with matching content, remove objects via open-set object detection and fill, recolor, crop, resize... Discover these and hundreds more ways to manage your web images and videos on a scale.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay