Build a URL shortener using FastAPI and Redis
In this post, we are going to build a simple URL shortening API using FastAPI and Redis. Redis is a key: value
based database, where everything is stored as key-value pairs(much like the python dictionary). Install Redis from its official website.
Start the Redis server before going any further(If you have installed Redis already!)
redis-server /etc/redis/6379.conf
Import all necessary things
import uuid
import redis
from fastapi import FastAPI
from pydantic import BaseModel
from starlette.responses import RedirectResponse
- uuid for generating random strings
- redis for connecting to our Redis server
- FastAPI is the app
- BaseModel for data validation
- RedirectResponse for redirecting to original URL
Create the app and redis client
app = FastAPI()
r = redis.Redis()
Create a test endpoint
@app.get("/")
def read_root():
return {"message": "Welcome to url shortening app"}
Run the app using uvicorn main:app --reload
. Now navigate to http://localhost:8000
to see our endpoint working successfully.
Define the URL shortening endpoint
class Item(BaseModel):
url: str
custom_target: str = None
@app.post("/")
def shorten_url(item: Item):
url = item.url
if r.get(url) is None:
new_name = item.custom_target or str(uuid.uuid4())[-6:]
if r.mset({url: new_name}):
return {"url": url, "short": r.get(url)}
else:
return {"message": "failed"}
return {"message": "URL already exists", "short": r.get(url)}
POST "/" should receive data in the specified format. A URL of type string and a custom short name (Optional). In case you didn't provide this, the program will create one for you(of length 6). We use mset
(you could also use set
) to set values in Redis. All values must be of dictionary type. You can retrieve any value using get
. Both set
and get
returns None
if not found.
So if I post data as {"url":"google.com", "custom_target": "amal"}, Redis will save this as {"google.com": "amal"}.
Define the redirect endpoint
@app.get("/{short}")
def redirect_url(short: str):
for key in r.keys():
if r.get(key).decode("utf8") == short:
return RedirectResponse(url=key.decode("utf8"))
return {"message": "URL not defined"}
This will search for the short URL in our database. After a successful URL is created, navigate to http://localhost:8000/<short_url>
, It'll redirect you to the original URL.
Define an endpoint to retrieve all URLs in database
@app.get("/get")
def get_all_urls():
data = []
for key in r.keys():
data.append({key.decode("utf8"): r.get(key).decode("utf8")})
return data
All the returned values should be decoded as they're binary encoded. Simply use
string.decode('utf8')
.VERY IMPORTANT
/get
should be defined before/{short}
, else FastAPI will readget
as a string and return "URL not defined".
Test the endpoints
❯ curl "http://localhost:8000/"
{"message":"Welcome to url shortening app"}%
❯ curl -X POST "http://localhost:8000/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"url\":\"https://youtube.com\"}"
{"message":"URL already exists","short":"4fb5bb"}%
❯ curl -X POST "http://localhost:8000/" -H "accept: application/json" -H "Content-Type: application/json" -d "{\"url\":\"https://gmail.com\"}"
{"url":"https://gmail.com","short":"f5ae2a"}%
❯ curl "http://localhost:8000/get"
[{"https://gmail.com":"f5ae2a"},{"https://youtube.com":"4fb5bb"},{"https://google.com":"amal"}]%
Complete Code: here
Improvements
This is just an example program. You need a lot of improvements if you're deploying this into production. You can add a nice frontend using vue/react. Use docker to deploy as it can help scale when deployed to GAE or any other services. URL shortening apps are used mainly because of the easiness and the analytics they provide, therefore add a lot of analytics😉.</short_url>
No Comments Yet