Building A URL Shortener Application
Tags: python, django, webdev, heroku
Published: 2021-08-10 11:15:49

Welcome to my first of many #DEV posts!

## Table of Contents

* [Introduction](#intro)
* [Development](#dev)
* [Deployment](#deploy)
* [Lessons](#lessons)

## Introduction <a name='intro'></a>

As part of my dev growth, I thought I'd build an application that ticked the following boxes:
- functionality: it should serve a specific purpose rather than just be a dummy
- usefulness: users should derive some meaningful merit from it 
- instructiveness: it should be to me, an opportunity to learn and improve my software engineering skills

Of course, there are other needs—in addition to the ones aforementioned—that applications should fulfil, but I reasoned it best to focus on those three for now. It resulted in me building a [**URL Shortener App**](https://weburlshortener.herokuapp.com/) that is scalable and ultimately, satisfying.

This post is not a "How To" guide on recreating this application; rather,  it's a summary of the project, and the documentation of the things I learnt—or more aptly, the things I learnt and still remember😅.

### The Stack
The core aspects of the project as well as the elements associated with them are summarized as follows:
- Programming Language - Python
- Backend - Django
- Frontend - Django (HTML/CSS and Bootstrap)
- Database - PostgreSQL
- Hosting - Heroku

You can find the repository for this project on my [GitHub](https://github.com/Wolemercy/url-shortener). You can check out the application [here](https://weburlshortener.herokuapp.com/).

## Development <a name='dev'></a>
Django uses the Model-View-Template (MVT) pattern which is only slightly different from the general  Model-View-Controller (MVC) pattern because Django takes care of the Controller aspect. The Models, Views and Templates are essentially the building blocks of the project as they define how your app functions and interacts with the user.

### The Model
The Django model provides an Object-relational Mapping (ORM) to the database. The models you define are mapped to tables in your database and they can all be connected. This [**URL Shortener App**](https://weburlshortener.herokuapp.com/) uses a model called `Shortener`. Its fields are as seen in the snippet below. The save method is called to update the database with the shortened URL associated with the user's original URL.

{% gist https://gist.github.com/Wolemercy/3079bf61211a3cc528f24bf4ea86fe40 file=urlshortener.py %}

### The Utility Function
An utility function is used to generate the shortened URL as seen below. It checks if the created URL is unique  and only returns the generated code once that condition has been satisfied.

{% gist https://gist.github.com/Wolemercy/3079bf61211a3cc528f24bf4ea86fe40 file=urlshortener_utility_function.py %}

### The Form
Forms are the receptacles of a user's input and they define the actions and methods (POST, GET) that get sent to the server. The `Form` class is what handles forms in Django. It accepts specifics such as form fields, layouts, valid values, and so forth. More on that can be read [here](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms). Forms can be inherited from Models and that's the road more travelled. It was also the road I took.

{% gist https://gist.github.com/Wolemercy/3079bf61211a3cc528f24bf4ea86fe40 file=urlshortener_form.py %}

### Views
The Views in Django are what take a user's web request and returns a response. Two basic view functions were used; one received the user's original URL and returned the shortened link while the other was responsible for redirecting the shortened link to the destination of the original URL.

In defining the views, I also -
- defined the logic that monitors the number of times the shortened link has been followed
- ensured that shortened links were only created if the user's URL wasn't currently existing in the database. If a record of the URL existed, the shortened URL associated with that record will simply be returned.

{% gist https://gist.github.com/Wolemercy/3079bf61211a3cc528f24bf4ea86fe40 file=urlshortener_views.py %}


### Templates
These are files that define the structure of the HTML pages associated with the rendered views. Two templates were used—the base template and the home template. The former defines the general boilerplate  html code to be inherited by other more specific templates like the home template. The templates were written in HTML and the Django Template Language. The former reprised its role as the standard markup language for web pages while the latter made it possible to extend templates, insert conditionals, and call URLs associated with the application.

### Tests
This was my first experience writing tests for an application. Thankfully, the Django documentation proved really helpful. You can learn more about Django tests [here](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Testing) and [here](https://docs.djangoproject.com/en/3.2/topics/testing/). I wrote tests for  the forms, models, and views. Below is a snippet of the unit tests written for the forms.

{% gist https://gist.github.com/Wolemercy/3079bf61211a3cc528f24bf4ea86fe40 file=urlshortener_forms_test.py %}

## Deployment <a name='deploy'></a>
The [**URL Shortener App**](https://weburlshortener.herokuapp.com/) was deployed using Heroku's free tier cloud service. It was a bit tricky but all I needed to do was follow the documentation as seen [here](https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Deployment), and it was fine in the end. Heroku is well integrated with git so deploying and even redeploying after making some changes is not so complex.

Furthermore, by default, Django makes use of an sqlite database which Heroku does not directly support. There are a few workarounds to that solves that problem but I ended up using a PostgreSQL database even in my local development environment. I could have done without that, yes. However, I wanted to learn how to incorporate a different database into an application and it was interesting doing exactly that.

### Opportunities for Improvement
For the next iteration of this project, I intend to add a couple of functionalities including (and probably beyond):
- showing users the links they've created
- allowing users delete their created links
- dockerizing the application
- automating the tests
- making the UI a bit better 


## Lessons <a name='lessons'></a>
I have categorized the critical things I learnt and the "big" challenges I faced into two; code-related and non-code-related.

### Code-related Lessons
These lessons specifically have to do with new approaches to solving problems I discovered (I'm not Christopher Columbus, I know) in the course of this project. Some, you may already know, but are, to me, worthy of note.

### Windows Terminal is the Truth
I use a Windows environment and for a long time, Git Bash was sufficient to fulfill my command-line needs. In deploying the application to Heroku, however, I struggled with adding a SECRET_KEY to my application environment. It turned out that the ampersand (&) character which was part of my SECRET_KEY  was a  reserved character and it was not getting parsed as intended. I was stuck for a few hours trying to find a way around it and I tried different solutions as seen on the web to no avail. Single quotes, double quotes, backslashes, etc. Nothing worked.

Then I decided to change my command-line environment from Git Bash to Windows Terminal and that did the trick. Using a PowerShell profile in Windows Terminal, I only had to wrap the ampersand character—and the close-parenthesis character,")"—in double quotes and the entire string in single quotes to get it to work. Honestly, I'm not quite sure why I stayed away from Windows Terminal for so long but I'll be using it here on out. It's customizable; it can be integrated with CMD, PowerShell and Linux environments; it supports "Ctrl + C" and "Ctrl + V" as the default copy and paste commands unlike Git Bash; and it also looks nice. 

### Python Decouple for Environment Variables
In the past, I've always approached accessing environment variables by:
- creating a JSON file and storing the environment variables therein
- writing a a few lines of code in my settings.py file to read the JSON file
- accessing each variable in the JSON file

However, in the course of this project, I picked up on using the Python Decouple library to access my secret keys. Once installed with pip, I only needed to:
- create an `.env` file and store the environment variables
- import the config object from decouple in my settings.py file
- access each variable in the `.env` file

In both methods, it is necessary to include the JSON and `.env` files in a `gitignore` file. However, the latter  is a lot quicker to implement than the former as it saves me a couple of lines, and going forward, it is definitely the way to go.

### Non-Code-related Lessons
These lessons aren't particularly linked to code but they were important things to note.

### Sometimes, it is okay to fail a test
If you'd told me in high school that I'd one day look forward to failing a test, I would have punched you in the face to snap you out of that illusion. Okay, maybe that's going a bit too far but you catch the drift. Being optimistic and more so, delighted about a failed test is not exactly a "natural" emotional state. 

However, while writing tests for this [**URL Shortener App**](https://weburlshortener.herokuapp.com/), some were specifically designed to fail. The successes of these tests were hinged on their failure. The tests had to fail for them to pass, for lack of a less ironic expression. This was for me, a rather poignant reminder that failure is not inherently a bad thing. For all the stigma and unenviable disrepute it continues to garner, failure, sometimes, is okay.

### Front-end isn't bread and butter
"It's just styling". Eh, no it's not! At least not for me. Giving the [**URL Shortener App**](https://weburlshortener.herokuapp.com/) a good interface wasn't the easiest of things for me. From the layout and the elements to the fonts and colors, it was a bit difficult. I refer more to deciding on what to do rather than the implementation. For instance, choosing the color scheme for the site was  a bit frustrating. If you think my final decision isn't so good, wait till you see what my many iterations looked like. For this bit, I'd appreciate any resource or tips that could help improve the choice of colors and page styling in general.

### I have a thing for this thing called `Code`
I thoroughly enjoyed building this App. I'm not exactly a code newbie but I'm green to Software Engineering, and it is, for  me, an exciting frontier. I look forward to building even better products, and learning from and collaborating with other Engineers.

Thanks for reading!

Let me know what you think about this [**URL Shortener App**](https://weburlshortener.herokuapp.com/)!

Also connect with me:

- LinkedIn: [Wolemercy](https://www.linkedin.com/in/wolemercy)
- Twitter:  [Wolemercy](https://twitter.com/wolemercy)