Deploying an API to Heroku
Deploys, where do they live? What do they eat? If you want to learn how to deploy something to Heroku you came to the right place!
What you will learn in this tutorial
- How to create a Flask application to run on Heroku
- How to deploy said app
- A little bit of Git
- A little bit of API consumption
Warnings: Of the two posts I used as a base to write this one, one of them was very outdated and the other, although recent, did not have instructions that correspond to the most recent version of Heroku, they are in the links section in case you want to read them.
Heroku who?
If youâve made it this far, you probably already know what Heroku is, but for those who donât know it, Heroku is a cloud platform that gives people âthe fastest way to go from idea to URLâ and without having headaches with the infrastructure part.
This platform provides a service that, based on a predefined structure of an application, manages to package that application and put it to run on a server in one of their data centers. Heroku accepts applications written in several languages, but today we are going to use Python.
Letâs code!
For this tutorial I made an API with the Python microframework called Flask. I wonât get into the details on how Flask works, but you can refer to the oficial documentation or look for one of the many tutorials available out there.
Our API
The API will only have one endpoint defined by /
route. This endpoint can give two responses depending on the request you make. They are as follows:
Request | Response |
---|---|
GET without extra headers | Donât panic! |
GET with Authorization 42 | the answer to life, the universe and everything |
The code to do this is short and straight forward, take a look:
import os
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route('/')
def dont_panic():
if request.headers.get('Authorization') == '42':
return jsonify({"42": "The answer to life the universe and everything"})
return jsonify({"message": "Don't panic!"})
if __name__ == "__main__":
port = int(os.environ.get("PORT", 5000))
app.run(host='0.0.0.0', port=port)
This code lives inside a file called dont_panic.py
in my project codebase, but you can name it as you see fit. In addition, this code has a peculiarity: the last three lines, they are the configuration used when the Flask server is running on Heroku.
Pipfile
I mentioned at the beginning of this post that the base posts I used were somewhat outdated? Well, nowadays Heroku requires a Pipfile
to prepare the environment in which our application will run. Pipfile will serve to install the Python libraries needed to run our application. And if like me, you donât like using Pipenv you can create the file by hand, as I created mine below:
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
[packages]
Flask = "*"
[requires]
python_version = "3.6"
For the development enviroment, I like to use the good old requirements.txt
which looks like this:
click == 6.7
Flask == 0.12.2
itsdangerous == 0.24
Jinja2 == 2.10
MarkupSafe == 1.0
Werkzeug == 0.14.1
Procfile
To run the API locally the command is as follows:
FLASK_APP=dont_panic.py flask run
As you wonât be inside the Heroku environment to input the command yourself to get your API up and running, youâll need some sort of configuration to tell Heroku how to run your application. The file that holds the command that runs your app is called Procfile.
One important detail is that this file does not have an extension, depending on your operational system and your text editor, you need to be careful when saving this file to avoid some extention like .txt
from appearing. Saving this file with an extention will cause a failure in your deployment.
For this application, Procfile will have only one line, which is the following:
web: python dont_panic.py
Okay, now what else?
Versioning
Since Heroku works with the Git versioning system, regardless of whether you host the code on GitHub or not, youâll need to define a code versioning history.
So, after creating all these files, you need to add all of that to your Git tree. If youâre not a big fan of Git, you can do the following:
$ git init
$ git add .
$ git commit -m "creating the application"
This commands above will gather all files in the same commit, this is not a good practice and if you are interested in good practices and learning more about Git, there will extra links for that in the links session at the end of the post.
3, 2, 1⊠Deploy \o/
Now you have a choice: there are a few ways to send your code to Heroku. You can connect with GitHub or Dropbox or use the Heroku CLI. In this post Iâll will show you how to do it using the Heroku command line.
First of all you need to install Heroku CLI.
Logging in
After installing the CLI, youâll need to login to your account
$ heroku login
This command will ask you to enter your Heroku account email and password:
Heroku CLI login screen
Note: I used the -i
here because Iâm running on a remote server, this will request user and password on the terminal instead of openning up a browser for the login
Next you need to create an app within Heroku to upload your code to.
Creating an application
Using the interface
It is possible to create an application through the website interface, where you will see the following screen:
You just need to input an unique name and then click the Create app
button, that will take you directly to the deploy tab of the app especific page. I created the app called dontpanicapi
, and if you scroll down youâll see steps to upload and deploy your app:
As you can see the last command displays the remote
we need to add to our repo, and basically you just have to run it, and youâll be able to publish the code:
heroku git:remote -a dontpanicapi
The output for the command above show be something like this:
Using the terminal
But we can also create the application using the Heroku CLI. The good thing about using the CLI is that when creating the application, remote
is created automatically:
heroku apps:create dontpanicapi
And that should output something like this:
One important thing here is that the name you choose for your application must be unique in Heroku, using the interface youâll get imiate feedback whether the name you chose is available or not. This is important because the name of your application is used to create the Heroku subdomain. If you try to create an application with a name that is already being used, you see an output to the command above like this this:
If you donât care about the name of your application, you can run the following command:
heroku create
And it will create an app with a random name:
After creating the application, you can take a peek at the Heroku dashboard in the applications area to see the list of your applications:
And finally deploy time!
After committing everything, logging in and creating your application on Heroku itâs deploy time!! đđđ
Deploying here means sending the application code to the server and running the process to get our API up on the Heroku instance.
Other contexts may bring variations and intermediate steps of the one I am presenting here, since as our application is simple and Heroku is made to help us get our applications up and running, the biggest job becomes the creation of the configuration files (which you know how to do now) đ
Then, the command that will actually send the code to the server is:
git push heroku master
If command is successful you should see something like this outputed to the terminal:
One last though, you may need to ensure that your application has at least one dyno running with the following command:
heroku ps: scale web = 1
Dynos
Now you maybe asking yourself, what is a dyno? A dyno is a Linux Container, Heroku uses a container model that isolates your application and allows for easy scalability of the system. In other, less technical words: Take all the code and configuration files weâve written so far and put them in a box and put them to run, that is a dyno.
Now to see if your API is actually working we can (in this case) open a browser and access https://dontpanicapi.herokuapp.com/
or use the command line to open app URL in the browser:
heroku open
If everything went well you will see something like this:
What happened was the following: when accessing the URL of the application in Heroku, the browser does a GET request for API with the standard header, that is, without an Authorization
field.
We could achieve the same result by running the following command on the terminal:
curl -X GET -k -i 'https://dontpanicapi.herokuapp.com/'
That gives me this result:
Now as we know, this API of ours will have a different response if you pass the Authorization
header with the value of 42
, so letâs make the request:
curl -X GET -k -H 'Authorization: 42' \
-i 'https://dontpanicapi.herokuapp.com/'
And checking out the result:
Can you pass the authorization header on the browser? You totally can! But Iâll explain how to do this that in another post/tutorial that I promise to link here when finish writing.
Pro tip: Log files
Letâs say that you have made a mistake in the process of configuring your application and, when deploying, Heroku warns you that it was unsuccessful attempt. In this case the first place to look for information about what went wrong is the logs.
A good system logs all actions performed. In this case, Heroku takes care of logging all the details for us, so letâs ask for the logs using the CLI. Here I still write the logs in a file to facilitate inspection, see:
heroku logs > out.log
Examples of lines you will find in one of these logs:
2021-02-15T00:56:46.708128+00:00 heroku[web.1]: Starting process with command `python dont_panic.py`
2021-02-15T00:56:50.825880+00:00 app[web.1]: * Serving Flask app "dont_panic" (lazy loading)
2021-02-15T00:56:50.825954+00:00 app[web.1]: * Environment: production
2021-02-15T00:56:50.826115+00:00 app[web.1]: WARNING: This is a development server. Do not use it in a production deployment.
2021-02-15T00:56:50.826237+00:00 app[web.1]: Use a production WSGI server instead.
2021-02-15T00:56:50.826326+00:00 app[web.1]: * Debug mode: off
2021-02-15T00:56:50.831184+00:00 app[web.1]: * Running on http://0.0.0.0:13487/ (Press CTRL+C to quit)
2021-02-15T00:56:51.000000+00:00 app[api]: Build succeeded
2021-02-15T00:56:51.851660+00:00 heroku[web.1]: State changed from starting to up
2021-02-15T00:58:45.102206+00:00 app[web.1]: 10.47.180.171 - - [15/Feb/2021 00:58:45] "GET / HTTP/1.1" 200 -
2021-02-15T00:58:45.103509+00:00 heroku[router]: at=info method=GET path="/" host=dontpanicapi.herokuapp.com request_id=9df2f453-77e4-4b63-94
30-c88e0a27206b fwd="186.209.20.97" dyno=web.1 connect=0ms service=7ms status=200 bytes=173 protocol=https
Now if someone says âDeploy the API!â you already know how to do it đ
PS.: If you have questions or comments, leave them below or send me a message I promise to try to answer, the DMs that are always open
Links
- Code I used in this post is available here
- Post by Diego Garcia: Publishing your Hello World on Heroku in Portuguese
- Text by John Kagga: Deploying a Python Flask app on Heroku
- What the Flask by Bruno Rocha in Portuguese
- Git for beginners
- Discussion on good Git practices on Stackoverflow
- The site Oh shit, git! with a few more top tips in Git