Recently, I have been exploring and experimenting with serverless databases and built a Telegram Bot to manage a to-do list with Fauna. So I decided to create something a bit complex this time around like a URL shortener.
A URL shortener is a service that shortens long web addresses making them easily shareable on social media. They work by generating a short identifier to represent a long web address. So, when a user submits the identifier to the platform, they are automatically redirected to the original URL.
In this article, we will be building a simple URL shortener using the Python programming language, Flask web framework, and the serverless database, Fauna. We will also be deploying our app to Heroku.
Why Use Fauna?
Fauna is a serverless document database that offers two interfaces, GraphQL and the Fauna Query Language (FQL). It can store collections, indexes, and even other databases (multi-tenancy). Here are a few reasons for using Fauna:
- Fauna is very easy to learn and integrate into projects.
- Increase in productivity as Fauna handles a lot of database operations under the hood for you.
- Fauna provides infinite scalability for your projects.
- Fauna allows you to model any data that is required by your business e.g relations, documents, graphs, or a combination.
- Fauna provides native GraphQL support for your database along with functionalities such as transactions, custom logic, and access control.
- Fauna databases have low-latency, so you can serve your users everywhere with consistent data that is being replicated in real-time to multiple regions and served from the closest locations to them.
Getting Started with Fauna
Step 1: Setting up our Fauna Database
The first thing we need to do is create the database for our app in our Fauna dashboard. If you have not created an account on Fauna before now, create one here: https://dashboard.fauna.com/accounts/register
Provide a name for your database then press the SAVE
button. The next thing we have to do is create a collection in our database. A collection is similar to SQL tables that contain data with similar characteristics e.g user collection that contain information about users in the database. We will start by pressing the NEW COLLECTION
button.
Then, we supply a name for the collection we will be creating and working with (we will be using urls
for this example). Once you have supplied this information proceed by pressing the SAVE
button.
We also need to create an index for our collection. A Fauna index allows us to browse through data that is stored in a database collection, based on specific attributes. To create one, navigate to the DB Overview
tab on the Fauna sidebar (left side of the screen) then click the NEW INDEX
button.
Supply the name you want to use for your index, set the terms to data.identifier
as we will be using that variable to reference our URLs later. Also, remember to select the Unique
attribute for our index, this ensures that we do not have a duplicate in our database entries.
Step 2: Generating Fauna API Key
We will need to create a Fauna API Key to connect to our database from our Python application. To do this, navigate to the security settings on the Fauna sidebar (left side of the screen).
Once you have done this, you will be presented with your API key (hidden here for privacy reasons). The key should be copied as soon as it is generated then stored somewhere you can easily retrieve.
Step 3: Integrating Fauna with Python
Next, we need to get the Python library for Fauna. It’s available on pip and can be installed with a single line in our terminal.
$ pip install faunadb
After this is installed, we are going to run the sample code provided in Fauna Python driver docs https://docs.fauna.com/fauna/current/drivers/python.html
from faunadb import query as q
from faunadb.objects import Ref
from faunadb.client import FaunaClient
client = FaunaClient(secret="your-secret-here")
indexes = client.query(q.paginate(q.indexes()))
print(indexes)
The code above shows how the Fauna Python driver connects to a database with its API key and prints the indexes associated with it. The result from running this code is similar to the image below.
Building our Shortener with Flask
Now that we have successfully integrated our Python script with Fauna, let’s get started with building our shortener with Flask. To keep things simple, we’ll be building our app as an API instead of a full-stack web application with HTML and CSS.
Step 1: Setting up our Flask Server
We need to install the flask library the same way we installed Fauna earlier using pip and our terminal.
$ pip install flask
Let’s get a basic server running in Flask that will display the text Hello World
when opened. Create a new project folder and a python file with the name app.py
and type the following code inside.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True)
When we run our app.py
file we should get a response similar to the image below
If you get this, you’re on the right track. Open the following in your browser http://127.0.0.1:5000
to access the app.
Step 2: Create the Shortener Function
This is a core part of our application, we need a function that generates a random string that will serve as our identifier for our shortened URLs.
import string
import random
def generate_identifier(n=6):
identifier = ""
for i in range(n):
identifier += random.choice(string.ascii_letters)
return identifier
We used the choice
method in the random
library to randomly select a letter provided by string.ascii_letters
variable then concatenated the results to generate a random string. When we run this function we should get results like the ones in the image below.
You might also want to look at the NewId
function function provided by Fauna which can be used to generate time-based random numbers that are guaranteed to be unique across the entire cluster of your database. Also, once generated they can never be generated a second time.
Step 3: Build the Shortener Endpoint
We have now created a function that is responsible for generating the identifiers, we need to create an endpoint in our web application that will take a web address as input, generate an identifier, save it in our database and give the user the shortened URL.
import string
import random
from flask import Flask, jsonify, request
from faunadb import query as q
from faunadb.objects import Ref
from faunadb.client import FaunaClient
app = Flask(__name__)
client = FaunaClient(secret="your-secret-here")
def generate_identifier(n=6):
identifier = ""
for i in range(n):
identifier += random.choice(string.ascii_letters)
return identifier
@app.route("/")
def home():
return "Hello World!"
@app.route("/generate/<path:address>/")
def generate(address):
identifier = generate_identifier()
client.query(q.create(q.collection("urls"), {
"data": {
"identifier": identifier,
"url": address
}
}))
shortened_url = request.host_url + identifier
return jsonify({"identifier": identifier, "shortened_url": shortened_url})
if __name__ == "__main__":
app.run(debug=True)
We used the create
method of the FQL client to store the original URL provided by the user along with the short identifier we generated in our database collection using client.query(q.create(q.collection(collection_name), data))
.
Here, we imported the jsonify
method from Flask which is used in REST APIs to return JSON data and the request
variable so we can access data our browser sent to the server along with its request. When we access this endpoint in our browser, we should get a response similar to this. We can also see our newly created document in our Fauna dashboard.
Step 4: Write the URL Redirection Logic
The only thing left is building the logic that will redirect the user to the original URL when it receives the shortened URL. For this to work, we would build an endpoint that will take the identifier as input, query our database then redirect the user to the original URL if it exists, or display a 404 page if it doesn’t.
@app.route("/<string:identifier>/")
def fetch_original(identifier):
try:
url = client.query(q.get(q.match(q.index("urls_by_identifier"), identifier)))
except:
abort(404)
return redirect(url["data"]["url"])
We imported 2 more methods from Flask (abort
and redirect
); abort
will stop the code that is currently running and trigger the response of a provided status code while redirect
will redirect a user to another web address, either on your server or an external one. The full code should resemble the one below.
import string
import random
from flask import Flask, jsonify, request, abort, redirect
from faunadb import query as q
from faunadb.objects import Ref
from faunadb.client import FaunaClient
app = Flask(__name__)
client = FaunaClient(secret="your-secret-here")
def generate_identifier(n=6):
identifier = ""
for i in range(n):
identifier += random.choice(string.ascii_letters)
return identifier
@app.route("/")
def home():
return "Hello World!"
@app.route("/generate/<path:address>/")
def generate(address):
identifier = generate_identifier()
client.query(q.create(q.collection("urls"), {
"data": {
"identifier": identifier,
"url": address
}
}))
shortened_url = request.host_url + identifier
return jsonify({"identifier": identifier, "shortened_url": shortened_url})
@app.route("/<string:identifier>/")
def fetch_original(identifier):
try:
url = client.query(q.get(q.match(q.index("urls_by_identifier"), identifier)))
except:
abort(404)
return redirect(url["data"]["url"])
if __name__ == "__main__":
app.run(debug=True)
Now that wraps up the whole code for our URL shortener service with Python and Fauna.
Deploying our Python App to Heroku
Deploying an application means you are uploading it online so it can be accessible to anyone from anywhere in the world. In this article, we would be deploying our Python application to a free and popular hosting platform called Heroku.
The prerequisites needed to work with Heroku is an account on Heroku, Heroku CLI, and GIT.
- Create a Heroku account here - https://signup.heroku.com/login
- Follow this guide to install Heroku CLI - https://devcenter.heroku.com/articles/heroku-cli
- Follow this guide to install GIT - https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
Step 1: Install Gunicorn
Gunicorn is a pure-Python HTTP server for WSGI applications. It allows you to run any Python application concurrently by running multiple Python processes within a single dyno. It provides a perfect balance of performance, flexibility, and configuration simplicity when deploying a web app to somewhere such as Heroku
$ pip install gunicorn
Step 2: Create a wsgi.py
file
from app import app
if __name__ == "__main__":
app.run()
A WSGI file is an entry to our Python application, create a file with the name wsgi.py
in the same folder as our app.py
and save the content above in it. Our file structure should look like this.
Step 3: Generate your requirements.txt
file
The next step is to generate your requirements.txt
file with either pip freeze
or pipreqs
. Whatever method you use, make sure gunicorn
is in your requirements file.
$ pip freeze > requirements.txt
OR
$ pip install pipreqs
$ pipreqs .
Our file structure and requirements.txt
should look like the image below.
Step 4: Create a Procfile
web: gunicorn wsgi:app
A Procfile
is used to specify commands that should be executed by a Heroku app when they are started. Create a file with the name Procfile
in the same folder as our app.py
and wsgi.py
then save the content above in it. Our file structure should look like this.
Step 5: Create our App in Heroku
Navigate to the Create new app
tab in your Heroku dashboard and you will be presented with a screen similar to the one below. Choose a name for your app and the region it should be deployed to.
Step 6: Deploy our App to Heroku
$ heroku login
$ git init
$ heroku git:remote -a fauna-shortener
$ git add .
$ git commit -m "deploying to heroku"
$ git push heroku master
Congratulations, we have successfully deployed our application to Heroku and it can be accessed from anywhere in the world with the web address that will be given to you by Heroku after deployment. Since the name of the app created in this tutorial is fauna-shortener
, its URL would be https://fauna-shortener.herokuapp.com/
Testing our URL Shortener
Now we have deployed our web app to Heroku, we can shorten URLs using the generate
endpoint.
Conclusion
A URL shortener is a service that takes a long URL and converts it to a shorter one. When the shortened link is visited, the user is redirected to the original URL. In this article, we used Fauna's serverless database to build a simple URL shortener. We also saw how easy it was to integrate Fauna with Python, got the chance to explore its CRUD functionalities, and finally deploy our app to Heroku.
The source code of the app is available on GitHub. If you have any questions, don't hesitate to contact me on Twitter: @LordGhostX
Top comments (3)
Interesting post. For the short string generation, you could have used the built-in Python secrets module and generate a URL safe string using
secrets.token_urlsafe(<length>)
.Thanks for this, I actually know this library and method but it skipped my mind when writing the article 😅😅😅
Try to create a account in accounts.sap.com - you can deploy your code in cloundfoundry .. -you can see in blogs.sap.com