Building Faunabin: A Pastebin Clone with Python and Fauna
Tags: fauna, webdev, serverless, python
Published: 2021-01-19 18:02:37

In the past couple of months, I've been building applications with [Fauna](https://fauna.com/) such as [a Telegram bot](https://dev.to/lordghostx/building-a-telegram-bot-with-python-and-fauna-494i) and a [URL shortener](https://dev.to/lordghostx/build-and-deploy-a-serverless-url-shortener-with-python-and-fauna-3077). It got me interested in building more, especially since Fauna makes it very easy to configure and manage serverless databases. So, I decided to explore more about this database and build something a little more complex.

In this article, we will be building an online text-pasting tool similar to [Pastebin](https://pastebin.com) using Python, Flask, and [Fauna](https://fauna.com). We will also be deploying our app to [Heroku](https://heroku.com).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1750lp0lo00h5qkqac5w.png)

# Creating Faunabin User Interface

We are going to build the user interface for Faunabin with [Flask-Bootstrap](https://pythonhosted.org/Flask-Bootstrap/) which is an extension that allows for easy integration of [Bootstrap](https://getbootstrap.com) into Flask applications.

We will be building two web pages for Faunabin. The first will have a text box where users can paste their notes (paste page), while the second page will enable users to view what they pasted (result page).

## Step 1: Install Required Libraries

We need to install both Flask and Flask-Bootstrap libraries via the Python Package Manager (pip).

```
$ pip install flask
$ pip install flask-bootstrap
```

## Step 2: Set Up Our Flask Server

Open your text editor, create a file with the name app.py, then save the code below in it.

```python
from flask import Flask, render_template
from flask_bootstrap import Bootstrap

app = Flask(__name__)
Bootstrap(app)

@app.route("/")
def index():
    return render_template(“index.html”)

if __name__ == "__main__":
    app.run(debug=True)
```

## Step 3: Build Our Paste Page

The paste page is where users are provided with a text box to paste whatever they wish, with options to tweak their paste (just like the image above).

First, create a folder named `templates` in the same folder as our `app.py`. A templates folder is used by Flask to store its HTML files which will be rendered by the server in later parts of the application. Our project folder should resemble the image below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/rz4ussg5j95jffyza14p.png)

Create another file named index.html which will be stored inside the templates folder and save the code below in it.

```html
{% extends "bootstrap/base.html" %}
{% block title %}Faunabin: Paste text and share with your friends{% endblock %}

{% block content %}
<div class="jumbotron text-center">
  <h1>Faunabin</h1>
  <h3>Paste text and share with your friends</h3>
</div>

<div class="container">
  <form method="POST">
    <div class="form-group">
      <h4>Faunabin Paste Title</h4>
      <input type="text" class="form-control" name="title" required>
    </div>
    <div class="form-group">
      <textarea class="form-control" name="paste-text" rows="20" required>
      </textarea>
    </div>
    <div class="form-group text-center">
      <button type="submit" class="btn btn-lg btn-primary">Create New Paste</button>
    </div>
  </form>
</div>
{% endblock %}
```

When we run our `app.py` file we should get a response similar to the image below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1hgz60n7uvcgr52ozp94.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d45jw9nx8cim8i1fqiw1.png)

## Step 4: Build Our Result Page

The result page is where users view pastes that have been created on Faunabin. First, create a file named `paste.html` in the `templates` folder and save the code below in it:

```html
{% extends "bootstrap/base.html" %}
{% block title %}Paste Title{% endblock %}

{% block content %}
<div class="jumbotron text-center">
  <h1>Faunabin</h1>
  <h3>Paste text and share with your friends</h3>
</div>

<div class="container">
  <form>
    <div class="form-group">
      <h4>View Faunabin Paste</h4>
      <textarea class="form-control" rows="20" readonly>
      </textarea>
    </div>
    <div class="form-group text-center">
      <a href="{{ url_for('index') }}"><button type="button" class="btn btn-lg btn-primary">Create Another Paste</button></a>
    </div>
  </form>
</div>
{% endblock %}
```

When this template is rendered we should get a result similar to the image below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1at4wwtw9f4t4z9jekig.png)

At this point, our project folder structure should resemble the image below:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/x7eboykl3eanpga5sobp.png)

# Getting Started with Fauna

## Step 1: Set Up Our Fauna Database

The first thing we need to do is create the database for Faunabin in our Fauna dashboard. If you have not created an account on Fauna before now, create one here: [https://dashboard.fauna.com/accounts/register](https://dashboard.fauna.com/accounts/register?utm_source%3DDevTo%26utm_medium%3Dreferral%26utm_campaign%3DWritewithFauna_PublicBookmark_FGiuliani)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/fp860wb6ly7opft0c6t6.png)

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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/5kl3rjomnwhgwpo0jvmd.png)

Then, we supply a name for the collection we will be creating and working with (we will be using `pastes` for this example). Once you have supplied this information proceed by pressing the `SAVE` button.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/xtcj8ylxppspgad726j0.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hixbd13ubzzx2cyxugtz.png)

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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/l01zt7g351hrpkwgyt3b.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/p0vmpiqf0as9o8d8b9wd.png)

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 pastes 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: Generate Fauna API Key

We will need to create a Fauna API Key to connect to our database from Faunabin. To do this, navigate to the security settings on the Fauna sidebar (left side of the screen).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cbjtvpc57epnast6w2sr.png)
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/nhitcz8e0ceuejuqeakd.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/64pwziju2y88sbqveddg.png)

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 it.

## Step 3: Integrate Fauna into 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>

```python
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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8ze86a7lkpaxzbrxfdxm.png)

# Integrating Fauna with Faunabin

Now that we have successfully integrated our Python script with Fauna, let’s get started with integrating Fauna with the user interface of Faunabin we created earlier.

## Step 1: Creating New Pastes

We need to take the `paste text` and `paste title` from the form in the index page, generate a unique identifier, then save it in Fauna. Our updated `app.py` should resemble the one below:

```python
import pytz
from datetime import datetime
from secrets import token_urlsafe
from flask import Flask, render_template, request, redirect
from flask_bootstrap import Bootstrap
from faunadb import query as q
from faunadb.objects import Ref
from faunadb.client import FaunaClient

app = Flask(__name__)
Bootstrap(app)
client = FaunaClient(secret="your-secret-here")


@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        title = request.form.get("title").strip()
        paste_text = request.form.get("paste-text").strip()

        identifier = token_urlsafe(5)
        paste = client.query(q.create(q.collection("pastes"), {
            "data": {
                "identifier": identifier,
                "paste_text": paste_text,
                "title": title,
                "date": datetime.now(pytz.UTC)
            }
        }))

        return redirect(request.host_url + identifier)
    return render_template("index.html")


if __name__ == "__main__":
    app.run(debug=True)
```

We used the `create` method of the FQL client to store the paste text and title as provided by the user along with paste identifier and creation date in our database collection using `client.query(q.create(q.collection(collection_name), data))`.

Here, we imported a couple of functions from flask so we can read form data, generate unique identifiers, and redirect users to the saved paste page. We can see our newly created document in our Fauna dashboard when we create a new paste.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/597yp999xwnwitjnb3kp.png)

## Step 2: Reading Old Pastes

Since we generated a unique identifier to store each paste submitted by users on Faunabin, we need to create a route in Flask that will take an identifier as input then fetch the related paste and render the data to the user. Our updated code should resemble the one below:

```python
import pytz
from datetime import datetime
from secrets import token_urlsafe
from flask import Flask, render_template, request, redirect, abort
from flask_bootstrap import Bootstrap
from faunadb import query as q
from faunadb.objects import Ref
from faunadb.client import FaunaClient

app = Flask(__name__)
Bootstrap(app)
client = FaunaClient(secret="your-secret-here")


@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        title = request.form.get("title").strip()
        paste_text = request.form.get("paste-text").strip()

        identifier = token_urlsafe(5)
        paste = client.query(q.create(q.collection("pastes"), {
            "data": {
                "identifier": identifier,
                "paste_text": paste_text,
                "title": title,
                "date": datetime.now(pytz.UTC)
            }
        }))

        return redirect(request.host_url + identifier)
    return render_template("index.html")


@app.route("/<string:paste_id>/")
def render_paste(paste_id):
    try:
        paste = client.query(
            q.get(q.match(q.index("paste_by_identifier"), paste_id)))
    except:
        abort(404)

    return render_template("paste.html", paste=paste[“data”])


if __name__ == "__main__":
    app.run(debug=True)
```

We used the `get` method of the FQL client to fetch the paste data we saved in Fauna earlier by specifying the paste ID and index we want to use with `client.query(q.get(q.match(q.index(index_name), search_query)))`.

We are also going to update our `paste.html` we stored in our `templates` folder to render the data we are passing accordingly:

```html
{% extends "bootstrap/base.html" %}
{% block title %}{{ paste.title }}{% endblock %}

{% block content %}
<div class="jumbotron text-center">
  <h1>Faunabin</h1>
  <h3>Paste text and share with your friends</h3>
</div>

<div class="container">
  <form>
    <div class="form-group">
      <h4>View Faunabin Paste</h4>
      <textarea class="form-control" rows="20" readonly>
      {{ "\n" + paste.paste_text }}
      </textarea>
    </div>
    <div class="form-group text-center">
      <a href="{{ url_for('index') }}"><button type="button" class="btn btn-lg btn-primary">Create Another Paste</button></a>
    </div>
  </form>
</div>
{% endblock %}
```

Now that wraps up the whole code for Faunabin which is a Pastebin clone built with Python and Fauna.

# Deploying Faunabin to Heroku

Deploying an application means you are uploading it online so it can be accessible to anyone, anywhere in the world. In this article, we would be deploying Faunabin 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](https://heroku.com).

```
$ pip install gunicorn
```

## Step 2: Create a WSGI File

```python
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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/owi6bzwi1zk6mrxs2lux.png)

## Step 3: Generate Requirements 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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/8x6up31u1n2ybfzmep80.png)

## 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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lugup59c9e6eksyvfpti.png)

## Step 5: Create a Heroku Application

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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jgkusarkfc6m4f7xt8fp.png)

## Step 6: Deploy Faunabin to Heroku

```
$ heroku login
$ git init
$ heroku git:remote -a faunabin
$ git add .
$ git commit -m "deploying to heroku"
$ git push heroku master
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d6lm6tkigay94wb4fwxh.png)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/3v7bxcr9d75frz78oudf.png)

Congratulations! We have successfully deployed Faunabin to Heroku and it can be accessed anywhere in the world with the web address that was given by Heroku after deployment. Since the name of the app created in this tutorial is `faunabin`, its URL would be <https://faunabin.herokuapp.com>

# Conclusion

In this article, we built an online text-pasting tool similar to [Pastebin](https://pastebin.com) with Fauna's serverless database and Python called Faunabin. We saw how easy it is to integrate Fauna into a Python application, got the chance to explore its CRUD functionalities, and finally deployed Faunabin to Heroku.

The source code of [Faunabin](https://faunabin.herokuapp.com) is available on [GitHub](https://github.com/LordGhostX/faunabin/). If you have any questions, don't hesitate to contact me on Twitter: [@LordGhostX](https://twitter.com/LordGhostX)