<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Heroku</title>
    <description>The latest articles on DEV Community by Heroku (@heroku).</description>
    <link>https://dev.to/heroku</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F123%2F38b10714-65da-4f1d-88ae-e9b28c1d7a5e.png</url>
      <title>DEV Community: Heroku</title>
      <link>https://dev.to/heroku</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/heroku"/>
    <language>en</language>
    <item>
      <title>Yes! I Can Finally Run My .NET Application on Heroku!</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Wed, 19 Feb 2025 19:18:36 +0000</pubDate>
      <link>https://dev.to/heroku/yes-i-can-finally-run-my-net-application-on-heroku-2nak</link>
      <guid>https://dev.to/heroku/yes-i-can-finally-run-my-net-application-on-heroku-2nak</guid>
      <description>&lt;p&gt;Heroku now officially supports .NET!&lt;/p&gt;

&lt;p&gt;.NET developers now have access to the &lt;a href="https://github.com/heroku/heroku-buildpack-dotnet" rel="noopener noreferrer"&gt;officially supported buildpack for .NET&lt;/a&gt;, which means you can now deploy your .NET apps onto Heroku with just one command: &lt;code&gt;git push heroku main&lt;/code&gt;. 🤯 Gone are the days of searching for Dockerfiles or community buildpacks. With official support, .NET developers can now run any .NET application (version 8.0 and higher) on the Heroku platform.&lt;/p&gt;

&lt;p&gt;Being on the platform means you also get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple, low friction deployment&lt;/li&gt;
&lt;li&gt;Scaling and service management&lt;/li&gt;
&lt;li&gt;Access to the add-on ecosystem&lt;/li&gt;
&lt;li&gt;Security and governance features for enterprise use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Intrigued? Let’s talk about what this means for .NET developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Matters for .NET Developers
&lt;/h2&gt;

&lt;p&gt;In my experience, running an app on Heroku is pretty easy. But deploying .NET apps was an exception. You could deploy on Heroku, but there wasn’t official support. One option was to wrap your app in a Docker container. This meant creating a Dockerfile and dealing with all the maintenance that comes along with that approach. Alternatively, you could find a third-party buildpack; but that introduced another dependency into your deployment process, and you’d lose time trying to figure out which community buildpack was the right one for you.&lt;/p&gt;

&lt;p&gt;Needing to use these workarounds was unfortunate, as Heroku’s seamless deployment is supposed to make it easy to create and prototype new apps. Now, with official buildpack support, the deployment experience for .NET developers is smoother and more reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits of .NET on Heroku
&lt;/h2&gt;

&lt;p&gt;The benefits of the new update center around simplicity and scalability. It all begins with &lt;strong&gt;simple deployment&lt;/strong&gt;. Just one &lt;code&gt;git&lt;/code&gt; command… and your deployment begins. No need to start another workflow or log into another site every time; just push your code from the command line, and Heroku takes care of the rest.&lt;/p&gt;

&lt;p&gt;Heroku’s &lt;a href="https://devcenter.heroku.com/articles/dotnet-heroku-support-reference" rel="noopener noreferrer"&gt;official .NET support&lt;/a&gt; currently includes C#, Visual Basic, and F# projects for .NET and ASP.NET Core frameworks (version 8.0 and higher). This means that a wide variety of .NET projects are now officially supported. Want to deploy a Blazor app alongside your ASP.NET REST API? You can do that now.&lt;/p&gt;

&lt;p&gt;Coming into the platform also means you can &lt;strong&gt;scale&lt;/strong&gt; as your app grows. If you need to add another service using a different language, you can deploy that service just as easily as your original app. Or you can easily scale your dynos to match peak load requirements. This scaling extends to Heroku’s ecosystem of &lt;strong&gt;add-ons&lt;/strong&gt;, making it easy for you to add value to your application with supporting services while keeping you and your team focused on your core application logic.&lt;/p&gt;

&lt;p&gt;In addition to simple application deployment, the platform also supports more advanced &lt;strong&gt;CI/CD and DevOps&lt;/strong&gt; needs. With &lt;a href="https://devcenter.heroku.com/articles/pipelines" rel="noopener noreferrer"&gt;Heroku Pipelines&lt;/a&gt;, you have multiple deployment environment support options and can set up &lt;a href="https://devcenter.heroku.com/articles/github-integration-review-apps" rel="noopener noreferrer"&gt;review apps&lt;/a&gt; so code reviewers can access a live version of your app for each pull request. And all of this integrates tightly with GitHub, giving you automatic deployment triggers to streamline your dev flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Let’s do a quick walk-through on how to get started. In addition to your application and Git, you will also need the &lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt;Heroku CLI&lt;/a&gt; installed on your local machine. Initialize the CLI with the &lt;code&gt;heroku login&lt;/code&gt; command. This will take you to a browser to log into your Heroku account:&lt;/p&gt;

&lt;p&gt;Once you’re logged in, navigate to your .NET application folder. In that folder, run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ heroku create
~/project$ heroku buildpacks:add heroku/dotnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you’re ready to push your app! You just need one command to go live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ git push heroku main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! For simpler .NET applications, this is all you need. Your application is now live at the app URL provided in the response to your &lt;code&gt;heroku create&lt;/code&gt; command. To see it again, you can always use &lt;code&gt;heroku info&lt;/code&gt;. Or, you can run &lt;code&gt;heroku open&lt;/code&gt; to launch your browser at your app URL.&lt;/p&gt;

&lt;p&gt;If you can’t find the URL, log in to the &lt;a href="https://dashboard.heroku.com" rel="noopener noreferrer"&gt;Heroku Dashboard&lt;/a&gt;. Find your app and click on &lt;strong&gt;Open app&lt;/strong&gt;. You’ll be redirected to your app URL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwi3asshwgujj9tfve6y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkwi3asshwgujj9tfve6y.png" alt="Image description" width="273" height="55"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have a more complex application or one with multiple parts, you will need to define a Procfile, which will tell Heroku how to start up your application. Don’t be intimidated! Many Procfiles are just a couple lines. For more in-depth information, check out the &lt;a href="https://devcenter.heroku.com/articles/getting-started-with-dotnet#define-a-procfile" rel="noopener noreferrer"&gt;Getting Started on Heroku with .NET guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now we’ve got another question to tackle…&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Should Care?
&lt;/h2&gt;

&lt;p&gt;The arrival of .NET on Heroku is relevant to anyone who wants to deploy scalable .NET services and applications seamlessly.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;solo devs and startups&lt;/strong&gt;, the platform’s low friction and scaling takes away the burden of deployment and hosting. This allows small teams to focus on building out their core application logic. These teams are also not restricted by their app’s architecture, as Heroku supports both large single-service applications as well as distributed microservice apps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enterprise teams&lt;/strong&gt; are poised to benefit from this as well. .NET has historically found much of its adoption in the enterprise, and the addition of official support for .NET to Heroku means that these teams can now combine their .NET experience with the ease of deploying to the Heroku platform. Heroku’s low friction enables rapid prototyping of new applications, and &lt;a href="https://devcenter.heroku.com/articles/scaling" rel="noopener noreferrer"&gt;Dyno Formations&lt;/a&gt; make it easier to manage and scale a microservice architecture. Additionally, you can get governance through &lt;a href="https://www.heroku.com/enterprise" rel="noopener noreferrer"&gt;Heroku Enterprise&lt;/a&gt;, enabling the security and controls that larger enterprises require.&lt;/p&gt;

&lt;p&gt;Finally, &lt;strong&gt;.NET enthusiasts&lt;/strong&gt; from all backgrounds and skill levels can now benefit from this new platform addition. By going with a modern PaaS, you can play around with apps and projects of all sizes, hassle-free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;That’s a brief introduction to official .NET support on Heroku! It’s now easier than ever to deploy .NET applications of all sizes to Heroku. What are you going to build and deploy first? Let me know in the comments!&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>webdev</category>
      <category>dotnet</category>
      <category>programming</category>
    </item>
    <item>
      <title>5 Simple Steps to Get Your Test Suite Running in Heroku CI</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Wed, 26 Jun 2024 13:27:49 +0000</pubDate>
      <link>https://dev.to/heroku/5-simple-steps-to-get-your-test-suite-running-in-heroku-ci-5326</link>
      <guid>https://dev.to/heroku/5-simple-steps-to-get-your-test-suite-running-in-heroku-ci-5326</guid>
      <description>&lt;p&gt;So, I’ve always thought about Heroku as just a place to run my code. They have a CLI. I can connect it to my GitHub repo, push my code to a Heroku remote, and bam… it’s deployed. No fuss. No mess.&lt;/p&gt;

&lt;p&gt;But I had always run my test suite… somewhere else: locally, or with CircleCI, or in GitHub Actions. How did I not know that Heroku has CI capabilities? You mean I can run my tests there? Where have I been for the last few years?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53fvyc6bnbyt482votrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F53fvyc6bnbyt482votrq.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So that’s why I didn’t know about Heroku CI…&lt;/p&gt;

&lt;p&gt;CI is pretty awesome. You can build, test, and integrate new code changes. You get fast feedback on those code changes so that you can identify and fix issues early. Ultimately, you deliver higher-quality software.&lt;/p&gt;

&lt;p&gt;By doing it in Heroku, I get my test suite running in an environment much closer to my staging and production deployments. And if I piece together a &lt;a href="https://devcenter.heroku.com/articles/pipelines" rel="noopener noreferrer"&gt;pipeline&lt;/a&gt;, I can automate the progression from passing tests to a staging deployment and then promote that staged build to production.&lt;/p&gt;

&lt;p&gt;So, how do we get our application test suite up and running in Heroku CI? It will take you 5 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write your tests&lt;/li&gt;
&lt;li&gt;Deploy your Heroku app&lt;/li&gt;
&lt;li&gt;Push your code to Heroku&lt;/li&gt;
&lt;li&gt;Create a Heroku Pipeline to use Heroku CI&lt;/li&gt;
&lt;li&gt;Run your tests with Heroku CI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We’ll walk through these steps by testing a simple Python application. If you want to follow along, you &lt;a href="https://github.com/capnMB/heroku-ci-demo" rel="noopener noreferrer"&gt;can clone my GitHub repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our python app: is it prime?
&lt;/h2&gt;

&lt;p&gt;We’ve built an API in Python that listens for GET requests on a single endpoint: /prime/{number}. It expects a number as a path parameter and then returns true or false based on whether that number is a prime number. Pretty simple.&lt;/p&gt;

&lt;p&gt;We have a modularized function in is_prime.py:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def is_prime(num):
    if num &amp;lt;= 1:
        return False
    if num &amp;lt;= 3:
        return True
    if num % 2 == 0 or num % 3 == 0:
        return False
    i = 5
    while i * i &amp;lt;= num:
        if num % i == 0 or num % (i + 2) == 0:
            return False
        i += 6
    return True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then, our main.py file looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from fastapi import FastAPI, HTTPException
from is_prime import is_prime

app = FastAPI()

# Route to check if a number is a prime number
@app.get("/prime/{number}")
def check_if_prime(number: int):
    return is_prime(number)
    raise HTTPException(status_code=400, detail="Input invalid")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="localhost", port=8000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That’s all there is to it. We can start our API locally (python main.py) and send some requests to try it out:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~$ curl http://localhost:8000/prime/91
false

~$ curl http://localhost:8000/prime/97
true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That looks pretty good. But we’d feel better with a unit test for the is_prime function. Let’s get to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #1: Write your tests
&lt;/h2&gt;

&lt;p&gt;With pytest added to our Python dependencies, we’ll write a file called test_is_prime.py and put it in a subfolder called tests. We have a set of numbers that we’ll test to make sure our function determines correctly if they are prime or not. Here’s our test file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;from is_prime import is_prime

def test_1_is_not_prime():
    assert not is_prime(1)

def test_2_is_prime():
    assert is_prime(2)

def test_3_is_prime():
    assert is_prime(3)

def test_4_is_not_prime():
    assert not is_prime(4)

def test_5_is_prime():
    assert is_prime(5)

def test_991_is_prime():
    assert is_prime(991)

def test_993_is_not_prime():
    assert not is_prime(993)

def test_7873_is_prime():
    assert is_prime(7873)

def test_7802143_is_not_prime():
    assert not is_prime(7802143)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;When we run pytest from the command line, here’s what we see:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ pytest
=========================== test session starts ===========================
platform linux -- Python 3.8.10, pytest-8.0.2, pluggy-1.4.0
rootdir: /home/michael/project/tests
plugins: anyio-4.3.0
collected 9 items                                                                                                                                                                                            

test_is_prime.py .........                                                                                                                                                                             [100%]

============================ 9 passed in 0.02s ============================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our tests pass! It looks like is_prime is doing what it’s supposed to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #2: Deploy your Heroku app
&lt;/h2&gt;

&lt;p&gt;It’s time to wire up Heroku. Assuming you have a Heroku account and you’ve installed the Heroku CLI, creating your Heroku app is going to go pretty quickly.&lt;/p&gt;

&lt;p&gt;Heroku will look in our project root folder for a file called requirements.txt, listing the Python dependencies our project has. This is what the file should look like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fastapi==0.110.1
pydantic==2.7.0
uvicorn==0.29.0
pytest==8.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, Heroku will look for a file called Procfile to determine how to start our Python application. Procfile should look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: uvicorn main:app --host=0.0.0.0 --port=${PORT}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With those files in place, let’s create our app.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ heroku login

~/project$ heroku apps:create is-it-prime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That was it? Yeah. That was it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #3: Push your code to Heroku
&lt;/h2&gt;

&lt;p&gt;Next, we push our project code to the git remote that the Heroku CLI set up when we created our app.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ git push heroku main
…
remote: -----&amp;gt; Launching...
remote:        Released v3
remote:        https://is-it-prime-2f2e4fe7adc1.herokuapp.com/ deployed to Heroku

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So, that’s done. Let’s check our API.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl https://is-it-prime-2f2e4fe7adc1.herokuapp.com/prime/91
false

$ curl https://is-it-prime-2f2e4fe7adc1.herokuapp.com/prime/7873
true

$ curl https://is-it-prime-2f2e4fe7adc1.herokuapp.com/prime/7802143
false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It works!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #4: Create a Heroku Pipeline to use Heroku CI
&lt;/h2&gt;

&lt;p&gt;Now, we want to create a Heroku Pipeline with Heroku CI enabled so that we can run our tests.&lt;/p&gt;

&lt;p&gt;We create the pipeline (called is-it-prime-pipeline), adding the app we created above to the staging phase of the pipeline.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku pipelines:create \
  --app=is-it-prime \
  --stage=staging \
  is-it-prime-pipeline

Creating is-it-prime-pipeline pipeline... done
Adding ⬢ is-it-prime to is-it-prime-pipeline pipeline as staging... done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With our pipeline created, we want to connect it to a GitHub repo so that our actions on the repo (such as new pull requests or merges) can trigger events in our pipeline (like automatically running the test suite).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku pipelines:connect is-it-prime-pipeline -r capnMB/heroku-ci-demo

Linking to repo... done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can see, I’m connecting my pipeline to my GitHub repo. When something like a pull request or a merge occurs in my repo, it will trigger the Heroku CI to run the test suite.&lt;/p&gt;

&lt;p&gt;Next, we need to configure our test environment in an app.json manifest. Our file contents should look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "environments": {
    "test": {
      "formation": {
        "test": {
          "quantity": 1,
          "size": "standard-1x"
        }
      },
      "scripts": {
        "test": "pytest"
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This manifest contains the script we would use to run through our test suite. It also specifies the dyno size we (standard-1x) would want to use for our test environment. We commit this file to our repo.&lt;/p&gt;

&lt;p&gt;Finally, in the web UI for Heroku, we navigate to the Tests page of our pipeline, and we click the Enable Heroku CI button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ubtn0a07uain6p04hpo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ubtn0a07uain6p04hpo.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After enabling Heroku CI, here’s what we see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wg7b1avo5r2kp0fvdqz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wg7b1avo5r2kp0fvdqz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #5: Run your tests with Heroku CI
&lt;/h2&gt;

&lt;p&gt;Just to demonstrate it, we can manually trigger a run of our test suite using the Heroku CLI:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku ci:run --pipeline is-it-prime-pipeline
…
-----&amp;gt; Running test command `pytest`...
========================= test session starts ============================
platform linux -- Python 3.12.3, pytest-8.0.2, pluggy-1.4.0
rootdir: /app
plugins: anyio-4.3.0
collected 9 items

tests/test_is_prime.py .........                                         [100%]

============================ 9 passed in 0.03s ============================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;How does the test run look in our browser? We navigate to our pipeline and click Tests. There, we see our first test run in the left-side nav.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd05h4x3fbcedwlitgbk6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd05h4x3fbcedwlitgbk6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A closer inspection of our tests shows this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzh89ty7kf37j2yqz45wk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzh89ty7kf37j2yqz45wk.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome. Now, let’s push some new code to a branch in our repo and watch the tests run!&lt;/p&gt;

&lt;p&gt;We create a new branch (called new-test), adding another test case to test_is_prime.py. As soon as we push our branch to GitHub, here’s what we see at Heroku:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3g53zgzvd4nypxsi8qd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3g53zgzvd4nypxsi8qd.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Heroku CI detects the pushed code and automates a new run of the test suite. Not too long after, we see the successful results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gkm2t7fj57yxr84btte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gkm2t7fj57yxr84btte.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Heroku CI for the win
&lt;/h2&gt;

&lt;p&gt;If you’re using Heroku for your production environment—and you’re ready to go all in with DevOps—then using &lt;a href="https://devcenter.heroku.com/articles/pipelines" rel="noopener noreferrer"&gt;pipelines&lt;/a&gt; and &lt;a href="https://devcenter.heroku.com/articles/heroku-ci" rel="noopener noreferrer"&gt;Heroku CI&lt;/a&gt; may be the way to go.&lt;/p&gt;

&lt;p&gt;Rather than using different tools and platforms for building, testing, reviewing, staging, and releasing to production… I can consolidate all these pieces in a single Heroku Pipeline. And with Heroku CI, I get automated testing with every push to my repo.&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>pipeline</category>
      <category>heroku</category>
      <category>testing</category>
    </item>
    <item>
      <title>How To Build a Simple GitHub Action To Deploy a Django Application to the Cloud</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Mon, 10 Jun 2024 14:25:13 +0000</pubDate>
      <link>https://dev.to/heroku/how-to-build-a-simple-github-action-to-deploy-a-django-application-to-the-cloud-4395</link>
      <guid>https://dev.to/heroku/how-to-build-a-simple-github-action-to-deploy-a-django-application-to-the-cloud-4395</guid>
      <description>&lt;p&gt;Continuous integration and continuous delivery (CI/CD) capabilities are basic expectations for modern development teams who want fast feedback on their changes and rapid deployment to the cloud. In recent years, we’ve seen the growing adoption of GitHub Actions, a feature-rich CI/CD system that dovetails nicely with cloud hosting platforms such as Heroku. In this article, we’ll demonstrate the power of these tools used in combination—specifically how GitHub Actions can be used to quickly deploy a Django application to the cloud. &lt;/p&gt;

&lt;h2&gt;
  
  
  A Quick Introduction to Django
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; is a Python web application framework that’s been around since the early 2000s. It follows a model-view-controller (MVC) architecture and is known as the “batteries-included” web framework for Python. That’s because it has lots of capabilities, including a strong object-relational mapping (ORM) for abstracting database operations and models. It also has a rich templating system with many object-oriented design features.&lt;/p&gt;

&lt;p&gt;Instagram, Nextdoor, and Bitbucket are examples of applications built using Django. Clearly, if Django is behind Instagram, then we know that it can scale well. (Instagram hovers around being the fourth most visited site in the world!)&lt;/p&gt;

&lt;p&gt;Security is another built-in feature; authentication, cross-site scripting protection, and CSRF features all come out of the box and are easy to configure. Django is over 20 years old, which means it has a large dev community and documentation base—both helpful when you’re trying to figure out why something has gone awry.&lt;/p&gt;

&lt;p&gt;Downsides to Django? Yes, there are a few, with the biggest one being a steeper learning curve than other web application frameworks. You need to know parts of everything in the system to get it to work. For example, to get a minimal “hello world” page up in your browser, you need to set up the ORM, templates, views, routes, and a few other things. Contrast that with a framework like Flask (which is, admittedly, less feature-rich), where less than 20 lines of code can get your content displayed on a web page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Our Simple Django Application
&lt;/h2&gt;

&lt;p&gt;If you’re not familiar with Django, &lt;a href="https://docs.djangoproject.com/en/5.0/intro/tutorial01/" rel="noopener noreferrer"&gt;their tutorial&lt;/a&gt; is a good place to start learning how to get a base system configured and running. For this article, I’ve created a similar system using a PostgreSQL database and a few simple models and views. But we won’t spend time describing how to set up a complete Django application. That’s what the Django tutorial is for.&lt;/p&gt;

&lt;p&gt;My application here is different from the tutorial in that I use PostgreSQL—instead of the default SQLite—as the database engine. The trouble with SQLite (besides poor performance in a web application setting) is that it is file-based, and the file resides on the same server as the web application that uses it. Most cloud platforms assume a stateless deployment, meaning the container that holds the application is wiped clean and refreshed every deployment. So, your database should run on a separate server from the web application. PostgreSQL will provide that for us.&lt;/p&gt;

&lt;p&gt;The source code for this mini-demo project is available in &lt;a href="https://github.com/CapnMB/django-heroku-github-actions" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Install Python dependencies
&lt;/h3&gt;

&lt;p&gt;After you have cloned the repository, start up a virtual environment and install the Python dependencies for this project:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

(venv) ~/project$ pip install -r requirements.txt


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Set up Django to use PostgreSQL
&lt;/h3&gt;

&lt;p&gt;To use PostgreSQL with Django, we use the following packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/psycopg2/" rel="noopener noreferrer"&gt;psycopg2&lt;/a&gt; provides the engine drivers for Postgres.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/dj-database-url/" rel="noopener noreferrer"&gt;dj-database-url&lt;/a&gt; helps us set up the database connection string from an environment variable (useful for local testing and cloud deployments).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In our Django app, we navigate to mysite/mysite/ and modify settings.py (around line 78) to use PostgreSQL.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

DATABASES = {"default": dj_database_url.config(conn_max_age=600, ssl_require=True)}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We’ll start by testing out our application locally. So, on your local PostgreSQL instance, create a new database.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

postgres=# create database django_test_db;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Assuming our PostgreSQL username is dbuser and the password is password, then our DATABASE_URL will look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

postgres://dbuser:password@localhost:5432/django_test_db


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;From here, we need to run our database migrations to set up our tables.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

(venv) ~/project$ \
  DATABASE_URL=postgres://dbuser:password@localhost:5432/django_test_db\
  python mysite/manage.py migrate

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, movie_journal, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying movie_journal.0001_initial... OK
  Applying sessions.0001_initial... OK


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Test application locally
&lt;/h3&gt;

&lt;p&gt;Now that we have set up our database, we can spin up our application and test it in the browser.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

(venv) ~/project$ \
  DATABASE_URL=postgres://dbuser:password@localhost:5432/django_test_db\
  python mysite/manage.py runserver

…
Django version 4.2.11, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our browser, we visit &lt;a href="http://localhost:8000/movie-journal" rel="noopener noreferrer"&gt;http://localhost:8000/movie-journal&lt;/a&gt;. This is what we see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpr0vbhpdxcehg8enfzzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpr0vbhpdxcehg8enfzzr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’re up and running! We can go through the flow of creating a new journal entry.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7g0zzrbeis1ega868p8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7g0zzrbeis1ega868p8p.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Looking in our database, we see the record for our new entry.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

django_test_db=# select * from movie_journal_moviejournalentry;
-[ RECORD 1 ]+-------------------------------------------------------------
id           | 1
title        | Best of the Best
imdb_link    | https://www.imdb.com/title/tt0096913/
is_positive  | t
review       | Had some great fight scenes. The plot was amazing.
release_year | 1989
created_at   | 2024-03-29 09:36:59.24143-07
updated_at   | 2024-03-29 09:36:59.241442-07


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our application is working. We’re ready to deploy. Let’s walk through how to deploy using GitHub Actions directly from our repository on commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Over the years, GitHub Actions has built up a large library of jobs/workflows, providing lots of reusable code and conveniences for developers.&lt;/p&gt;

&lt;p&gt;With CI/CD, a development team can get fast feedback as soon as code changes are committed and pushed. Typical jobs found in a CI pipeline include style checkers, static analysis tools, and unit test runners. All of these help enforce good coding practices and adherence to team standards. Yes, all these tools existed before. But now, developers don’t need to worry about manually running them or waiting for them to finish.&lt;/p&gt;

&lt;p&gt;Push your changes to the remote branch, and the job starts automatically. Go on to focus on your next coding task as GitHub runs the current jobs and displays their results as they come in. That’s the power of automation and the cloud, baby!&lt;/p&gt;

&lt;h3&gt;
  
  
  Plug-and-play GitHub Action workflows
&lt;/h3&gt;

&lt;p&gt;You can even have GitHub create your job configuration file for you. Within your repository on GitHub, click Actions. You’ll see an entire library of templates, giving you pre-built workflows that could potentially fit your needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwvmx2p6ytcvalttw8wi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwvmx2p6ytcvalttw8wi.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s click on the Configure button for the Pylint workflow. It looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

name: Pylint

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.8", "3.9", "3.10"]
    steps:
    - uses: actions/checkout@v3
    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v3
      with:
        python-version: ${{ matrix.python-version }}
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install pylint
    - name: Analysing the code with pylint
      run: |
        pylint $(git ls-files '*.py')



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This configuration directs GitHub Actions to create a new workflow in your repository named Pylint. It triggers a push to any branch. It has one job, build, that runs the latest Ubuntu image. Then, it runs all the steps for each of the three different versions of Python specified.&lt;/p&gt;

&lt;p&gt;The steps are where the nitty-gritty work is defined. In this example, the job checks out your code, sets up the Python version, installs dependencies, and then runs the linter over your code. &lt;/p&gt;

&lt;p&gt;Let’s create our own GitHub Action workflow to deploy our application directly to Heroku.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Heroku via a GitHub Action
&lt;/h2&gt;

&lt;p&gt;Here’s the good news: it’s easy. First, &lt;a href="https://www.googleadservices.com/pagead/aclk?sa=L&amp;amp;ai=DChcSEwiXtdaw-5mFAxXBLdQBHZJ5BJkYABABGgJvYQ&amp;amp;ase=2&amp;amp;gclid=Cj0KCQjwzZmwBhD8ARIsAH4v1gXSRHbXjzlc9n8gUY1x-3mgbp4AV3KsQznkWamh3S91LfOBGbOO0IYaArHsEALw_wcB&amp;amp;ohost=www.google.com&amp;amp;cid=CAESV-D2oE_02dbv0jHQ_lty1qqK8Lx5as8Sx0CGWY4yAwXX-iO_prj5-K-woiZ95Jwj1VyouwGSiQnd0ORFU-fyr8keQSYrY-2msOiEEe6x87QjOio6iFfYcw&amp;amp;sig=AOD64_07cvmaddkY6Suzb9QJWReiRtqCSQ&amp;amp;q&amp;amp;nis=4&amp;amp;adurl&amp;amp;ved=2ahUKEwivwc-w-5mFAxXbJUQIHTVzAGsQqyQoAHoECAoQEw" rel="noopener noreferrer"&gt;sign up for a Heroku account&lt;/a&gt; and &lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt;install the Heroku CLI&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Login, create app, and PostgreSQL add-on
&lt;/h3&gt;

&lt;p&gt;With the Heroku CLI, we run the following commands to create our app and the PostgreSQL add-on:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ heroku login

$ heroku apps:create django-github
Creating ⬢ django-github... done
https://django-github-6cbf23e36b5b.herokuapp.com/ | https://git.heroku.com/django-github.git

$ heroku addons:create heroku-postgresql:mini --app django-github
Creating heroku-postgresql:mini on ⬢ django-github... ~$0.007/hour (max $5/month)
Database has been created and is available
 ! This database is empty. If upgrading, you can transfer
 ! data from another database with pg:copy


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Add Heroku app host to allowed hosts list in Django
&lt;/h3&gt;

&lt;p&gt;In our Django application settings, we need to update the list of &lt;a href="https://docs.djangoproject.com/en/5.0/ref/settings/#allowed-hosts" rel="noopener noreferrer"&gt;ALLOWED_HOSTS&lt;/a&gt;, which represent the host/domain names that your Django site can serve. We need to add the host from our newly created Heroku app. Edit mysite/mysite/settings.py, at around line 31, to add your Heroku app host. It will look similar to this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ALLOWED_HOSTS = ["localhost", "django-github-6cbf23e36b5b.herokuapp.com"]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Don’t forget to commit this file to your repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Procfile and requirements.txt
&lt;/h3&gt;

&lt;p&gt;Next, we need to add a Heroku-specific file called Procfile. This goes into the root folder of our repository. This file tells Heroku how to start up our app and run migrations. It should have the following contents:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

web: gunicorn --pythonpath mysite mysite.wsgi:application
release: cd mysite &amp;amp;&amp;amp; ./manage.py migrate --no-input



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Heroku will also need your requirements.txt file so it knows which Python dependencies to install.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get your Heroku API key
&lt;/h3&gt;

&lt;p&gt;We will need our Heroku account API key. We’ll store this at GitHub so that our GitHub Action has authorization to deploy code to our Heroku app.&lt;/p&gt;

&lt;p&gt;In your Heroku account settings, find the auto-generated API key and copy the value.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb6cffsiu1jg6jb8dnq9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb6cffsiu1jg6jb8dnq9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, in your GitHub repository settings, navigate to Secrets and variables &amp;gt; Actions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cwsoho9yg807p89kjmr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0cwsoho9yg807p89kjmr.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On that page, click New repository secret. Supply a name for your repository secret and. Then, paste in your Heroku API key and click Add secret.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrbqajg0luxgjca2gtuf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrbqajg0luxgjca2gtuf.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your list of GitHub repository secrets should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsupreljvjoai37ih5r2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsupreljvjoai37ih5r2t.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the job configuration file
&lt;/h2&gt;

&lt;p&gt;Let’s create our GitHub Action workflow. Typically, we configure CI/CD jobs with a YAML file. With GitHub Actions, this is no different.&lt;/p&gt;

&lt;p&gt;To add an action to your repository, create a .github subfolder in your project, and then create a workflows subfolder within that one. In .github/workflows/, we’ll create a file called django.yml. Your project tree should look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.
├── .git
│   └── …
├── .github
│   └── workflows
│       └── django.yml

├── mysite
│   ├── manage.py
│   ├── mysite
│   │   ├── …
│   │   └── settings.py
│   └── …
├── Procfile
└── requirements.txt


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our django.yml file has the following contents:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

name: Django CI

on:
  push:
    branches: [ "main" ]

jobs:
  release:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v2
       - uses: akhileshns/heroku-deploy@v3.13.15
         with:
           heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
           heroku_app_name: "&amp;lt;your-heroku-app-name&amp;gt;"
           heroku_email: "&amp;lt;your-heroku-email&amp;gt;"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This workflow builds off of the &lt;a href="https://github.com/marketplace/actions/deploy-to-heroku" rel="noopener noreferrer"&gt;Deploy to Heroku Action&lt;/a&gt; in the GitHub Actions library. In fact, using that pre-built action makes our Heroku deployment simple. The only things you need to configure in this file are your Heroku app name and account email.&lt;/p&gt;

&lt;p&gt;When we commit this file to our repo and push our main branch to GitHub, this kicks off our GitHub Action job for deploying to Heroku. In GitHub, we click the Actions tab and see the newly triggered workflow. When we click the release job in the workflow, this is what we see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvilmmholm7h67ehanyb5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvilmmholm7h67ehanyb5.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Near the bottom of the output of the deploy step, we see results from the Heroku deploy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfnxp61t78ww8pzrk7bm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfnxp61t78ww8pzrk7bm.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we look in our Heroku app logs, we also see the successful deploy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4qppz58bblcke32mbmh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj4qppz58bblcke32mbmh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, when we test our Heroku-deployed app in our browser, we see that it’s up and running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5im1l1afiq0p8biwz3il.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5im1l1afiq0p8biwz3il.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congrats! You’ve successfully deployed your Django action to Heroku via a GitHub Action!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we set up a simple Django application with a PostgreSQL database. Then, we walked through how to use GitHub Actions to deploy the application directly to your Heroku on commit.&lt;/p&gt;

&lt;p&gt;Django is a feature-rich web application framework for Python. Although for some cloud platforms, it can take some time to get things configured correctly, that’s not the case when you’re deploying to Heroku with GitHub Actions. Convenient off-the-shelf tools are available in both GitHub and Heroku, and they make deploying your Django application a breeze. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Working with Heroku Logplex for Comprehensive Application Logging</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Tue, 28 May 2024 15:13:48 +0000</pubDate>
      <link>https://dev.to/heroku/working-with-heroku-logplex-for-comprehensive-application-logging-89m</link>
      <guid>https://dev.to/heroku/working-with-heroku-logplex-for-comprehensive-application-logging-89m</guid>
      <description>&lt;p&gt;With the complexity of modern software applications, one of the biggest challenges for developers is simply understanding how their applications behave. Understanding the behavior of your app is key to maintaining its stability, performance, and security.&lt;/p&gt;

&lt;p&gt;This is a big reason why we do application logging: to capture and record events through an application’s lifecycle, so that we can gain valuable insights into our application. What kinds of insights? Application activity (user interactions, system events, and so on), errors and exceptions, resource usage, potential security threats, and more.&lt;/p&gt;

&lt;p&gt;When developers can capture and analyze these logs effectively, this improves application stability and security, which, in turn, improves the user experience. It’s a win-win for everybody.&lt;/p&gt;

&lt;p&gt;Application logging is easy—if you have the right tools. In this post, we’ll walk through using Heroku Logplex as a centralized logging solution. We’ll start by deploying a simple Python application to Heroku. Then, we’ll explore the different ways to use Logplex to view and filter our logs. Finally, we’ll show how to use Logplex to send your logs to an external service for further analysis.&lt;/p&gt;

&lt;p&gt;Ready to dive in? Let’s start with a brief introduction to Heroku Logplex.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Heroku Logplex
&lt;/h2&gt;

&lt;p&gt;Heroku Logplex is a central hub that collects, aggregates, and routes log messages from various sources across your Heroku applications. Those sources include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dyno logs&lt;/strong&gt;: generated by your application running on Heroku dynos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heroku logs&lt;/strong&gt;: generated by Heroku itself, such as platform events and deployments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom sources&lt;/strong&gt;: generated by external sources, such as databases or third-party services.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By consolidating logs in a single, central place, Logplex simplifies log management and analysis. You can find all your logs in one place for simplified monitoring and troubleshooting. You can perform powerful filtering and searching on your logs. And you can even route logs to different destinations for further processing and analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core components
&lt;/h2&gt;

&lt;p&gt;At its heart, Heroku Logplex consists of three crucial components that work together to streamline application logging:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Log sources&lt;/strong&gt; are the starting points where log messages originate within your Heroku environment. They are your dyno logs, Heroku logs, and custom sources which we mentioned above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log drains&lt;/strong&gt; are the designated destinations for your log messages. Logplex allows you to configure drains to route your logs to various endpoints for further processing. Popular options for log drains include:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;External logging services&lt;/strong&gt; with advanced log management features, dashboards, and alerting capabilities. Examples are Datadog, Papertrail, and Sumo Logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notification systems&lt;/strong&gt; that send alerts or notifications based on specific log entries, enabling real-time monitoring and troubleshooting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom destinations&lt;/strong&gt; such as your own Syslog or web server.&lt;/li&gt;
&lt;li&gt;Log filters are powerful tools that act as checkpoints, allowing you to refine the log messages before they reach their final destinations. Logplex allows you to filter logs based on source, log level, and even message content.
By using filters, you can significantly reduce the volume of data sent to your drains, focusing only on the most relevant log entries for that specific destination.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Routing and processing
&lt;/h2&gt;

&lt;p&gt;As Logplex collects log messages from all your defined sources, it passes these messages through your configured filters, potentially discarding entries that don't match the criteria. Finally, filtered messages are routed to their designated log drains for further processing or storage.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Alright, enough talk. Show me how, already!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating Logplex with Your Application
&lt;/h2&gt;

&lt;p&gt;Let’s walk through how to use Logplex for a simple Python application. To get started, make sure you have a Heroku account. Then, &lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt;download and install the Heroku CLI&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Demo application
&lt;/h3&gt;

&lt;p&gt;You can find our very simple Python script (main.py) in the &lt;a href="https://github.com/CapnMB/heroku-logplex-python" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt; for this demo. Our script runs an endless integer counter, starting from zero. With each iteration, it emits a log message (cycling through log levels INFO, DEBUG, ERROR, and WARN). Whenever it detects a prime number, it emits an additional CRITICAL log event to let us know. We use &lt;a href="https://docs.sympy.org/latest/modules/ntheory.html#sympy.ntheory.primetest.isprime" rel="noopener noreferrer"&gt;isprime from the sympy library&lt;/a&gt; to help us determine if a number is prime.&lt;/p&gt;

&lt;p&gt;To run this Python application on your local machine, first clone the repository. Then, install the dependencies:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(venv) ~/project$ pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, start up the Python application. We use gunicorn to spin up a server that binds to a port, while our prime number logging continues to run in the background. (We do this because a Heroku deployment is designed to bind to a port, so that’s how we’ve written our application even though we’re focused on logging).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(venv) ~/project$ gunicorn -w 1 --bind localhost:8000 main:app
[2024-03-25 23:18:59 -0700] [785441] [INFO] Starting gunicorn 21.2.0
[2024-03-25 23:18:59 -0700] [785441] [INFO] Listening at: http://127.0.0.1:8000 (785441)
[2024-03-25 23:18:59 -0700] [785441] [INFO] Using worker: sync
[2024-03-25 23:18:59 -0700] [785443] [INFO] Booting worker with pid: 785443
{"timestamp": "2024-03-25T23:18:59.507828Z", "level": "INFO", "name": "root", "message": "New number", "Number": 0}
{"timestamp": "2024-03-25T23:19:00.509182Z", "level": "DEBUG", "name": "root", "message": "New number", "Number": 1}
{"timestamp": "2024-03-25T23:19:01.510634Z", "level": "ERROR", "name": "root", "message": "New number", "Number": 2}
{"timestamp": "2024-03-25T23:19:02.512100Z", "level": "CRITICAL", "name": "root", "message": "Prime found!", "Prime Number": 2}
{"timestamp": "2024-03-25T23:19:05.515133Z", "level": "WARNING", "name": "root", "message": "New number", "Number": 3}
{"timestamp": "2024-03-25T23:19:06.516567Z", "level": "CRITICAL", "name": "root", "message": "Prime found!", "Prime Number": 3}
{"timestamp": "2024-03-25T23:19:09.519082Z", "level": "INFO", "name": "root", "message": "New number", "Number": 4}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Simple enough. Now, let’s get ready to deploy it and work with logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the app
&lt;/h3&gt;

&lt;p&gt;We start by logging into Heroku through the CLI.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then, we create a new Heroku app. I’ve named my app logging-primes-in-python, but you can name yours whatever you’d like.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku apps:create logging-primes-in-python
Creating ⬢ logging-primes-in-python... done
https://logging-primes-in-python-6140bfd3c044.herokuapp.com/ | https://git.heroku.com/logging-primes-in-python.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we &lt;a href="https://devcenter.heroku.com/articles/git#create-a-heroku-remote" rel="noopener noreferrer"&gt;create a Heroku remote&lt;/a&gt; for our GitHub repo with this Python application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku git:remote -a logging-primes-in-python
set git remote heroku to https://git.heroku.com/logging-primes-in-python.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  A note on requirements.txt and Procfile
&lt;/h3&gt;

&lt;p&gt;We need to let Heroku know what dependencies our Python application needs, and also how it should start up our application. To do this, our repository has two files: requirements.txt and Procfile.&lt;/p&gt;

&lt;p&gt;The first file, requirements.txt, looks like this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python-json-logger==2.0.4
pytest==8.0.2
sympy==1.12
gunicorn==21.2.0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And Procfile looks like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: gunicorn -w 1 --bind 0.0.0.0:${PORT} main:app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;That’s it. Our entire repository has these files:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ tree
.
├── main.py
├── Procfile
└── requirements.txt

0 directories, 3 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Deploy the code
&lt;/h3&gt;

&lt;p&gt;Now, we’re ready to deploy our code. We run this command:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git push heroku main
…
remote: Building source:
remote: 
remote: -----&amp;gt; Building on the Heroku-22 stack
remote: -----&amp;gt; Determining which buildpack to use for this app
remote: -----&amp;gt; Python app detected
…
remote: -----&amp;gt; Installing requirements with pip
…
remote: -----&amp;gt; Launching...
remote:        Released v3
remote:        https://logging-primes-in-python-6140bfd3c044.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Verify the app is running
&lt;/h3&gt;

&lt;p&gt;To verify that everything works as expected, we can dive into Logplex right away. Logplex is enabled by default for all Heroku applications.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku logs --tail -a logging-primes-in-python
…
2024-03-22T04:34:15.540260+00:00 heroku[web.1]: Starting process with command `gunicorn -w 1 --bind 0.0.0.0:${PORT} main:app`
…
2024-03-22T04:34:16.425619+00:00 app[web.1]: {"timestamp": "2024-03-22T04:34:16.425552Z", "level": "INFO", "name": "root", "message": "New number", "taskName": null, "Number": 0}
2024-03-22T04:34:17.425987+00:00 app[web.1]: {"timestamp": "2024-03-22T04:34:17.425837Z", "level": "DEBUG", "name": "root", "message": "New number", "taskName": null, "Number": 1}
2024-03-22T04:34:18.000000+00:00 app[api]: Build succeeded
2024-03-22T04:34:18.426354+00:00 app[web.1]: {"timestamp": "2024-03-22T04:34:18.426205Z", "level": "ERROR", "name": "root", "message": "New number", "taskName": null, "Number": 2}
2024-03-22T04:34:19.426700+00:00 app[web.1]: {"timestamp": "2024-03-22T04:34:19.426534Z", "level": "CRITICAL", "name": "root", "message": "Prime found!", "taskName": null, "Prime Number": 2}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can see that logs are already being written. Heroku’s log format is following this scheme:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timestamp source[dyno]: message
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp&lt;/strong&gt;: The date and time recorded at the time the dyno or component produced the log line. The timestamp is in the format specified by RFC5424 and includes microsecond precision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source&lt;/strong&gt;: All of your app’s dynos (web dynos, background workers, cron) have the source app. Meanwhile, all of Heroku’s system components (HTTP router, dyno manager) have the source heroku.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dyno&lt;/strong&gt;: The name of the dyno or component that wrote the log line. For example, web dyno #1 appears as web.1, and the Heroku HTTP router appears as router.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message&lt;/strong&gt;: The content of the log line. Logplex splits any lines generated by dynos that exceed 10,000 bytes into 10,000-byte chunks without extra trailing newlines. It submits each chunk that is submitted as a separate log line.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  View and filter logs
&lt;/h3&gt;

&lt;p&gt;We’ve seen the first option for examining our logs, the Heroku CLI. You can use command line arguments, such as --source and --dyno, to use filters and specify which logs to view.&lt;/p&gt;

&lt;p&gt;To specify the number of (most recent) log entries to view, do this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku logs --num 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To filter down logs to a specific dyno or source, do this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku logs --dyno web.1
$ heroku logs --source app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Of course, you can combine these filters, too:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku logs --source app --dyno web.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Heroku Dashboard is another place where you can look at your logs. On your app page, click More -&amp;gt; View logs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9oagc40pogeaprx2blto.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9oagc40pogeaprx2blto.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is what we see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib4jbeoyqc9jo1mwy8sb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib4jbeoyqc9jo1mwy8sb.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look closely, you’ll see different sources: heroku and app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Log Drains
&lt;/h2&gt;

&lt;p&gt;Let’s demonstrate how to use a log drain. For this, we’ll use &lt;a href="https://betterstack.com/logs" rel="noopener noreferrer"&gt;BetterStack&lt;/a&gt; (formerly Logtail). We created a free account. After logging in, we navigated to the Source page, and clicked Connect source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2d03s20p081xdxcjb8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw2d03s20p081xdxcjb8z.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We enter a name for our source and select Heroku as the source platform. Then, we click Create source.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxayw8et6ojnxf52uoyg2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxayw8et6ojnxf52uoyg2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After creating our source, BetterStack provides the Heroku CLI command we would use to add a log drain for sending logs to BetterStack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgid8sqzj397k79nseqss.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgid8sqzj397k79nseqss.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Technically, this command adds an HTTPS drain that points to an HTTPS endpoint from BetterStack. We run the command in our terminal, and then we restart our application:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku drains:add \
"https://in.logs.betterstack.com:6515/events?source_token=YKGWLN7****************" \
-a logging-primes-in-python


Successfully added drain https://in.logs.betterstack.com:6515/events?source_token=YKGWLN7*****************

$ heroku restart -a logging-primes-in-python
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Almost instantly, we begin to see our Heroku logs appear on the Live tail page at BetterStack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pbxmefp85emktmcsesw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pbxmefp85emktmcsesw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By using a log drain to send our logs from Heroku Logplex to an external service, we can take advantage of the features from BetterStack to work with our Heroku logs. For example, we can create visualization charts and configure alerts on certain log events.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom drains
&lt;/h2&gt;

&lt;p&gt;In our example above, we created a custom HTTPS log drain that happened to point to an endpoint from BetterStack. However, we can send our logs to any endpoint we want. We could even send our logs to another Heroku app! 🤯 Imagine building a web service on Heroku that only Heroku Logplex can make POST requests to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging best practices
&lt;/h2&gt;

&lt;p&gt;Before we conclude our walkthrough, let’s briefly touch on some logging best practices.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Focus on relevant events&lt;/strong&gt;: Log only the information that’s necessary to understand and troubleshoot your application's behavior. Prioritize logging application errors, user actions, data changes, and other crucial activities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enrich logs with context&lt;/strong&gt;: Include details that provide helpful context to logged events. Your future troubleshooting self will thank you. So, instead of just logging "User logged in," capture details like user ID, device information, and relevant data associated with the login event. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embrace structured logging&lt;/strong&gt;: Use a standardized format like JSON to make your logs machine-readable. This allows easier parsing and analysis by logging tools, saving you time in analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protect sensitive data&lt;/strong&gt;: Never log anything that could compromise user privacy or violate data regulations. This includes passwords, credit card information, or other confidential data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Take advantage of log levels&lt;/strong&gt;: Use different log levels (like DEBUG, INFO, WARNING, and ERROR) to categorize log events based on their severity. This helps with issue prioritization, allowing you to focus on critical events requiring immediate attention.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Heroku Logplex empowers developers and operations teams with a centralized and efficient solution for application logging within the Heroku environment. While our goal in this article was to provide a basic foundation for understanding Heroku Logplex, remember that the platform offers a vast array of advanced features to explore and customize your logging based on your specific needs. &lt;br&gt;
As you dig deeper into Heroku’s documentation, you’ll come across advanced functionalities like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Customizable log processing&lt;/strong&gt;: Leverage plugins and filters to tailor log processing workflows for specific use cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time alerting&lt;/strong&gt;: Configure alerts based on log patterns or events to proactively address potential issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced log analysis tools&lt;/strong&gt;: Integrate with external log management services for comprehensive log analysis, visualization, and anomaly detection.
By understanding the core functionalities and exploring the potential of advanced features, you can leverage Heroku Logplex to create a robust and efficient logging strategy. Ultimately, good logging will go a long way in enhancing the reliability, performance, and security of your Heroku applications.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>logging</category>
      <category>heroku</category>
      <category>programming</category>
    </item>
    <item>
      <title>Caching RESTful API requests with Heroku’s Redis Add-on</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Wed, 17 Apr 2024 13:39:30 +0000</pubDate>
      <link>https://dev.to/heroku/caching-restful-api-requests-with-herokus-redis-add-on-3bg8</link>
      <guid>https://dev.to/heroku/caching-restful-api-requests-with-herokus-redis-add-on-3bg8</guid>
      <description>&lt;p&gt;Most software developers encounter two main problems: naming things, caching, and off-by-one errors. 🤦🏻‍♂️ &lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll deal with caching. We’ll walk through how to implement RESTful request caching with an open source-licensed version of Redis. We’ll also set up and deploy this system easily with Heroku.&lt;/p&gt;

&lt;p&gt;For this demo, we’ll build a Node.js application with the Fastify framework, and we’ll integrate caching with Redis to reduce certain types of latency.&lt;/p&gt;

&lt;p&gt;Ready to dive in? Let’s go!&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js + Fastify + long-running tasks
&lt;/h2&gt;

&lt;p&gt;As I’m sure our readers know, Node.js is a very popular platform for building web applications. With its support for JavaScript (or TypeScript, or both at the same time!), Node.js allows you to use the same language for both the frontend and the backend of your application. It also has a rich event loop that makes asynchronous request handling more intuitive.&lt;/p&gt;

&lt;p&gt;The concurrency model in Node.js is very performant, able to &lt;a href="https://www.pixel506.com/insights/how-much-traffic-can-nodejs-handle"&gt;handle upwards of 15,000 requests per second&lt;/a&gt;. But even then, you might still run into situations where the request latency is unacceptably high. We’ll show this with our application.&lt;/p&gt;

&lt;p&gt;As you follow along, you can always browse the codebase for this mini demo at my &lt;a href="https://github.com/CapnMB/fastify-caching-redis"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initialize the basic application
&lt;/h2&gt;

&lt;p&gt;By using &lt;a href="https://fastify.dev/"&gt;Fastify&lt;/a&gt;, you can quickly get a Node.js application up and running to handle requests. Assuming you have Node.js installed, you’ll start by initializing a new project. We’ll use &lt;a href="https://npmjs.com/"&gt;npm&lt;/a&gt; as our package manager.&lt;/p&gt;

&lt;p&gt;After initializing a new project, we will install our Fastify-related dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ npm i fastify fastify-cli fastify-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we update our package.json file to add two scripts and turn on ES module syntax. We make sure to have the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "type": "module",
  "main": "app.js",
  "scripts": {
    "start": "fastify start -a 0.0.0.0 -l info app.js",
    "dev": "fastify start -p 8000 -w -l info -P app.js"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From there, we create our first file (routes.js) with an initial route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// routes.js

export default async function (fastify, _opts) {
  fastify.get("/api/health", async (_, reply) =&amp;gt; {
    return reply.send({ status: "ok" });
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we create our app.js file that prepares a Fastify instance and registers the routes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.js
import routes from "./routes.js";

export default async (fastify, opts) =&amp;gt; {
  fastify.register(routes);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These two simple files—our application and our route definitions—are all we need to get up and running with a small Fastify service that exposes one endpoint: /api/health. Our dev script in package.json is set to run the fastify-cli to start our server on localhost port 8000, which is good enough for now. We start up our server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, in another terminal window, we use &lt;a href="https://curl.se/"&gt;curl&lt;/a&gt; to hit the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~$ curl http://localhost:8000/api/health 
{"status":"ok"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add a simulated long-running process
&lt;/h2&gt;

&lt;p&gt;We’re off to a good start. Next, let’s add another route to simulate a long-running process. This will help us gather some latency data. In routes.js, we add another route handler within our exported default async function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; fastify.get("/api/user-data", async (_, reply) =&amp;gt; {
    await sleep(5000);
    const userData = readData();
    return reply.send({ data: userData });
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This exposes another endpoint: /api/user-data. Here, we have a method to simulate reading a lot of data from a database (readData) and a long-running process (sleep). We define those methods in routes.js as well. They look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import fs from "fs";

function readData() {
  try {
    const data = fs.readFileSync("data.txt", "utf8");
    return data;
  } catch (err) {
    console.error(err);
  }
}

function sleep(ms) {
  return new Promise((resolve) =&amp;gt; {
    setTimeout(resolve, ms);
  });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With our new route in place, we restart our server (npm run dev).&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure latency with curl
&lt;/h2&gt;

&lt;p&gt;How do we measure latency? The simplest way is to use curl. Curl captures various time profiling metrics when it makes requests. We just need to format curl’s output so that we can easily see the various latency values available. To do this, we define the output we want to see with a text file (curl-format.txt):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    time_namelookup:  %{time_namelookup}s\n
        time_connect:  %{time_connect}s\n
     time_appconnect:  %{time_appconnect}s\n
    time_pretransfer:  %{time_pretransfer}s\n
       time_redirect:  %{time_redirect}s\n
  time_starttransfer:  %{time_starttransfer}s\n
  -------------------  ----------\n
          time_total:  %{time_total}s\n
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With our output format defined, we can use it with our next curl call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -w "@curl-format.txt" \
     -o /dev/null -s \
     "http://localhost:8000/api/user-data"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The response we receive looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  time_namelookup:  0.000028s
        time_connect:  0.000692s
     time_appconnect:  0.000000s
    time_pretransfer:  0.000772s
       time_redirect:  0.000000s
  time_starttransfer:  5.055683s
                       ----------
          time_total:  5.058479s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, that’s not good. Over five seconds is way too long for a transfer time (the time it takes the server to actually handle the request). Imagine if this endpoint was being hit hundreds or thousands of times per second! Your users would be frustrated, and your server may crash under the weight of continually re-doing this work. &lt;/p&gt;

&lt;h2&gt;
  
  
  Redis to the rescue!
&lt;/h2&gt;

&lt;p&gt;Caching your responses is the first line of defense to reduce your transfer time (assuming you’ve addressed any of the poor programming practices that might be causing the latency!). So, let’s assume we’ve done everything we can do to reduce latency, but our application still needs five seconds to put this complex data together and return it to the user.&lt;/p&gt;

&lt;p&gt;In our scenario, because &lt;u&gt;the data is the same every time&lt;/u&gt; for every request to /api/user-data, we have a perfect candidate for caching. With caching, we’ll perform the necessary computation once, cache the result, and return the cached value for all subsequent requests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://redis.com/"&gt;Redis&lt;/a&gt; is a performant, in-memory key/value store, and it’s a common tool used for caching. To leverage it, we first install Redis on our local machine. Then, we need to add &lt;a href="https://www.npmjs.com/package/@fastify/redis"&gt;Fastify’s Redis plugin&lt;/a&gt; to our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ npm i @fastify/redis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Register the Redis plugin with Fastify
&lt;/h2&gt;

&lt;p&gt;We create a file, redis.js, which configures our Redis plugin and registers it with Fastify. Our file looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// redis.js

const REDIS_URL = process.env.REDIS_URL || "redis://127.0.0.1:6379";

import fp from "fastify-plugin";
import redis from "@fastify/redis";

const parseRedisUrl = (redisUrl) =&amp;gt; {
  const url = new URL(redisUrl);
  const password = url.password;
  return {
    host: url.hostname,
    port: url.port,
    password,
  };
};

export default fp(async (fastify) =&amp;gt; {
  fastify.register(redis, parseRedisUrl(REDIS_URL));
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the lines in this file are dedicated to parsing a REDIS_URL value into a host, port, and password. If we have REDIS_URL set properly at runtime as an environment variable, then registering Redis with Fastify is simple. After configuring our plugin, we just need to modify app.js to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// app.js

import redis from "./redis.js";
import routes from "./routes.js";

export default async (fastify, opts) =&amp;gt; {
  fastify.register(redis);
  fastify.register(routes);
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have access to our Redis instance by referencing fastify.redis anywhere within our app. &lt;/p&gt;

&lt;h2&gt;
  
  
  Modify our endpoint to use caching
&lt;/h2&gt;

&lt;p&gt;With Redis in the mix, let’s change our /api/user-data endpoint to use caching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  fastify.get("/api/user-data", async (_, reply) =&amp;gt; {
    const { redis } = fastify;

    // check if data is in cache
    const data = await redis.get("user-data", (err, val) =&amp;gt; {
      if (val) {
        return { data: val };
      }
      return null;
    });

    if (data) {
      return reply.send(data);
    }

    // simulate a long-running task
    await sleep(5000);
    const userData = readData();


    // add data to the cache
    redis.set("user-data", userData);


    return reply.send({ data: userData });
  });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, you see that we’ve hardcoded in Redis a single key, user-data, and stored our data under that key. Of course, our key could be a user ID or some other value that identifies a particular type of request or state. Also, we could &lt;a href="https://github.com/redis/ioredis?tab=readme-ov-file#expiration"&gt;set a timeout value&lt;/a&gt; to expire our key, in the case that we expect data to change after a certain window of time.&lt;/p&gt;

&lt;p&gt;If there is data in the cache, then we’ll return it and skip all the time-consuming work. Otherwise, do the long-running computation, add the result to the cache, and then return it to the user.&lt;/p&gt;

&lt;p&gt;What do our transfer times look like after hitting this endpoint two more times (the first one to add the data into the cache, and the second one to retrieve it)?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   time_namelookup:  0.000023s
        time_connect:  0.000560s
     time_appconnect:  0.000000s
    time_pretransfer:  0.000729s
       time_redirect:  0.000000s
  time_starttransfer:  0.044512s
                       ----------
          time_total:  0.047479s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better! We’ve reduced our request times from several seconds to milliseconds. That’s a huge improvement in performance! &lt;/p&gt;

&lt;p&gt;Redis has many more features that may be useful here, including having key/value pairs timeout after a certain amount of time; that’s a more common scenario in production environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Redis in your Heroku deployment
&lt;/h2&gt;

&lt;p&gt;Up to this point, we’ve only shown how this works in a local environment. Now, let’s go one step further and deploy it all to the cloud. Fortunately, Heroku provides many options for deploying web applications and working with Redis. Let’s walk through how to get set up there.&lt;/p&gt;

&lt;p&gt;After &lt;a href="https://signup.heroku.com/"&gt;signing up for a Heroku account&lt;/a&gt; and installing their &lt;a href="https://devcenter.heroku.com/articles/heroku-command-line"&gt;CLI tool&lt;/a&gt;, we’re ready to create a new app. In our case, we’ll call our app fastify-with-caching. Here are our steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Login to Heroku
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/projects$ heroku login
...
Logging in... done
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Create the Heroku app
&lt;/h3&gt;

&lt;p&gt;When we create our Heroku app, we’ll get back our Heroku app URL. We take note of this because we’ll use it in our subsequent curl requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ heroku create -a fastify-with-caching
Creating ⬢ fastify-with-caching... done
https://fastify-with-caching-3e247d11f4ad.herokuapp.com/ | https://git.heroku.com/fastify-with-caching.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Add the Redis add-on
&lt;/h3&gt;

&lt;p&gt;We need to set up a Redis add-on that meets our application’s needs. For our demo project, it’s sufficient to create a Mini-tier Redis instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ heroku addons:create heroku-redis:mini -a fastify-with-caching
Creating heroku-redis:mini on ⬢ fastify-with-caching…
…
redis-transparent-98258 is being created in the background.
…
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spinning up the Redis instance may take two or three minutes. We can check the status of our instance periodically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ heroku addons:info redis-transparent-98258
...
State:        creating

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not too long after, we see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;State:        created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re just about ready to go!&lt;/p&gt;

&lt;p&gt;When Heroku spins up our Redis add-on, it also adds our Redis credentials as config variables attached to our Heroku app. We can run the following command to see these config variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ heroku config -a fastify-with-caching
=== fastify-with-caching Config Vars

REDIS_TLS_URL: rediss://:p171d98f7696ab7eb2319f7b78083af749a0d0bb37622fc420e6c1205d8c4579c@ec2-18-213-142-76.compute-1.amazonaws.com:15940
REDIS_URL:     redis://:p171d98f7696ab7eb2319f7b78083af749a0d0bb37622fc420e6c1205d8c4579c@ec2-18-213-142-76.compute-1.amazonaws.com:15939
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Your credentials, of course, will be unique and different from what you see above.)&lt;/p&gt;

&lt;p&gt;Notice that we have a REDIS_URL variable all set up for us. It’s a good thing our redis.js file is coded to properly parse an environment variable called REDIS_URL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Create a Heroku remote
&lt;/h3&gt;

&lt;p&gt;Finally, we need to &lt;a href="https://devcenter.heroku.com/articles/git#create-a-heroku-remote"&gt;create a Heroku remote&lt;/a&gt; in our git repo so that we can easily deploy with git.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ heroku git:remote -a fastify-with-caching
set git remote heroku to https://git.heroku.com/fastify-with-caching.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Deploy!
&lt;/h3&gt;

&lt;p&gt;Now, when we push our branch to our Heroku remote, Heroku will build and deploy our application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/project$ git push heroku main
...
remote: Building source:
remote: 
remote: -----&amp;gt; Building on the Heroku-22 stack
remote: -----&amp;gt; Determining which buildpack to use for this app
remote: -----&amp;gt; Node.js app detected
remote:        
remote: -----&amp;gt; Creating runtime environment
...
remote: -----&amp;gt; Compressing...
remote:        Done: 50.8M
remote: -----&amp;gt; Launching...
remote:        Released v4
remote:        https://fastify-with-caching-3e247d11f4ad.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our application is up and running. It’s time to test it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test our deployed application
&lt;/h2&gt;

&lt;p&gt;We start with a basic curl request to our /api/health endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl https://fastify-with-caching-3e247d11f4ad.herokuapp.com/api/health
{"status":"ok"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Excellent. That looks promising.&lt;/p&gt;

&lt;p&gt;Next, let’s send our first request to the long-running process and capture the latency metrics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl \
  -w "@curl-format.txt" \
  -o /dev/null -s \
  https://fastify-with-caching-3e247d11f4ad.herokuapp.com/api/user-data

     time_namelookup:  0.035958s
        time_connect:  0.101336s
     time_appconnect:  0.249308s
    time_pretransfer:  0.249389s
       time_redirect:  0.000000s
  time_starttransfer:  5.384986s
  -------------------  ----------
          time_total:  6.554382s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When we send the same request a second time, here’s the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl \
  -w "@curl-format.txt" \
  -o /dev/null -s \
  https://fastify-with-caching-3e247d11f4ad.herokuapp.com/api/user-data

     time_namelookup:  0.025807s
        time_connect:  0.091763s
     time_appconnect:  0.236050s
    time_pretransfer:  0.236119s
       time_redirect:  0.000000s
  time_starttransfer:  0.334859s
  -------------------  ----------
          time_total:  1.276264s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Much better! Caching allows us to bypass the long-running processes. From here, we can build out a much more robust caching mechanism for our application across all our routes and processes. We can continue to lean on Heroku and Heroku’s Redis add-on when we need to deploy our application to the cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus Tip: Clearing the cache for future tests
&lt;/h2&gt;

&lt;p&gt;By the way, if you want to test this more than once, then you may occasionally need to delete the user-data key/value pair in Redis. You can use the Heroku CLI to access the Redis CLI for your Redis instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~$ heroku redis:cli -a fastify-with-caching
Connecting to redis-transparent-98258 (REDIS_TLS_URL, REDIS_URL):
ec2-18-213-142-76.compute-1.amazonaws.com:15940&amp;gt; DEL user-data
1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we explored how caching can greatly improve your web service's response time in cases where identical requests would produce identical responses. We looked at how to implement this with Redis, the industry-standard caching tool. We did this all with ease within a Node.js application that leverages the Fastify framework. Lastly, we deployed our demo application to Heroku, using their built-in Heroku Data for Redis instance management to cache in the cloud. &lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

</description>
      <category>redis</category>
      <category>heroku</category>
      <category>webdev</category>
      <category>restapi</category>
    </item>
    <item>
      <title>Managing Django Media &amp; Static Files on Heroku with Bucketeer</title>
      <dc:creator>Daniel Starner</dc:creator>
      <pubDate>Thu, 13 Jan 2022 18:22:54 +0000</pubDate>
      <link>https://dev.to/heroku/properly-managing-django-media-static-files-on-heroku-o2l</link>
      <guid>https://dev.to/heroku/properly-managing-django-media-static-files-on-heroku-o2l</guid>
      <description>&lt;p&gt;This article will walk through how we correctly persist static &amp;amp; media files for a Django application hosted on Heroku. As a bonus, it will also explain how we can satisfy the additional constraint of specifying private versus public media files based on model definitions.&lt;/p&gt;

&lt;p&gt;Before I begin, this post extends from &lt;a href="https://testdriven.io/blog/storing-django-static-and-media-files-on-amazon-s3/" rel="noopener noreferrer"&gt;this TestDriven.io article&lt;/a&gt; that was written awhile back. I frequent it often when setting up my projects, and have built some extra functionality on top of it over the years. I decided to create a more focused post that references Heroku &amp;amp; Bucketeer with these extra features after helping an individual on StackOverflow.&lt;/p&gt;


&lt;div class="ltag__stackexchange--container"&gt;
  &lt;div class="ltag__stackexchange--title-container"&gt;
    
      &lt;div class="ltag__stackexchange--title"&gt;
        &lt;div class="ltag__stackexchange--header"&gt;
          &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fstackoverflow-logo-b42691ae545e4810b105ee957979a853a696085e67e43ee14c5699cf3e890fb4.svg" alt=""&gt;
          &lt;a href="https://stackoverflow.com/questions/70481782/how-to-add-image-for-image-field-for-a-model-instance-in-django-admin-panel-on-h/70483913#70483913" rel="noopener noreferrer"&gt;
            &lt;span class="title-flare"&gt;answer&lt;/span&gt; re: How to add image for image field for a model instance in django admin panel on heroku?
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="ltag__stackexchange--post-metadata"&gt;
          &lt;span&gt;Dec 26 '21&lt;/span&gt;
        &lt;/div&gt;
      &lt;/div&gt;
      &lt;a class="ltag__stackexchange--score-container" href="https://stackoverflow.com/questions/70481782/how-to-add-image-for-image-field-for-a-model-instance-in-django-admin-panel-on-h/70483913#70483913" rel="noopener noreferrer"&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fstackexchange-arrow-up-eff2e2849e67d156181d258e38802c0b57fa011f74164a7f97675ca3b6ab756b.svg" alt=""&gt;
        &lt;div class="ltag__stackexchange--score-number"&gt;
          0
        &lt;/div&gt;
        &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fstackexchange-arrow-down-4349fac0dd932d284fab7e4dd9846f19a3710558efde0d2dfd05897f3eeb9aba.svg" alt=""&gt;
      &lt;/a&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--body"&gt;
    
&lt;blockquote&gt;
&lt;p&gt;I think it's because I turn off a PC, where I took these images&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This probably is not it, because Heroku doesn't have access to the files on your computer.&lt;/p&gt;

&lt;p&gt;When you upload a file to the Django admin, it looks at the &lt;code&gt;DEFAULT_FILE_STORAGE&lt;/code&gt; settings configuration to determine how to…&lt;/p&gt;
    
  &lt;/div&gt;
  &lt;div class="ltag__stackexchange--btn--container"&gt;
    &lt;a href="https://stackoverflow.com/questions/70481782/how-to-add-image-for-image-field-for-a-model-instance-in-django-admin-panel-on-h/70483913#70483913" class="ltag__stackexchange--btn" rel="noopener noreferrer"&gt;Open Full Answer&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;So without further ado, let's first dive into what static &amp;amp; media files are and how Heroku dynos manage their filesystem? &lt;/p&gt;

&lt;h2&gt;
  
  
  What are Media &amp;amp; Static Files
&lt;/h2&gt;

&lt;p&gt;If you are working with a &lt;a href="http://djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; project, then you inevitably have all of your Python application code written around a bunch of &lt;code&gt;.py&lt;/code&gt; files. These are the code paths of your application, and the end-user - hopefully - never actually sees these files or their contents.&lt;/p&gt;

&lt;p&gt;Outside of these business-logic files, it is common to serve users directly from your server's file system. For these &lt;strong&gt;static files&lt;/strong&gt;, Django doesn't need to run any code for them; the framework looks up the file and returns the contents for the requesting user to view.&lt;/p&gt;

&lt;p&gt;Some examples of static files include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Non-templated HTML&lt;/li&gt;
&lt;li&gt;CSS &amp;amp; JavaScript files to make your page look nice&lt;/li&gt;
&lt;li&gt;User profile pictures&lt;/li&gt;
&lt;li&gt;Generated PDFs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Media files&lt;/strong&gt; in Django are a particular variant of static files. Media files are read from the server's file system as well. Unlike static files, though, they are usually generated files uploaded by users or generated by your application and are associated with a model's &lt;a href="https://docs.djangoproject.com/en/4.0/ref/models/fields/#filefield" rel="noopener noreferrer"&gt;&lt;code&gt;FileField&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.ImageField" rel="noopener noreferrer"&gt;&lt;code&gt;ImageField&lt;/code&gt;&lt;/a&gt;. In the examples above, user profile pictures and generated PDFs are typical examples of media files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Django with Media &amp;amp; Static Files
&lt;/h3&gt;

&lt;p&gt;When a new media file is uploaded to a Django web application, the framework looks at the &lt;code&gt;DEFAULT_FILE_STORAGE&lt;/code&gt; settings configuration to determine how to store that file. &lt;a href="https://docs.djangoproject.com/en/4.0/ref/settings/#default-file-storage" rel="noopener noreferrer"&gt;By default&lt;/a&gt;, it uses the &lt;a href="https://docs.djangoproject.com/en/4.0/ref/files/storage/#django.core.files.storage.FileSystemStorage" rel="noopener noreferrer"&gt;&lt;code&gt;django.core.files.storage.FileSystemStorage&lt;/code&gt; class&lt;/a&gt;, which is what most projects start off as having configured. This implementation looks at the &lt;a href="https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-MEDIA_ROOT" rel="noopener noreferrer"&gt;&lt;code&gt;MEDIA_ROOT&lt;/code&gt; configuration&lt;/a&gt; that is defined in the &lt;code&gt;settings.py&lt;/code&gt; file and copies the uploaded file contents to &lt;a href="https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.FileField.upload_to" rel="noopener noreferrer"&gt;a deterministically-created file path&lt;/a&gt; under that given &lt;code&gt;MEDIA_ROOT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, if the &lt;code&gt;MEDIA_ROOT&lt;/code&gt; is set as &lt;code&gt;/var/www/media&lt;/code&gt;, all uploaded files will be copied and written to a location under &lt;code&gt;/var/www/media/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Heroku with Media &amp;amp; Static Files
&lt;/h3&gt;

&lt;p&gt;Storing these static files on your server's disk file system is okay until you start to work with a containerization platform such as &lt;a href="https://www.heroku.com/home" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt;. To explain why this is the case, it helps to take a step back.&lt;/p&gt;

&lt;p&gt;When downloading files on your personal computer, it's okay that these get written to the file system - usually under &lt;code&gt;~/Downloads&lt;/code&gt; or somewhere similar. This download is because you &lt;em&gt;expect&lt;/em&gt; your computer's file system to persist across restarts and shutdowns; if you download a file and restart your computer, that downloaded file should still be there once the laptop is finished restarting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://heroku.com/home" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; uses &lt;a href="https://www.docker.com/resources/what-container" rel="noopener noreferrer"&gt;containerization&lt;/a&gt; to execute customer workloads. One fact of this environment is that the associated file systems do not persist across restarts and reschedules. &lt;a href="https://www.heroku.com/dynos" rel="noopener noreferrer"&gt;Heroku dynos&lt;/a&gt; are ephemeral, and they can be destroyed, restarted, and moved without any warning, which &lt;a href="https://devcenter.heroku.com/articles/dynos#ephemeral-filesystem" rel="noopener noreferrer"&gt;replaces the associated filesystem&lt;/a&gt;. This situation means that any uploaded files referenced by &lt;code&gt;FileField's and&lt;/code&gt;ImageField's are just deleted without a trace every time the dyno is restarted, moved, or scaled.&lt;/p&gt;




&lt;h2&gt;
  
  
  Complete Example Codebase
&lt;/h2&gt;

&lt;p&gt;I will be stepping through the process of configuring the Django application for Heroku &amp;amp; S3-compatible storage, but feel free to reference the repository below for the complete code to browse through.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dstarner" rel="noopener noreferrer"&gt;
        dstarner
      &lt;/a&gt; / &lt;a href="https://github.com/dstarner/django-heroku-static-file-example" rel="noopener noreferrer"&gt;
        django-heroku-static-file-example
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Used in my blog post of detailing private &amp;amp; public static files for a Heroku-served Django application
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Properly Managing Django Media &amp;amp; Static Files on Heroku Example&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Used in my blog post of detailing private &amp;amp; public static files for a Heroku-served Django application.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This does include a $5.00 / month &lt;a href="https://elements.heroku.com/addons/bucketeer" rel="nofollow noopener noreferrer"&gt;Bucketeer add-on&lt;/a&gt; as a part of the one-click deployment.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://heroku.com/deploy?template=https://github.com/dstarner/django-heroku-static-file-example" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/dc2056acd0e6ff421bfc2b129417f4f832d626c61d1c083221211d8503a429f7/68747470733a2f2f7777772e6865726f6b7563646e2e636f6d2f6465706c6f792f627574746f6e2e737667" alt="Deploy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dstarner/django-heroku-static-file-example" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h2&gt;
  
  
  Bootstrapping Django on Heroku
&lt;/h2&gt;

&lt;p&gt;This tutorial aims to help you retrofit an existing Django project with S3-compatible storage, but I'll quickly go through the steps I used to set up the example Django application. It may help those new to Django &amp;amp; Heroku or those who encounter bugs following the rest of the setup process.&lt;/p&gt;

&lt;p&gt;You can view the tagged project before the storage change &lt;a href="https://github.com/dstarner/django-heroku-static-file-example/commit/299bbe2403e3c35b4cd905aa61eee974ccdb9558" rel="noopener noreferrer"&gt;at commit &lt;code&gt;299bbe2&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bootstrapped a Django project &lt;code&gt;example&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;a href="https://python-poetry.org/" rel="noopener noreferrer"&gt;&lt;code&gt;poetry&lt;/code&gt;&lt;/a&gt; for dependency management&lt;/li&gt;
&lt;li&gt;All of the Django code is under the &lt;code&gt;example&lt;/code&gt; package, and the &lt;code&gt;manage.py&lt;/code&gt; file is in the root. I've always found this structure cleaner than the &lt;a href="https://docs.djangoproject.com/en/4.0/ref/applications/" rel="noopener noreferrer"&gt;Django apps&lt;/a&gt; defined in the project root.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Configured the project for Heroku

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pypi.org/project/django-heroku/" rel="noopener noreferrer"&gt;&lt;code&gt;django-heroku&lt;/code&gt; package&lt;/a&gt; to automatically configure &lt;code&gt;ALLOWED_HOSTS&lt;/code&gt;, &lt;code&gt;DATABASE_URL&lt;/code&gt;, and more. This reduces the headache of deploying Django on Heroku considerably&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://devcenter.heroku.com/articles/procfile" rel="noopener noreferrer"&gt;&lt;code&gt;Procfile&lt;/code&gt;&lt;/a&gt; that runs a &lt;a href="https://gunicorn.org/" rel="noopener noreferrer"&gt;&lt;code&gt;gunicorn&lt;/code&gt; process&lt;/a&gt; for managing the WSGI application&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;app.json&lt;/code&gt; is defined with some fundamental configuration values and resources defined for the project to work&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;release&lt;/code&gt; process definition in the &lt;code&gt;Procfile&lt;/code&gt; and an associated &lt;code&gt;scripts/release.sh&lt;/code&gt; script that runs staticfile collection and database migrations&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introducing Heroku's Bucketeer Add-On
&lt;/h2&gt;

&lt;p&gt;Before we can start managing static and media files, the Django application needs a persistent place to store the files. Again, we can look to &lt;a href="https://elements.heroku.com/addons" rel="noopener noreferrer"&gt;Heroku's extensive list of Add-Ons&lt;/a&gt; for s3-compatible storage. Ours of choice will be one called &lt;strong&gt;Bucketeer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Heroku's &lt;a href="https://elements.heroku.com/addons/bucketeer" rel="noopener noreferrer"&gt;Bucketeer add-on&lt;/a&gt; provides an &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;AWS S3 storage&lt;/a&gt; bucket to upload and download files for our application. The Django application will use this configured bucket to store files uploaded by the server and download them from the S3 when a user requests the files.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you'd like to learn more about AWS S3, the widely-popular data storage solution that Bucketeer is built upon, you can read the &lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html" rel="noopener noreferrer"&gt;S3 user documentation&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;It is worth mentioning that the base plan for Bucketeer - &lt;code&gt;Hobbyist&lt;/code&gt; - is  $5 per month.&lt;/strong&gt; If you plan on spinning up the one-click example posted above, it should only cost a few cents if you &lt;a href="https://help.heroku.com/LGKL6LTN/how-do-i-delete-destroy-a-heroku-application" rel="noopener noreferrer"&gt;proactively destroy the application&lt;/a&gt; when you are done using it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Including the Bucketeer Add-On
&lt;/h3&gt;

&lt;p&gt;To include the &lt;a href="https://elements.heroku.com/addons/bucketeer" rel="noopener noreferrer"&gt;Bucketeer add-on&lt;/a&gt; in our application, we can configure it through the Heroku CLI, web dashboard, or via the project's &lt;code&gt;app.json&lt;/code&gt; file. We will use the third method of including the add-on in an &lt;code&gt;app.json&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;If the project does not have one already, we can create the basic structure listed below, with the critical part being the addition of the &lt;code&gt;"add-ons"&lt;/code&gt; configuration. This array defines the &lt;code&gt;"bucketeer:hobbyist"&lt;/code&gt; resource that our application will use, and Heroku will install the add-on into our application if it does not already exist. We also include the &lt;code&gt;" as"&lt;/code&gt; keyword, which will preface the associated configuration variables with the term &lt;code&gt;BUCKETEER&lt;/code&gt;. This prefacing is helpful to keep the generated configuration value names deterministic because, by default, Heroku will generate the prefix as a random color.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ... rest above&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;addons&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="c1"&gt;// ...other addons...&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bucketeer:hobbyist&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;as&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BUCKETEER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With the required resources being defined, we can start integrating with our storage add-on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Our Storage Solution
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://django-storages.readthedocs.io/en/latest/" rel="noopener noreferrer"&gt;&lt;code&gt;django-storages&lt;/code&gt; package&lt;/a&gt; is a collection of custom, reuseable storage backends for Django. It aids immensely in saving static and media files to different cloud &amp;amp; storage provider options. &lt;a href="https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html" rel="noopener noreferrer"&gt;One of the supported storage providers is S3&lt;/a&gt;, which our Bucketeer add-on is built on. We will leverage the S3 &lt;code&gt;django-storages&lt;/code&gt; backend to handle different file types.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing &lt;code&gt;django-storages&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Begin by installing the &lt;code&gt;django-storages&lt;/code&gt; package and the related &lt;a href="https://aws.amazon.com/sdk-for-python/" rel="noopener noreferrer"&gt;&lt;code&gt;boto3&lt;/code&gt; package&lt;/a&gt; used to interface with AWS's S3. We will also lock our dependencies to ensure &lt;code&gt;poetry&lt;/code&gt; and our Heroku deployment continue to work as expected.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

poetry add django-storages boto3 &amp;amp;&amp;amp; poetry lock


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Then, just like most Django-related packages, &lt;code&gt;django-storages&lt;/code&gt; will need to be added to the project's &lt;code&gt;INSTALLED_APPS&lt;/code&gt; in the projects &lt;code&gt;settings.py&lt;/code&gt; file. This will allow Django to load the appropriate code flows as the application starts up.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# example/config/settings.py
&lt;/span&gt;&lt;span class="n"&gt;INSTALLED_APPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# ... django.X.Y apps above
&lt;/span&gt;    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;storages&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# ... custom project apps below
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Implementing Static, Public &amp;amp; Private Storage Backends
&lt;/h3&gt;

&lt;p&gt;We will return to the &lt;code&gt;settings.py&lt;/code&gt; file later to configure the usage of &lt;code&gt;django-storages&lt;/code&gt;, but before that can be done, we will implement three custom &lt;a href="https://docs.djangoproject.com/en/4.0/howto/custom-file-storage/" rel="noopener noreferrer"&gt;storage backends&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A storage backend for static files - CSS, Javascript, and publicly accessible images - that will be stored in version control - aka &lt;code&gt;git&lt;/code&gt; - and shipped with the application&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;public&lt;/strong&gt; storage backend for dynamic media files that are not stored in version control, such as uploaded files and attachments&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;private&lt;/strong&gt; storage backend for dynamic media files that are not stored in the version control that require extra access to be viewed, such as per-user reports and potentially profile images. Files managed by this backend require an access key and will block access to those without a valid key.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can extend from &lt;code&gt;django-storages&lt;/code&gt; 's &lt;code&gt;S3Boto3Storage&lt;/code&gt; storage backend to create these. The following code can be directly "copy and paste "'d into your project. The different &lt;code&gt;settings&lt;/code&gt; attributes read in the module will be written shortly, so &lt;strong&gt;do not expect this code to work if you import it right now&lt;/strong&gt;. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# FILE: example/utils/storage_backends.py
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;storages.backends.s3boto3&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;S3Boto3Storage&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StaticStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S3Boto3Storage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Used to manage static files for the web server&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STATIC_LOCATION&lt;/span&gt;
    &lt;span class="n"&gt;default_acl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STATIC_DEFAULT_ACL&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PublicMediaStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S3Boto3Storage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Used to store &amp;amp; serve dynamic media files with no access expiration&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PUBLIC_MEDIA_LOCATION&lt;/span&gt;
    &lt;span class="n"&gt;default_acl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PUBLIC_MEDIA_DEFAULT_ACL&lt;/span&gt;
    &lt;span class="n"&gt;file_overwrite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PrivateMediaStorage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S3Boto3Storage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Used to store &amp;amp; serve dynamic media files using access keys
    and short-lived expirations to ensure more privacy control
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PRIVATE_MEDIA_LOCATION&lt;/span&gt;
    &lt;span class="n"&gt;default_acl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PRIVATE_MEDIA_DEFAULT_ACL&lt;/span&gt;
    &lt;span class="n"&gt;file_overwrite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
    &lt;span class="n"&gt;custom_domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The attributes listed in each storage backend class perform the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;location&lt;/code&gt;: This dictates the parent directory used in the S3 bucket for associated files. This is concatenated with the generated path provided by a &lt;code&gt;FileField&lt;/code&gt; or &lt;code&gt;ImageField&lt;/code&gt; 's &lt;a href="https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.FileField.upload_to" rel="noopener noreferrer"&gt;&lt;code&gt;upload_to&lt;/code&gt; method&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default_acl&lt;/code&gt;: This dictates the access policy required for reading the files. This dictates the storage backend's access control through values of &lt;code&gt;None&lt;/code&gt;, &lt;code&gt;public-read&lt;/code&gt;, and &lt;code&gt;private&lt;/code&gt;. &lt;code&gt;django-storages&lt;/code&gt; and the &lt;code&gt;S3Boto3Storage&lt;/code&gt; parent class with translate these into object policies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;file_overwrite&lt;/code&gt;: In most cases, it's better not to overwrite existing files if we update a specific path. With this set to &lt;code&gt;False&lt;/code&gt;, a unique suffix will be appended to the path to prevent naming collisions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;custom_domain&lt;/code&gt;: Disabled here, but you can enable it if you want to use &lt;a href="https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html#cloudfront" rel="noopener noreferrer"&gt;AWS's CloudFront and &lt;code&gt;django-storage&lt;/code&gt; to serve from it&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configure Settings to Use the Storage Backends
&lt;/h3&gt;

&lt;p&gt;With our storage backends defined, we can configure them to be used in different situations via the &lt;code&gt;settings.py&lt;/code&gt; file. However, it is challenging to use S3 and these different cloud storage backends while in development, and I've always been a proponent of keeping all resources and files "local" to the development machine, so we will create a logic path that will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the local filesystem to store static and media files for convenience. The Django server will be responsible for serving these files directly.&lt;/li&gt;
&lt;li&gt;Use the custom S3 storage backends when an environment variable is enabled. We will use the &lt;code&gt;S3_ENABLED&lt;/code&gt; variable to control this, enabling it in our Heroku configuration variables.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, we will assume that you have a relatively vanilla &lt;code&gt;settings.py&lt;/code&gt; file concerning the static- &amp;amp; media-related variables. For reference, a new project should have a block that looks similar to the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/
&lt;/span&gt;
&lt;span class="n"&gt;STATIC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;static/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="n"&gt;STATIC_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BASE_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;collected-static&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We will design a slightly advanced control flow that will seamlessly handle the two cases defined above. In addition, it will provide enough control to override each part of the configuration as needed.&lt;/p&gt;

&lt;p&gt;Since there are already default values for the static file usage, we can add default values for media file usage. These will be used when serving files locally from the server while in development mode.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;STATIC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/static/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;STATIC_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BASE_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;collected-static&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

&lt;span class="n"&gt;MEDIA_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/media/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BASE_DIR&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;collected-media&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To begin the process of including S3, let's create the controls to manage if we should serve static &amp;amp; media files from the local server or through the S3 storage backend. We will create three variables&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;S3_ENABLED&lt;/code&gt;: controls whether media &amp;amp; static files should use S3 storage by default&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LOCAL_SERVE_MEDIA_FILES&lt;/code&gt;: controls whether media files should use S3 storage. Defaults to the negated &lt;code&gt;S3_ENABLED&lt;/code&gt; value&lt;/li&gt;
&lt;li&gt; &lt;code&gt;LOCAL_SERVE_STATIC_FILES&lt;/code&gt;: controls whether static files should use S3 storage. Defaults to the negated &lt;code&gt;S3_ENABLED&lt;/code&gt; value&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decouple&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;  &lt;span class="c1"&gt;# import explained below
&lt;/span&gt;
&lt;span class="c1"&gt;# ...STATIC and MEDIA settings here...
&lt;/span&gt;
&lt;span class="c1"&gt;# The following configs determine if files get served from the server or an S3 storage
&lt;/span&gt;&lt;span class="n"&gt;S3_ENABLED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;S3_ENABLED&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_SERVE_MEDIA_FILES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LOCAL_SERVE_MEDIA_FILES&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;S3_ENABLED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;LOCAL_SERVE_STATIC_FILES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;LOCAL_SERVE_STATIC_FILES&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cast&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;S3_ENABLED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;LOCAL_SERVE_MEDIA_FILES&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;LOCAL_SERVE_STATIC_FILES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;S3_ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;S3_ENABLED must be true if either media or static files are not served locally&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the example above, we are using the &lt;a href="https://pypi.org/project/python-decouple/" rel="noopener noreferrer"&gt;&lt;code&gt;python-decouple&lt;/code&gt; package&lt;/a&gt; to make it easier to read and cast environment variables to Python variables. I highly recommend this package when working with &lt;code&gt;settings.py&lt;/code&gt; configurations. We also include a value check to ensure consistency across these three variables. If all three variables are defined in the environment but conflict with one another, the program will throw an error.&lt;/p&gt;

&lt;p&gt;We can now start configuring the different configuration variables required by our file storage backends based on those control variables' value(s). We begin by including some S3 configurations required whether we are serving static, media, or both types of files.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;S3_ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BUCKETEER_AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BUCKETEER_AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BUCKETEER_BUCKET_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_REGION_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BUCKETEER_AWS_REGION&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;AWS_DEFAULT_ACL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_SIGNATURE_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;S3_SIGNATURE_VERSION&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3v4&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_ENDPOINT_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AWS_STORAGE_BUCKET_NAME&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.s3.amazonaws.com&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;AWS_S3_OBJECT_PARAMETERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CacheControl&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;max-age=86400&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above defines some of the variables required by the &lt;a href="https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html" rel="noopener noreferrer"&gt;&lt;code&gt;django-storages&lt;/code&gt; S3 backend&lt;/a&gt; and sets the values to environment configurations that are provided by the Bucketeer add-on. As previously mentioned, all of the add-on environment variables are prefixed with &lt;code&gt;BUCKETEER_&lt;/code&gt;. The &lt;code&gt;S3_SIGNATURE_VERSION&lt;/code&gt; environment variable is not required and &lt;em&gt;most likely&lt;/em&gt; does not need to be included.&lt;/p&gt;

&lt;p&gt;With the S3 configuration together, we can reference the &lt;code&gt;LOCAL_SERVE_MEDIA_FILES&lt;/code&gt; and &lt;code&gt;LOCAL_SERVE_STATIC_FILES&lt;/code&gt; control variables to override the default static and media file settings if they are desired to be served via S3.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;LOCAL_SERVE_STATIC_FILES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;STATIC_DEFAULT_ACL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;public-read&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;STATIC_LOCATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;static&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;STATIC_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_ENDPOINT_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;STATIC_LOCATION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;STATICFILES_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;example.utils.storage_backends.StaticStorage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice the last line where &lt;code&gt;STATICFILES_STORAGE&lt;/code&gt; is set to the custom Backend we created. That ensures it follows the location &amp;amp; ACL (Access Control List) policies that we configured initially. With this configuration, all static files will be placed under &lt;code&gt;/static/&lt;/code&gt; in the bucket, but feel free to update &lt;code&gt;STATIC_LOCATION&lt;/code&gt; if desired.&lt;/p&gt;

&lt;p&gt;We can configure a very similar situation for media files.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;LOCAL_SERVE_MEDIA_FILES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;PUBLIC_MEDIA_DEFAULT_ACL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;public-read&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;PUBLIC_MEDIA_LOCATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media/public&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="n"&gt;MEDIA_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;AWS_S3_ENDPOINT_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PUBLIC_MEDIA_LOCATION&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;DEFAULT_FILE_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;example.utils.storage_backends.PublicMediaStorage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;

    &lt;span class="n"&gt;PRIVATE_MEDIA_DEFAULT_ACL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;private&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;PRIVATE_MEDIA_LOCATION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;media/private&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="n"&gt;PRIVATE_FILE_STORAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;example.utils.storage_backends.PrivateMediaStorage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The big difference here is that we have configured &lt;em&gt;two&lt;/em&gt; different storage backends for media files; one for publicly accessible objects and one for objects that require an access token. When the file is requested, this token will be generated internally by &lt;code&gt;django-storages&lt;/code&gt; so you do not have to worry about anonymous public access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Local Development Serving
&lt;/h3&gt;

&lt;p&gt;Since we will have &lt;code&gt;S3_ENABLED&lt;/code&gt; set to &lt;code&gt;False&lt;/code&gt; in our local development environment, it will serve static and media files locally through the Django server instead of from S3. We will need to configure the URL routing to handle this scenario. We can configure our &lt;code&gt;urls.py&lt;/code&gt; file to serve the appropriate files like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.conf.urls.static&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;static&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.contrib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;


&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;admin/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOCAL_SERVE_STATIC_FILES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STATIC_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;document_root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STATIC_ROOT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LOCAL_SERVE_MEDIA_FILES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MEDIA_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;document_root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MEDIA_ROOT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will locally serve the static or media files based on the values of the &lt;code&gt;LOCAL_SERVE_STATIC_FILES&lt;/code&gt; and &lt;code&gt;LOCAL_SERVE_MEDIA_FILES&lt;/code&gt; settings variables we defined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Enabling S3 Storage
&lt;/h3&gt;

&lt;p&gt;We can enable these storages and our add-on in the &lt;code&gt;app.json&lt;/code&gt; file to start using these storage backends. This will effectively disable &lt;code&gt;LOCAL_SERVE_STATIC_FILES&lt;/code&gt; and &lt;code&gt;LOCAL_SERVE_MEDIA_FILES&lt;/code&gt; to start serving both via S3 when deployed to Heroku.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...rest of configs...&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;env&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...rest of envs...&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;S3_ENABLED&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enable to upload &amp;amp; serve static and media files from S3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;True&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Using the Private Storage
&lt;/h3&gt;

&lt;p&gt;By default, Django will use the &lt;code&gt;PublicMediaStorage&lt;/code&gt; class for uploading media files, meaning the contents will be publicly accessible to anyone with the link. However, a model can utilize the &lt;code&gt;PrivateMediaStorage&lt;/code&gt; backend when desired, which will create short-lived access tokens that prevent the public from viewing the associated object.&lt;/p&gt;

&lt;p&gt;The below is an example of using public and private media files on the same model.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;example.utils.storage_backends&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PrivateMediaStorage&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Organization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A sample Organization model with public and private file field usage
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;logo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;help_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;A publicly accessible company logo&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;expense_report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;FileField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;help_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;The private expense report requires a short-lived access token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
        &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;PrivateMediaStorage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# will create private files
&lt;/span&gt;    &lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;You can see the code for this complete example &lt;a href="https://github.com/dstarner/django-heroku-static-file-example/commit/265becc025cd41ebe1de6cb489150b7f6b110f23" rel="noopener noreferrer"&gt;at commit &lt;code&gt;265becc&lt;/code&gt;&lt;/a&gt;. This configuration will allow your project to scale efficiently using &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt; on &lt;a href="https://www.heroku.com/home" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; using &lt;a href="https://elements.heroku.com/addons/bucketeer" rel="noopener noreferrer"&gt;Bucketeer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a future post, we will discuss how to upload and set these files using vanilla Django &amp;amp; &lt;a href="https://www.django-rest-framework.org/" rel="noopener noreferrer"&gt;Django REST Framework&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As always, if you find any bugs, issues, or unclear explanations, please reach out to me so I can improve the tutorial &amp;amp; experience for future readers.&lt;/p&gt;

&lt;p&gt;Take care everyone&lt;/p&gt;

</description>
      <category>django</category>
      <category>heroku</category>
      <category>aws</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploying a Kotlin App to Heroku</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Wed, 13 Oct 2021 11:24:52 +0000</pubDate>
      <link>https://dev.to/heroku/deploying-a-kotlin-app-to-heroku-5d0g</link>
      <guid>https://dev.to/heroku/deploying-a-kotlin-app-to-heroku-5d0g</guid>
      <description>&lt;p&gt;Since its earliest release, Java has touted itself as a "&lt;a href="https://en.wikipedia.org/wiki/Write_once,_run_anywhere"&gt;write once, run everywhere&lt;/a&gt;" programming language. The idea was that a programmer could develop an app in Java, have it compiled down to bytecode, and become an executable that can run on any platform, regardless of operating system or platform. It was able to do so in part by a runtime known as the Java Virtual Machine, or &lt;a href="https://en.wikipedia.org/wiki/Java_virtual_machine"&gt;JVM&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;To Java's credit, the JVM was (and still is!) an incredibly fine-tuned runtime that abstracted away a computer's underlying hardware. While Java as a programming language survives to this day, it is often viewed as cumbersome, stodgy, and representative of an outdated approach to implementing solutions. &lt;/p&gt;

&lt;p&gt;In the last 10 years, more and more languages that run on the JVM have developed, but look and feel nothing like Java. One such language is &lt;a href="https://en.wikipedia.org/wiki/Kotlin_(programming_language)"&gt;Kotlin&lt;/a&gt;. Because of the JVM, it has no real performance advantages over regular Java. Still, its strength lies in the fact that it prioritizes legibility in a way that Java does not. Consider, for example, printing a substring in Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Java
String input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42";
String answer = input.substring(input.indexOf("?") + 1);
System.out.println(answer);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must first get the index of the character you want to be in the substring, add one (since strings are zero-indexed), and call &lt;code&gt;System.out.println&lt;/code&gt; to write to stdout.&lt;/p&gt;

&lt;p&gt;In Kotlin, this is much shorter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Kotlin
val input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42"
val answer = input.substringAfter("?")
println(answer)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kotlin has garnered so much interest that&lt;a href="https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development/"&gt; Google even recommends it over Java for developing Android apps&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In this post, we'll take a quick look at how to develop an app in Kotlin. We'll build a simple API with a PostgreSQL database and deploy it to Heroku to see it live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, you'll need to make sure you've got the following software installed on your machine: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://signup.heroku.com/"&gt;An account on Heroku&lt;/a&gt;. This is completely free and doesn't require any payment information.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://devcenter.heroku.com/articles/heroku-cli#download-and-install"&gt;The Heroku CLI&lt;/a&gt;. Once your application is on Heroku, this will make managing it much easier.&lt;/li&gt;
&lt;li&gt;You'll need to have &lt;a href="https://kotlinlang.org/docs/command-line.html"&gt;Kotlin&lt;/a&gt; installed (&amp;gt;= 1.4).&lt;/li&gt;
&lt;li&gt;You'll also need &lt;a href="https://gradle.org/install/"&gt;Gradle&lt;/a&gt; installed (&amp;gt;= 7.0).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You will also need to be a little familiar with &lt;a href="https://git-scm.com"&gt;Git&lt;/a&gt; and have it installed on your machine.&lt;/p&gt;

&lt;p&gt;We’re going to be using &lt;a href="https://www.jetbrains.com/help/idea/get-started-with-kotlin.html"&gt;the IntelliJ&lt;/a&gt; IDE for this Kotlin app. Their documentation provides some guidance on how to create a new project. Make sure you select the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want to create a Kotlin application that uses Gradle as a build system&lt;/li&gt;
&lt;li&gt;Set the name of the project to kotlin-api&lt;/li&gt;
&lt;li&gt;Set the JDK version to 16. If you don’t have this version installed, you can select &lt;strong&gt;Download JDK&lt;/strong&gt;… from the dropdown, then choose &lt;strong&gt;Oracle Open JDK&lt;/strong&gt; version 16&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the IDE sets everything up, you should have a directory structure that looks roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kotlin-api
├── build.gradle.kts
└── src
    ├── main
    │   ├── kotlin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our Kotlin files will be kept in &lt;code&gt;src/main/kotlin&lt;/code&gt;, and our build logic will be in &lt;em&gt;build.gradle.kts&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;Gradle is a build tool for a variety of languages. It also acts as a dependency management tool, similar to Maven. You’ll already have some lines in your &lt;em&gt;build.gradle.kts&lt;/em&gt; file, which the IDE automatically added to be helpful. You can delete all of that, and paste in these lines instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;plugins {
    id("java")
    id("org.jetbrains.kotlin.jvm") version "1.5.10"
    id("org.springframework.boot") version "2.4.3"

    id("io.spring.dependency-management") version "1.0.11.RELEASE"
}

group "com.example"
version "0.0.1"

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib") 

    implementation("org.springframework.boot:spring-boot-starter-web")    
    implementation("org.springframework.boot:spring-boot-starter")

    developmentOnly("org.springframework.boot:spring-boot-devtools")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These lines specify our project's dependencies and where to find them. For example, we want to use  &lt;code&gt;[org.springframework.boot](https://plugins.gradle.org/plugin/org.springframework.boot)&lt;/code&gt; at version 2.4.3, which is why it's defined within the &lt;code&gt;plugins&lt;/code&gt; block. We point out the repositories where the packages can be found—at &lt;code&gt;mavenCentral()&lt;/code&gt;—and which exposed classes we want to use (&lt;code&gt;implementation( "org.springframework.boot:spring-boot-starter-web")&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Let's create two small files to test our setup. Create a file called &lt;code&gt;Application.kt&lt;/code&gt; in the &lt;em&gt;src/main/kotlin&lt;/em&gt; folder and paste in the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
open class Application

fun main(args: Array&amp;lt;String&amp;gt;) {
    SpringApplication.run(Application::class.java, *args)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This starts a default app using the Spring framework. The real magic happens in this next file, &lt;code&gt;Controller.kt&lt;/code&gt;, which you should create alongside &lt;code&gt;Application.kt&lt;/code&gt; in &lt;em&gt;src/main/kotlin&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController

@RestController
class GreetingController {

    @GetMapping("/{name}")
    fun get(@PathVariable name: String) = "Hello, $name"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we define a route (&lt;code&gt;@GetMapping("/{name}")&lt;/code&gt;), where &lt;code&gt;{name}&lt;/code&gt; is a dynamic value. By placing this decorator over a Kotlin method (&lt;code&gt;fun get&lt;/code&gt;, or "a function named get"), we're able to match the route to whatever behavior we want—in this case, returning a greeting with the path name for a &lt;code&gt;GET&lt;/code&gt; request. &lt;/p&gt;

&lt;p&gt;In order for the IDE to know how to launch our application, we need to create &lt;a href="https://www.jetbrains.com/help/idea/run-debug-configuration.html"&gt;a run configuration&lt;/a&gt;. At the top of the IDE menu, click the button that says &lt;strong&gt;Add Configuration…&lt;/strong&gt;. Select &lt;strong&gt;Add new run configuration&lt;/strong&gt;, then choose Gradle from the list. For the Gradle project name, enter kotlin-api. In the Tasks field, type &lt;code&gt;bootRun&lt;/code&gt;. &lt;code&gt;&lt;a href="https://docs.spring.io/spring-boot/docs/2.5.0/gradle-plugin/reference/htmlsingle/#running-your-application"&gt;bootRun&lt;/a&gt;&lt;/code&gt; is a Gradle task provided by the Spring framework which will compile our code and start the server. Click &lt;strong&gt;Ok&lt;/strong&gt;; you should now have a green Play button in your IDE menu bar. When you click on this, the IDE will execute  &lt;code&gt;gradle bootRun&lt;/code&gt; to build this Kotlin app and start the server. When that finishes, navigate to &lt;code&gt;http:&lt;em&gt;//localhost:8080/world&lt;/em&gt;&lt;/code&gt;. You should see a nice greeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interacting with the database
&lt;/h2&gt;

&lt;p&gt;Now, let's get to the (somewhat) serious stuff. Suppose we wanted to attach a database to this project. In a Maven/Java world, we'd need to update an XML file and add a reference to a JAR file. In Gradle, we can get by with just adding a few lines to our &lt;code&gt;build.gradle.kts&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dependencies {
    # ...

    implementation("com.zaxxer:HikariCP:4.0.3")
    runtimeOnly("org.postgresql:postgresql")

    # ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we've included &lt;a href="https://github.com/brettwooldridge/HikariCP"&gt;HikariCP&lt;/a&gt; in our project, which is a popular database connection driver. We also indicate that we want to "load" the &lt;code&gt;org.postgresql&lt;/code&gt; library during runtime. With just these two lines, we've let our configuration know that we want to interact with a PostgreSQL database. If you already have a PostgreSQL database running locally, that's great. You'll be able to continue the rest of this guide locally and see the results when browsing localhost. If you don't have PostgreSQL, don't fret—we'll show you just how easy it is to deploy this app on Heroku, which will take care of the infrastructure for you. &lt;/p&gt;

&lt;p&gt;Head back to &lt;code&gt;Controller.kt&lt;/code&gt;, and replace it with the contents below. This takes some of what we had from before but adds to it. We'll go over the changes shortly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package com.example
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.http.MediaType
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import java.net.URI
import javax.sql.DataSource

@RestController
class GreetingController {

   val dataSource = dataSource()
   val connection = dataSource.connection

   @GetMapping("/{name}")

   fun get(@PathVariable name: String) = "Hello, $name"

   @PostMapping(value = ["/add-name"], consumes = [MediaType.TEXT_PLAIN_VALUE])
   fun post(@RequestBody requestBody: String) : String {
       initDb()
       val stmt = connection.createStatement()
       stmt.executeUpdate("INSERT INTO names values('$requestBody')")
       return "Added $requestBody"
   }

   @GetMapping("/everyone")

   fun getAll() : String {
       initDb()
       val stmt = connection.createStatement()
       val rs = stmt.executeQuery("SELECT name FROM names")
       val output = ArrayList&amp;lt;String&amp;gt;()
       while (rs.next()) {
           output.add(rs.getString("name"))
       }
       val names = output.joinToString(", ")
       return "Here are the names: $names"
   }

   internal fun initDb() {
       val stmt = connection.createStatement()
       stmt.executeUpdate("CREATE TABLE IF NOT EXISTS names (name text)")
   }

   internal fun dataSource(): HikariDataSource {
       val config = HikariConfig()
       var dbUri = URI(System.getenv("DATABASE_URL") ?: "postgresql://localhost:5432/")
       var dbUserInfo =  dbUri.getUserInfo()
       var username: String?; var password: String?;
       if (dbUserInfo != null) {
           username = dbUserInfo.split(":").get(0)
           password = dbUserInfo.split(":").get(1)
       } else {
           username = System.getenv("DATABASE_USERNAME") ?: null
           password = System.getenv("DATABASE_PASSWORD") ?: null
       }
       if (username != null) {
           config.setUsername(username)
       }
       if (password != null) {
           config.setPassword(password)
       }
       val dbUrl = "jdbc:postgresql://${dbUri.getHost()}:${dbUri.getPort()}${dbUri.getPath()}"
       config.setJdbcUrl(dbUrl)
       return HikariDataSource(config)
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's quite a lot going on here! Let's start from the bottom. We define a function called &lt;code&gt;dataSource&lt;/code&gt; which provides a connection to our database. Because we're building&lt;a href="https://12factor.net/config"&gt; a 12-Factor app&lt;/a&gt;, our database credentials are stored in an environment variable called &lt;code&gt;DATABASE_URL&lt;/code&gt;. We fetch that URL and pull out the username and password from it if one exists. If not, we check another two environment variables for that information—&lt;code&gt;DATABASE_USERNAME&lt;/code&gt; and &lt;code&gt;DATABASE_PASSWORD&lt;/code&gt;. We then put all that information together into a format that the database connector needs. The &lt;code&gt;initDb&lt;/code&gt; function creates a table called &lt;code&gt;names&lt;/code&gt;, with a single text column called &lt;code&gt;name&lt;/code&gt;. The &lt;code&gt;/everyone&lt;/code&gt; endpoint has a &lt;code&gt;@GetMapping&lt;/code&gt; decorator just like before. This defines a &lt;code&gt;GET /everyone&lt;/code&gt; route, which gets all the names from the database.&lt;/p&gt;

&lt;p&gt;Finally, we've added something rather new: a &lt;code&gt;@PostMapping&lt;/code&gt; decorator. Here, we need to define what types of content this &lt;code&gt;POST&lt;/code&gt; route can accept. In this case, it &lt;code&gt;consumes&lt;/code&gt; a &lt;code&gt;TEXT_PLAIN_VALUE&lt;/code&gt; media type (in other words, &lt;code&gt;"Content-Type: text/plain"&lt;/code&gt;). Whatever string of information we put in the request body will be added to the database. In just a few lines, we've built a small API that we can add to and query.&lt;/p&gt;

&lt;p&gt;If you start this server now—and if you have PostgreSQL running locally—you should be able to interact with it. Try making the following request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl -H "Content-Type: text/plain" -X POST http://localhost:8080/add-name -d 'Frank'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you navigate to &lt;code&gt;http://localhost:8080/everyone&lt;/code&gt;, you'll see that &lt;code&gt;Frank&lt;/code&gt; was included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploying to Heroku
&lt;/h2&gt;

&lt;p&gt;It's time to see just how easy it is to get Kotlin running on Heroku. First, we need to create a file that's specific to Heroku:&lt;a href="https://devcenter.heroku.com/articles/procfile"&gt; the Procfile&lt;/a&gt;. This text file defines how our application should boot and run. &lt;/p&gt;

&lt;p&gt;Create a file named &lt;em&gt;Procfile&lt;/em&gt; in the root level directory, right next to your &lt;em&gt;build.gradle.kts&lt;/em&gt; file. Copy-paste the following lines into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: java -jar build/libs/kotlin-api.jar --server.port=$PORT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we're saying that we want Heroku to run &lt;code&gt;java -jar build/libs/kotlin-api.jar&lt;/code&gt;. That JAR is packaged and built during the deployment process; Heroku will create it automatically for us because it knows how to execute the Gradle task to do so. We are also binding the &lt;code&gt;$PORT&lt;/code&gt; environment variable so that Heroku knows which port the server is listening to.&lt;/p&gt;

&lt;p&gt;Next, run the following Git commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git init
$ git add .
$ git commit -m "Preparing my first Kotlin app"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we have the Heroku CLI installed, we can &lt;a href="https://devcenter.heroku.com/articles/creating-apps"&gt;call heroku create on the command line to generate an app&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku create
Creating app... done, ⬢ desolate-plains-67007
Created http://desolate-plains-67007.herokuapp.com/ | git@heroku.com:desolate-plains-67007.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app will be assigned a random name—in this example, it's &lt;code&gt;desolate-plains-67007&lt;/code&gt;—as well as a publicly accessible URL.&lt;/p&gt;

&lt;p&gt;In order to provision a database, we use the &lt;code&gt;&lt;a href="https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-addons-all-app-app"&gt;heroku addons&lt;/a&gt;&lt;/code&gt; command. Calling it without arguments will let us know if any exist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku addons
No add-ons for app desolate-plains-67007.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No add-ons exist for our app, which makes sense—we just created it! To add a PostgreSQL database, we can use the &lt;code&gt;addons:create&lt;/code&gt; command like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ heroku addons:create heroku-postgresql:hobby-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Heroku offers several tiers of PostgreSQL databases. &lt;code&gt;hobby-dev&lt;/code&gt; is the free tier, so we can play around with this without paying a dime.&lt;/p&gt;

&lt;p&gt;Your code is ready, your Heroku app is configured—you’re ready to deploy. This is the easy part! Just type out the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git push heroku master
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your code will be pushed to Heroku. From that point on, Heroku will take over. You'll see your build logs scrolling through your terminal. This will show you what Heroku is installing on your behalf and where you are in the build process. After it’s complete, you can visit your special URL in the browser (in this case, &lt;code&gt;https://desolate-plains-67007.herokuapp.com&lt;/code&gt;) and interact with the API on the internet!&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning more
&lt;/h2&gt;

&lt;p&gt;Kotlin's &lt;a href="https://blog.heroku.com/rise-of-kotlin"&gt;performant design and legible syntax&lt;/a&gt;—combined with the ease of Gradle—make it ideal for enterprises that need to rely on battle-tested Java packages. Because of its interoperability with Java, Kotlin is ideal as a transitional language; vast swaths of Java code can be converted into Kotlin while still maintaining a functional app. Deploying to Heroku is smooth, and I didn't even take advantage of the different ways to &lt;a href="https://devcenter.heroku.com/categories/java-support"&gt;fine-tune Java and JVM-based apps for deployment&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>kotlin</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Azure/Heroku Service Bus</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Thu, 04 Feb 2021 15:29:33 +0000</pubDate>
      <link>https://dev.to/heroku/azure-heroku-service-bus-e3p</link>
      <guid>https://dev.to/heroku/azure-heroku-service-bus-e3p</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Background Jobs in Heroku with Azure Service Bus&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Web applications are optimized for throughput and latency to service a high number of HTTP requests as quickly as possible. For improved performance, web applications defer the CPU intensive, IO intensive, time-intensive, and scheduled processing workloads to &lt;strong&gt;background jobs&lt;/strong&gt; that run independently of the user interface. These background jobs must function without intervention from the user interface and should not block a synchronous user and system interaction. Offloading slow and compute or memory-intensive activity to background jobs improves web applications' performance and throughput.&lt;/p&gt;

&lt;p&gt;For example, consider an eCommerce web application that captures a customer’s orders and triggers the background jobs to process the orders further. The application’s background jobs work with the operational data (all orders placed by customers) and the contextual data (orders for a single customer) to update the inventory and shipping systems.&lt;/p&gt;

&lt;p&gt;Heroku supports several queue services as add-ons such as&lt;a href="https://elements.heroku.com/addons/cloudamqp"&gt; RabbitMQ&lt;/a&gt;,&lt;a href="https://elements.heroku.com/addons/heroku-kafka"&gt; Kafka&lt;/a&gt;, and&lt;a href="https://elements.heroku.com/addons/iron_mq"&gt; IronMQ&lt;/a&gt;. However, you are not limited to using add-ons for integrating with cloud queue services. In this example, we will build a background job that processes messages from an Azure Service Bus queue. AWS, Azure, and GCP offer message queues as a service that you can use to extend the capabilities of your Heroku applications. &lt;/p&gt;

&lt;p&gt;Azure Service Bus offers a rich set of features including support for At-Least-Once and At-Most-Once delivery guarantee. Azure Service Bus also offers First In, First Out (FIFO) messages for both point-to-point (queue) and publish/subscribe communication. While Heroku's application platform is simple, easy to scale, and supports low ceremony DevOps integration, Azure supports an array of enterprise grade services of Azure that can be easily integrated. For complex scenarios, you will find it easy to build applications by integrating the right services across the cloud. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Background Jobs in Heroku&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Heroku allows you to compose your application from&lt;a href="https://devcenter.heroku.com/articles/process-model"&gt; various process types&lt;/a&gt; such as web and worker processes. In this demo, we will deploy a simple background worker process that processes messages from a work queue. Heroku allows you to scale the processes in an application independently, which gives you the ability to scale worker instances in proportion to the workload.&lt;/p&gt;

&lt;p&gt;Apart from the worker, a feature-rich queue is the next crucial component of an event-driven worker process.&lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/"&gt; Azure Service Bus&lt;/a&gt; queue service allows consumer processes to lock and process messages independently, enabling you to scale the number of worker dynos and achieve high throughput. Let’s discuss the Azure Service Bus queue service in detail next.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Azure Service Bus Queues&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The Azure Service Bus service includes&lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions"&gt; a reliable queue service and a durable publish/subscribe messaging service&lt;/a&gt;, any of which you can choose based on your needs. Let’s focus on the Azure Service Bus queue service, which offers FIFO message delivery. The message receivers of an Azure Service Bus queue receive the messages in the same sequence in which they were added to the queue by the producer.&lt;/p&gt;

&lt;p&gt;Service Bus queues act as a buffer between the producer and the consumer of the messages. During the peak load period, the producer can enqueue several additional messages to the queue, which the message consumers can keep processing at the same scale as during an average load period. You can create an Azure Service Bus queue using the &lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quickstart-cli"&gt;Azure CLI&lt;/a&gt; and the &lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quickstart-portal"&gt;Azure Portal&lt;/a&gt;, among other options. The Azure Service Bus SDK is available in &lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/"&gt;many popular programming languages&lt;/a&gt; such as C#, Java, Node, and Go.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Demo Application&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I will use Go and the &lt;a href="https://godoc.org/github.com/Azure/azure-service-bus-go"&gt;Azure Service Bus Go package&lt;/a&gt; to build a sample application to demonstrate how we can develop and deploy a background service that reads messages off a work queue, processes them, and prints the results. The following link will take you to the GitHub repository of the application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/rahulrai-in/azsb-heroku-worker"&gt;Source Code&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The application itself is straightforward. It receives messages from the configured Service Bus queue and prints the message body to the console after a small delay. The deliberate processing delay will help me demonstrate that each worker dyno instance receives a different message from the queue and can process the messages independently and thus scale out if required.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Building the Application&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Start your favorite Go code editor such as VSCode, create a folder for your project, and create a module named &lt;strong&gt;sbworker&lt;/strong&gt; using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go mod init tcblabs.net/sbworker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To work with Azure Service Bus, let’s install the&lt;a href="https://github.com/Azure/azure-service-bus-go"&gt; Azure Service Bus Go package&lt;/a&gt; and the &lt;a href="https://github.com/joho/godotenv"&gt;Godotenv package&lt;/a&gt; to load environment variables from a .env file. The Godotenv package makes it easier to work with applications on development machines and CI servers where several applications might run with each requiring their own set of environment variables. You can read more about this package in the README of &lt;a href="https://github.com/joho/godotenv"&gt;its GitHub repository&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get github.com/Azure/azure-service-bus-go
go get github.com/joho/godotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a file named &lt;strong&gt;main.go&lt;/strong&gt; and create the main method in it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func main() {

    // Read env variables from .env file if it exists

    loadEnvFromFileIfExists()

    handler := &amp;amp;MessageHandler{}

    // Set background context

    ctx, cancel := context.WithCancel(context.Background())

    defer cancel()

    connStr := os.Getenv("SERVICEBUS_CONNECTION_STRING")

    qName := os.Getenv("QUEUE_NAME")

    if connStr == "" || qName == "" {

        fmt.Println("FATAL: expected environment variables SERVICEBUS_CONNECTION_STRING or QUEUE_NAME not set")

        return

    }

    // Create a client to communicate with a Service Bus Namespace.

    ns, err := servicebus.NewNamespace(servicebus.NamespaceWithConnectionString(connStr))

    if err != nil {

        fmt.Println(err)

        return

    }

    // Create queue receiver

    q, err := ns.NewQueue(qName)

    if err != nil {

        fmt.Println(err)

        return

    }

    for {

        if err = q.ReceiveOne(ctx, handler); err != nil {

            if innerErr, ok := err.(*amqp.Error); ok &amp;amp;&amp;amp; innerErr.Condition == "com.microsoft:timeout" {

                fmt.Println("➰ Timeout waiting for messages. Entering next loop.")

                continue

            }

            fmt.Println(err)

            return

        }

    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s read the code together. The &lt;code&gt;loadEnvFromFileIfExists&lt;/code&gt; function loads the environment variables from the .env file present in the folder. We will create and populate the .env file later. Also, remember that this feature is only for our convenience. We will use actual environment variables for configuring our application in Heroku.&lt;/p&gt;

&lt;p&gt;Next, we instantiated the message handler that will receive and process the messages we receive from the Service Bus queue. We will discuss the message handler in detail later. Since we intend to build a background application, we created a background context with support for cancellation.&lt;/p&gt;

&lt;p&gt;Next, we fetched the connection string for the Service Bus namespace and the name of the queue from environment variables. We then created a client to communicate with the service bus namespace, and we also created a client to communicate with the queue. A namespace is a container for all messaging components; in this case, the queue.&lt;/p&gt;

&lt;p&gt;Finally, we started the message receiver with the &lt;code&gt;ReceiveOne&lt;/code&gt; function. We handled the particular case of a timeout error, in which case we recurse the loop and reattach the message receiver to the queue. Note that we passed the &lt;code&gt;handler&lt;/code&gt; object to the &lt;code&gt;ReceiveOne&lt;/code&gt; function, which implements the &lt;code&gt;Handler&lt;/code&gt; interface. This interface only requires defining the &lt;code&gt;Handle&lt;/code&gt; function that is invoked whenever the receiver can lock a message for processing on the service bus. Let’s define the struct &lt;code&gt;MessageHandler&lt;/code&gt; next.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type MessageHandler struct{}

func (mh *MessageHandler) Handle(ctx context.Context, msg *servicebus.Message) error {

    fmt.Printf("-&amp;gt; Received message: %s\n", string(msg.Data))

    // Processing of message simulated through delay

    time.Sleep(5 * time.Second)

    fmt.Printf("✔ Finished processing the message: %s\n", string(msg.Data))

    return msg.Complete(ctx)

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The implementation of the function &lt;code&gt;Handle&lt;/code&gt; is straightforward. We log the message data, wait five seconds, and mark the message as complete. Note that you must mark a message as complete after processing; otherwise it will reappear on the queue.&lt;/p&gt;

&lt;p&gt;Finally, let’s define the &lt;code&gt;loadEnvFromFileIfExists&lt;/code&gt; function to help us read and load environment variables from a file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func loadEnvFromFileIfExists() {

    envFile := ".env"

    if _, err := os.Stat(envFile); err == nil {

        if err = godotenv.Load(envFile); err != nil {

            log.Fatalf("Error loading .env file")

        }

    }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a file named &lt;strong&gt;.env&lt;/strong&gt; to the folder. We will add the Service Bus connection string and the name of the queue to this file shortly. The last artifact that you need to add to the project is a Procfile. A &lt;a href="https://devcenter.heroku.com/articles/procfile"&gt;Heroku Procfile&lt;/a&gt; specifies the processes in your application and the commands executed by the applications on startup. Our application is of the worker process type. To start the application, we need to run the command &lt;code&gt;sbworker&lt;/code&gt; to launch the module executable generated after Go compiles our application.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Create the Azure Service Bus Queue&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let’s spin up an Azure namespace and a queue. I prefer to use the &lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quickstart-cli"&gt;Azure CLI&lt;/a&gt;, but you can also use any supported means, such as the &lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quickstart-portal"&gt;Azure portal&lt;/a&gt;. The following commands will create a resource group named &lt;strong&gt;azsb-heroku-worker-rg&lt;/strong&gt;, an Azure Service Bus namespace named &lt;strong&gt;worker-ns&lt;/strong&gt;, and a queue named &lt;strong&gt;messages&lt;/strong&gt; in the namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;az group create -l westus -n azsb-heroku-worker-rg

az servicebus namespace create --resource-group azsb-heroku-worker-rg --name worker-ns --location westus --sku Standard

az servicebus queue create --resource-group azsb-heroku-worker-rg --namespace-name worker-ns --name messages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s now navigate to the &lt;a href="https://portal.azure.com/"&gt;Azure portal&lt;/a&gt; to fetch the connection strings of the namespace. Visit the &lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-quickstart-portal"&gt;Azure portal quickstart guide&lt;/a&gt; for creating Azure Service Bus namespace and queue if you face difficulty navigating through the portal.&lt;/p&gt;

&lt;p&gt;We will create an access policy that grants only the listen permission (receive messages) to the client. Open the Service Bus namespace that you created and click on &lt;strong&gt;Shared access policies&lt;/strong&gt;. In the next blade, click on the &lt;strong&gt;Add&lt;/strong&gt; button, and on the next panel, provide a name for the policy and select &lt;strong&gt;Listen&lt;/strong&gt; from the list of permissions. Finally, click on the &lt;strong&gt;Create&lt;/strong&gt; button to finish creating the policy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5p4f317ppca1b2bkjufv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5p4f317ppca1b2bkjufv.png" alt="" width="800" height="535"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create listen only policy&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After creating the policy, click on it, and copy the connection string value from the next panel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5bx69u9vwi5auc9tf30j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5bx69u9vwi5auc9tf30j.png" alt="" width="800" height="535"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Copy the connection string&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s now apply this value to the .env file that we created earlier as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SERVICEBUS_CONNECTION_STRING=&amp;amp;lt;connection string&amp;gt;

QUEUE_NAME=messages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a .gitignore file to the project and add the pattern .env to avoid committing this file to the Git repository.&lt;/p&gt;

&lt;p&gt;You can try running the program on your system with the command &lt;code&gt;go run main.go&lt;/code&gt; and debug any errors if the application fails to start. Create a GitHub repository and push the code to it. We will connect this repository to Heroku next.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Create Heroku App&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Navigate to the&lt;a href="https://devcenter.heroku.com/articles/heroku-dashboard"&gt; Heroku dashboard&lt;/a&gt; and create an app using the&lt;a href="https://devcenter.heroku.com/articles/dyno-runtime"&gt; Common Runtime&lt;/a&gt; as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnc2eupvqd3bbmu8yxzsb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fnc2eupvqd3bbmu8yxzsb.png" alt="" width="611" height="760"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Create app in Heroku&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After creating your app, add the&lt;a href="https://devcenter.heroku.com/articles/config-vars"&gt; config vars&lt;/a&gt; to it, which Heroku will surface as environment variables to our application. Click on the gears icon, and click on the &lt;strong&gt;Reveal Config Vars&lt;/strong&gt; button as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxzsxa9dpiidoc2jvn64y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxzsxa9dpiidoc2jvn64y.png" alt="" width="588" height="709"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Show config vars&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create two config vars &lt;code&gt;SERVICEBUS_CONNECTION_STRING&lt;/code&gt; and &lt;code&gt;QUEUE_NAME&lt;/code&gt; and set the same value of the variables you set in the .env file earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcdr5vw8u7o5kmgz5up8x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcdr5vw8u7o5kmgz5up8x.png" alt="" width="593" height="398"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Set config vars&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It is now time to connect our GitHub repository to the application. Navigate to the Deployment tab and click on the &lt;strong&gt;Connect to GitHub&lt;/strong&gt; button. You will be asked to log into GitHub and grant access to Heroku to your repositories, which you must accept. Search for your repository and connect it as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flq93jig7u8idp0v3vnku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flq93jig7u8idp0v3vnku.png" alt="" width="611" height="760"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Connect app to GitHub&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the expanded panel of the deployment view, select the branch you want to deploy to Heroku and click on the &lt;strong&gt;Enable Automatic Deploys&lt;/strong&gt; button. Any subsequent commit to your repository now will trigger a build and deployment on Heroku.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flji5h61690ayt418d2km.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flji5h61690ayt418d2km.png" alt="" width="586" height="1050"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Select the repository branch to deploy&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Since we have already committed our code and do not intend to make any changes to our application, click on the &lt;strong&gt;Deploy Branch&lt;/strong&gt; button to immediately kick off a deployment.&lt;/p&gt;

&lt;p&gt;Heroku does not automatically create worker dyno instances upon the first deployment. You must use the Heroku CLI or the portal to select the type and the number of dyno instances that you require. Click on the &lt;strong&gt;Dynos&lt;/strong&gt; tab and click on the &lt;strong&gt;Edit&lt;/strong&gt; button, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2gwiwaieypkw5istz50q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2gwiwaieypkw5istz50q.png" alt="" width="595" height="640"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Edit dyno configuration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the dyno edit view, you can select the compute configuration and the instance count of dynos. Set the count of dyno instances to 2 and click the &lt;strong&gt;Confirm&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd1c71l2k63pd8z65qzp6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fd1c71l2k63pd8z65qzp6.png" alt="" width="592" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Set dyno instance count&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It’s now time to run the application by submitting some messages to it from the Azure portal.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Running the Application&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Launch the Azure portal in your browser and navigate to the queue that you created in the namespace. Click on the &lt;strong&gt;Service Bus Explorer&lt;/strong&gt; option, which will launch the&lt;a href="https://docs.microsoft.com/en-us/azure/service-bus-messaging/explorer"&gt; Service Bus Explorer tool&lt;/a&gt; that you can use to send, receive, and peek (see without lock or delete) messages in your queue. Send a few messages to your queue successively after changing the message text. Remember to keep the Content-type to &lt;code&gt;Text/Plain&lt;/code&gt;, which is what our receiver expects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgts5x56h7lkhu0alxo0c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgts5x56h7lkhu0alxo0c.png" alt="" width="800" height="723"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Send messages to queue&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Open the logs view of your application in the Heroku portal, as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc6y1jfl20emlqem362yn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc6y1jfl20emlqem362yn.png" alt="" width="713" height="345"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;View application logs&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the logs, you can see the two instances processing the messages independently. Also, each receiver instance is independently locking a different message to process, and hence the messages are not duplicating between them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhec9wgkvtqldqgsk52i9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhec9wgkvtqldqgsk52i9.png" alt="" width="715" height="543"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Worker dynos processing the queue messages&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Instead of the intentional delay, you can try adding an actual operation to your application and store the result of processing in a persistent data store. You can also try to add a front end to the application that submits messages to the Service Bus, which will convert this simple background job to a complete application.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This article presented you with the procedure to integrate Azure Service Bus queues with Heroku worker process to build an event-driven background job. Background services are a critical component of event driven architecture which enables building microservices that are decoupled and iterate independently. Since messages placed on the Azure Service Bus are immutable, they can be treated as the source of truth of business events that can be audited.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Rahul Rai for his kind permission to publish this article.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Using Serenade to Code by Voice</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Wed, 27 Jan 2021 16:03:23 +0000</pubDate>
      <link>https://dev.to/heroku/using-serenade-to-code-by-voice-5007</link>
      <guid>https://dev.to/heroku/using-serenade-to-code-by-voice-5007</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Using Serenade to Code By Voice&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The emergence of voice-controlled devices like Siri, Amazon Echo, and Google Home have simplified tasks that previously required a keyboard. But we've only just begun to inhabit a world filled with devices that we control in different ways, one where we can accomplish more by what we say than what we can do.&lt;a href="https://serenade.ai/" rel="noopener noreferrer"&gt; Serenade&lt;/a&gt; aims to tackle an interesting problem: writing code through speech.&lt;/p&gt;

&lt;p&gt;Serenade is a voice-to-code software that's available to plug into several popular IDEs, like VS Code and IntelliJ. Their claim is to allow you to code "using natural speech," and with support for nearly a dozen languages, it's an intriguing proposition. The ability to code through voice commands is more than just a Sci-Fi fantasy. For programmers with carpal tunnel, or other more severe physical ailments, coding can continue to provide them access to the creative endeavor they enjoy—and a living.&lt;/p&gt;

&lt;p&gt;Is Serenade actually viable? In this post, we'll put the program to the test. My colleague Garen and I will attempt to build a Node.js site, with a simple HTML and CSS frontend, using only our voice. Our site will be nothing more than a button that, when clicked, will display a random emoji. To truly immerse ourselves in the hands-free experience, we'll also attempt to deploy the app to&lt;a href="https://www.heroku.com/" rel="noopener noreferrer"&gt; Heroku&lt;/a&gt; using their&lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt; CLI&lt;/a&gt;. Let's get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Set up the server&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;As with most new projects, we'd like to create a new directory, change into it, and install our package dependencies. Since we can only use Serenade within the IDEs they support, we must resort to&lt;a href="https://support.apple.com/en-gb/guide/mac-help/mh40584/mac" rel="noopener noreferrer"&gt; macOS' Dictation feature&lt;/a&gt;, which is lacking and unable to comprehend programmer speak. We must explicitly spell out Unix commands, or the program won't understand them. Furthermore, there doesn't seem to be any way to enforce lowercase, which really irks the directory aesthetic. Perhaps most frustrating of all is the inability to speak filename conventions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/v68RTAWLSoU" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611692954%2Fvideo_to_markdown%2Fimages%2Fyoutube--v68RTAWLSoU-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="Initial setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In order to move this along, we just typed &lt;code&gt;npm i express --save-dev&lt;/code&gt; on the command line, created a new file called server.js, and opened up Visual Studio Code. Hands 1, Voice 0.&lt;/p&gt;

&lt;p&gt;Serenade runs on your computer and detects your IDE, in order to integrate with its functionality. After installing and activating the app, you can pretty much get started by issuing explicit directives. In the event that Serenade didn't understand, you can choose a visual option to resolve the ambiguity. There are&lt;a href="https://serenade.ai/guides/" rel="noopener noreferrer"&gt; several tutorials available&lt;/a&gt; to help you begin learning the directives. Here's the first try:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/MP7sq7CGHuM" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611693063%2Fvideo_to_markdown%2Fimages%2Fyoutube--MP7sq7CGHuM-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="Add express"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The first attempt at using Serenade&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There's a lot to like about how Serenade works: it knows common IDE commands like "delete word". Additionally, in general, if you speak out a sentence, it'll know when to add spaces and semicolons. However, one issue to keep in mind is the fact that Serenade has three different ways to add text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://serenade.ai/docs/#inserting-code" rel="noopener noreferrer"&gt;insert&lt;/a&gt;, which is a general way to describe a line&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serenade.ai/docs/#adding-code" rel="noopener noreferrer"&gt;add&lt;/a&gt;, which specifically recognizes language features like classes and functions&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://serenade.ai/docs/#adding-raw-text" rel="noopener noreferrer"&gt;type&lt;/a&gt;, which enters raw, plain text&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing which one to use can be a bit confusing, but after spending some time with the program (and thoroughly reading the documentation), it is possible to move at a somewhat faster clip. However, phrases are still misunderstood from time to time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/vLS2tPmfW0M" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611693156%2Fvideo_to_markdown%2Fimages%2Fyoutube--vLS2tPmfW0M-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="Add app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Moving a little faster as we learn the syntax&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There seems to be some confusion about where to place parentheses and arguments. On occasion, it can feel like we are fighting against the app's syntax, rather than it understanding me. Hands 2, Voice 0.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Write the HTML and CSS&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let's assume we did manage to get a basic server going:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const express = require('express');

const app = express();

const path = require('path');

app.get('/', function(req, res) {

   res.sendFile('/index.html');

});

app.listen(8080);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll now write a file called index.html to represent the entirety of our design. HTML is meant to be a structured language; how does it fare?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/YeLhZXuWK7o" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611693225%2Fvideo_to_markdown%2Fimages%2Fyoutube--YeLhZXuWK7o-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="Add HTML"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;HTML for the win&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Success! Writing HTML is a breeze. We was worried there would be some mistakes with tag placement, especially after adding the attributes, but the alignment and nesting are well understood. Hands 2, Voice 1.&lt;/p&gt;

&lt;p&gt;Let's move on to the CSS:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/251myHyBgHU" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611693268%2Fvideo_to_markdown%2Fimages%2Fyoutube--251myHyBgHU-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="CSS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;CSS also works well&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is an example of when a really close understanding of the underlying language's grammar is really important; we never knew the bracketed CSS definitions were specifically called "rulesets."  Still, at this point, we've started to understand how Serenade works, including its nifty capability to move directly to a line. Hands 2, Voice 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Write the client-side JavaScript&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Our final HTML and CSS will end up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
    &amp;lt;link rel=stylesheet type=text/css href=/main.css&amp;gt;&amp;lt;/link&amp;gt;
    &amp;lt;script src="main.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;hello!&amp;lt;/h1&amp;gt;
    &amp;lt;p id="container"&amp;gt;&amp;lt;/p&amp;gt;
    &amp;lt;button id="emoji"&amp;gt;Emoji!&amp;lt;/button&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;h1 {
  color: blue;
  margin-top: 1em;
}
#container {
  text-align: center;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To avoid executing our JavaScript immediately, we need to wait for&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event" rel="noopener noreferrer"&gt; the DOMContentLoaded event&lt;/a&gt; to fire before setting up any event listeners. Capitalization matters here, and we couldn't get Serenade to recognize the right spelling:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/oZ2tbdSTIcc" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611693341%2Fvideo_to_markdown%2Fimages%2Fyoutube--oZ2tbdSTIcc-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="DOMContent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Capitalizations are a challenge&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Still, either through our increased proficiency with Serenade, or the relatively straightforward nature of what to add, it was easy to add the event handling code. As programmers are not infallible, we intentionally tried to make mistakes to see if Serenade could recover. For example, we missed some indentation: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/QykpCH3yhmc" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611693383%2Fvideo_to_markdown%2Fimages%2Fyoutube--QykpCH3yhmc-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="javascripting"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll call this one a draw.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Deploy to Heroku&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For the final piece of our test, we need to deploy to Heroku. Heroku requires a Procfile, which is rather short for this project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;web: node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, once again, the obstacle here is switching to the Dictation app, which simply cannot understand the commands required to push this application up to Heroku:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://youtu.be/P0ePibGolMU" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fres.cloudinary.com%2Fmarcomontalbano%2Fimage%2Fupload%2Fv1611693427%2Fvideo_to_markdown%2Fimages%2Fyoutube--P0ePibGolMU-c05b58ac6eb4c4700831b2b3070cd403.jpg" alt="git attempt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With a final clinch at the end: Hands 3, Voice 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Serenade is pretty fun to use. Navigating the IDE is solid, and explicitly speaking out the proper names for programming concepts makes one feel like a wise sage. However, it requires a specific understanding of the various commands, which doesn't feel like speaking English; it feels like we're speaking specific instructions to a computer, rather than a person we're pair programming with. Still, we'd recommend it as a system to augment your current two-handed coding style, rather than a full-on replacement. With practice and patience, it could be a useful tool for developers who otherwise can't code. And as a nice touch, the company provides&lt;a href="https://serenade.ai/schedule/" rel="noopener noreferrer"&gt; free one-on-one training&lt;/a&gt; to anyone who wants to get started quickly.&lt;/p&gt;

&lt;p&gt;Another issue that we did not note here is that, like any good programmer, we frequently relied on the internet to look up specific APIs. This means that we spent the majority of our time navigating away from the IDE (and Serenade), which tested Dictation's coping capabilities. We're not quite at the point of fully composing applications with voice (and there's a long way to go before we can deploy them), but we're getting closer.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Deploying Your First Golang Webapp</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Tue, 19 Jan 2021 14:18:05 +0000</pubDate>
      <link>https://dev.to/heroku/deploying-your-first-golang-webapp-11b3</link>
      <guid>https://dev.to/heroku/deploying-your-first-golang-webapp-11b3</guid>
      <description>&lt;p&gt;The&lt;a href="https://golang.org/" rel="noopener noreferrer"&gt; Go programming language&lt;/a&gt;, often referred to as "golang", has gained a lot of well-deserved traction in the DevOps community. Many of the most popular tools such as&lt;a href="https://www.docker.com/" rel="noopener noreferrer"&gt; Docker&lt;/a&gt;,&lt;a href="https://kubernetes.io/" rel="noopener noreferrer"&gt; Kubernetes&lt;/a&gt;, and&lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt; Terraform&lt;/a&gt; are written in Go, but it can also be a great choice for building web applications and APIs. &lt;/p&gt;

&lt;p&gt;Go provides you with all the speed and performance of a compiled language, but feels like coding in an interpreted language. This comes down to the great tooling that you get out of the box with a compiler, runner, test suite and code formatter provided by default. Add to that the robust and easily comprehensible approach to concurrency, to take maximum advantage of today's multi-core or multi-cpu execution environments, and it's clear why the Go community has grown so rapidly.&lt;/p&gt;

&lt;p&gt;Go feels like it was created with specific goals in mind, leading to a deceptively simple language design that's straightforward to learn, without compromising on capabilities.&lt;/p&gt;

&lt;p&gt;In this post, I'm going to show you how easy it is to develop a simple web application in Go, package it as a lightweight Docker image, and deploy it to&lt;a href="https://heroku.com/" rel="noopener noreferrer"&gt; Heroku&lt;/a&gt;. I'll also show a fairly new feature of Go: built-in package management.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Go Modules&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I'm going to use Go's built-in module support for this article.&lt;/p&gt;

&lt;p&gt;Go 1.0 was released in March 2012. Up until version 1.11 (released in August 2018), developing Go applications involved&lt;a href="https://golang.org/doc/gopath_code.html" rel="noopener noreferrer"&gt; managing a GOPATH&lt;/a&gt; for each "workspace", analogous to java's &lt;code&gt;JAVA_HOME&lt;/code&gt;, and all of your Go source code and any third-party libraries were stored below the &lt;code&gt;GOPATH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I always found this a bit off-putting, compared to developing code in languages like Ruby or Javascript where I could have a simpler directory structure isolating each project. In both of those languages, a single file (&lt;code&gt;Gemfile&lt;/code&gt; for Ruby, &lt;code&gt;package.json&lt;/code&gt; for Javascript) lists all the external libraries, and the package manager keeps track of managing and installing dependencies for me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'm not saying you can't manage the &lt;code&gt;GOPATH&lt;/code&gt; environment variable to isolate projects from one another. I particularly find the package manager approach is easier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Thankfully, Go now has excellent package management built in, so this is no longer a problem. However, you might find &lt;code&gt;GOPATH&lt;/code&gt; mentioned in many older blog posts and articles, which can be a little confusing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Hello, World!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's get started on our web application. As usual, this is going to be a very simple "Hello, World!" app, because I want to focus on the development and deployment process, and keep this article to a reasonable length.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pre-requisites&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You'll need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A recent version of golang (I'm using 1.14.9.)&lt;/li&gt;
&lt;li&gt;  Docker&lt;/li&gt;
&lt;li&gt;  A&lt;a href="https://heroku.com/" rel="noopener noreferrer"&gt; Heroku&lt;/a&gt; account (The free account works fine for this example.)&lt;/li&gt;
&lt;li&gt;  The&lt;a href="https://devcenter.heroku.com/articles/heroku-cli" rel="noopener noreferrer"&gt; Heroku command-line client&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://git-scm.com/" rel="noopener noreferrer"&gt;git&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Go mod init&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To create our new project, we need to create a directory for it, and use the &lt;code&gt;go mod init&lt;/code&gt; command to initialize it as a Go module.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

mkdir helloworld
cd helloworld
go mod init digitalronin/helloworld


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It's common practice to use your github username to keep your project names globally unique, and avoid name conflicts with any of your project dependencies, but you can use any name you like.&lt;/p&gt;

&lt;p&gt;You'll see a &lt;code&gt;go.mod&lt;/code&gt; file in the directory now. This is where Go will track any project dependencies. If you look at the contents of the file, they should look something like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

module digitalronin/helloworld

go 1.14


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's start committing our changes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git init git add * git commit -m "Initial commit"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;gin&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We're going to use&lt;a href="https://github.com/gin-gonic/gin" rel="noopener noreferrer"&gt; gin&lt;/a&gt; for our web application. Gin is a lightweight web framework, similar to&lt;a href="http://sinatrarb.com/" rel="noopener noreferrer"&gt; Sinatra&lt;/a&gt; for Ruby,&lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt; express.js&lt;/a&gt; for Javascript, or&lt;a href="https://pypi.org/project/Flask/" rel="noopener noreferrer"&gt; Flask&lt;/a&gt; for Python.&lt;/p&gt;

&lt;p&gt;Create a file called &lt;code&gt;hello.go&lt;/code&gt; containing this code:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()

    r.GET("/hello", func(c *gin.Context) {
        c.String(200, "Hello, World!")
    })

    r.Run(":3000")
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's break this down a little:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

r := gin.Default()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This creates a router object, &lt;code&gt;r&lt;/code&gt;, using the built-in defaults that come with gin.&lt;/p&gt;

&lt;p&gt;Then, we assign a handler function to be called for any HTTP GET requests to the path &lt;code&gt;/hello&lt;/code&gt;, and to return the string "Hello, World!" and a 200 (HTTP OK) status code:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

r.GET("/hello", func(c *gin.Context) {
        c.String(200, "Hello, World!")
    })


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally, we start our webserver and tell it to listen on port 3000:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

r.Run(":3000")


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To run this code, execute:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

go run hello.go


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see output like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

go: finding module for package github.com/gin-gonic/gin
go: found github.com/gin-gonic/gin in github.com/gin-gonic/gin v1.6.3
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /hello                    --&amp;gt; main.main.func1 (3 handlers)
[GIN-debug] Listening and serving HTTP on :3000


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now if you visit &lt;code&gt;http://localhost:3000/hello&lt;/code&gt; in your web browser, you should see the message "Hello, World!"&lt;/p&gt;

&lt;p&gt;Notice that we didn't have to install gin separately, or even edit our &lt;code&gt;go.mod&lt;/code&gt; file to declare it as a dependency. Go figures that out and makes the necessary changes for us, which is what's happening when we see these lines in the output:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

go: finding module for package github.com/gin-gonic/gin
go: found github.com/gin-gonic/gin in github.com/gin-gonic/gin v1.6.3


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you look at the &lt;code&gt;go.mod&lt;/code&gt; file, you'll see it now contains this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

module digitalronin/helloworld

go 1.14

require github.com/gin-gonic/gin v1.6.3 // indirect


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You will also see a &lt;code&gt;go.sum&lt;/code&gt; file now. This is a text file containing references to the specific versions of all the package dependencies, and &lt;em&gt;their&lt;/em&gt; dependencies, along with a cryptographic hash of the contents of that version of the relevant module.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;go.sum&lt;/code&gt; file serves a similar function to &lt;code&gt;package-lock.json&lt;/code&gt; for a Javascript project, or &lt;code&gt;Gemfile.lock&lt;/code&gt; in a Ruby project, and you should always check it into version control along with your source code.&lt;/p&gt;

&lt;p&gt;Let's do that now:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git add *
git commit -m "Add 'Hello world' web server"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Serving HTML and JSON&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I'm not going very far into what you can build with gin, but I do want to demonstrate a little more of its functionality. In particular, sending JSON responses and serving static files.&lt;/p&gt;

&lt;p&gt;Let's look at JSON responses first. Add the following code to your &lt;code&gt;hello.go&lt;/code&gt; file, right after the &lt;code&gt;r.GET&lt;/code&gt; block:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

api := r.Group("/api")

api.GET("/ping", func(c *gin.Context) {
  c.JSON(200, gin.H{
    "message": "pong",
  })
})


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here we're creating a "group" of routes behind the path &lt;code&gt;/api&lt;/code&gt; with a single path &lt;code&gt;/ping&lt;/code&gt; which will return a JSON response.&lt;/p&gt;

&lt;p&gt;With this code in place, run the server with &lt;code&gt;go run&lt;/code&gt; and then hit the new API endpoint:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

curl http://localhost:3000/api/ping


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should get the response:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{"message":"pong"}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally, let's make our webserver serve static files. Gin has an additional library for this.&lt;/p&gt;

&lt;p&gt;Change the import block at the top of the &lt;code&gt;hello.go&lt;/code&gt; file to this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import (
    "github.com/gin-gonic/contrib/static"
    "github.com/gin-gonic/gin"
)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The most popular code editors have golang support packages you can install which will take care of the &lt;code&gt;import&lt;/code&gt; declarations for you automatically, updating them for you whenever you use a new module in your code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then, add this line inside the &lt;code&gt;main&lt;/code&gt; function:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

r.Use(static.Serve("/", static.LocalFile("./views", true)))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The full code for our web application now looks like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;hello.go&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

package main

import (
    "github.com/gin-gonic/contrib/static"
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.Default()

    r.GET("/hello", func(c *gin.Context) {
        c.String(200, "Hello, World!")
    })

    api := r.Group("/api")

    api.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Use(static.Serve("/", static.LocalFile("./views", true)))

    r.Run()
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;r.Use(static.Serve...&lt;/code&gt; line enables our webserver to serve any static files from the &lt;code&gt;views&lt;/code&gt; directory, so let's add a few:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

mkdir -p views/css


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;views/css/stylesheet.css&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

body {
  font-family: Arial;
}

h1 {
  color: red;
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;views/index.html&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;link rel="stylesheet" href="/css/stylesheet.css" /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;h1&amp;gt;Hello, World!&amp;lt;/h1&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now restart the webserver using &lt;code&gt;go run hello.go&lt;/code&gt; and visit &lt;code&gt;http://localhost:3000&lt;/code&gt; and you should see the styled message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjzajlqb2wvgg8xp6hfbd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjzajlqb2wvgg8xp6hfbd.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Dockerize&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We've written our go web application, now let's package it up as a Docker image. We could create it as a Heroku buildpack, but one of the nice features of Go is that you can distribute your software as a single binary file. This is an area where Go really shines, and using a Docker-based Heroku deployment lets us take advantage of that. Also, this technique isn't limited to Go applications: You can use Docker-based deployment to Heroku for projects in any language. So, it's a good technique to understand.&lt;/p&gt;

&lt;p&gt;So far, we've been running our code with the &lt;code&gt;go run&lt;/code&gt; command. To compile it into a single, executable binary, we simply run:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

go build


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will compile all our Go source code and create a single file. By default, the output file will be named according to the module name, so in our case it will be called &lt;code&gt;helloworld&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can run this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

./helloworld


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And we can hit the same HTTP endpoints as before, either with &lt;code&gt;curl&lt;/code&gt; or our web browser.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The static files are not compiled into the binary, so if you put the &lt;code&gt;helloworld&lt;/code&gt; file in a different directory, it will not find the &lt;code&gt;views&lt;/code&gt; directory to serve our HTML and CSS content.&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's all we need to do to create a binary for whatever platform we're developing on (in my case, my Mac laptop). However, to run inside a Docker container (for eventual deployment to Heroku) we need to compile a binary for whatever architecture our Docker container will run on.&lt;/p&gt;

&lt;p&gt;I'm going to use&lt;a href="https://www.alpinelinux.org/" rel="noopener noreferrer"&gt; alpine linux&lt;/a&gt;, so let's build our binary on that OS. Create a &lt;code&gt;Dockerfile&lt;/code&gt; with the following content:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FROM golang:1.14.9-alpine
RUN mkdir /build
ADD go.mod go.sum hello.go /build/
WORKDIR /build
RUN go build


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this image, we start with the &lt;code&gt;golang&lt;/code&gt; base image, add our source code, and run &lt;code&gt;go build&lt;/code&gt; to create our &lt;code&gt;helloworld&lt;/code&gt; binary.&lt;/p&gt;

&lt;p&gt;We can build our Docker image like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker build -t helloworld .


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't forget the trailing &lt;code&gt;.&lt;/code&gt; at the end of that command. It tells Docker we want to use the current directory as the build context.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This creates a Docker image with our &lt;code&gt;helloworld&lt;/code&gt; binary in it, but it also contains all the Go tools needed to &lt;strong&gt;compile&lt;/strong&gt; our code, and we don't want any of that in our final image for deployment, because it makes the image unnecessarily large. It can also be a security risk to install unnecessary executables on your Docker images.&lt;/p&gt;

&lt;p&gt;We can see the size of our Docker image like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker images helloworld
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
helloworld          latest              9657ec1ca905        4 minutes ago       370MB


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For comparison, the &lt;code&gt;alpine&lt;/code&gt; image (a lightweight linux distribution, often used as a base for docker images) is much smaller:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker images alpine
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
alpine              latest              caf27325b298        20 months ago       5.53MB


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;On my Mac, the &lt;code&gt;helloworld&lt;/code&gt; binary is around 14MB, so the golang image is much bigger than it needs to be.&lt;/p&gt;

&lt;p&gt;What we want to do is use this Dockerfile to &lt;strong&gt;build&lt;/strong&gt; our helloworld binary to run on alpine linux, then copy the compiled binary into an alpine base image, without all the extra golang tools.&lt;/p&gt;

&lt;p&gt;We can do this using a "multistage" Docker build. Change the Dockerfile to look like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FROM golang:1.14.9-alpine AS builder
RUN mkdir /build
ADD go.mod go.sum hello.go /build/
WORKDIR /build
RUN go build

FROM alpine
RUN adduser -S -D -H -h /app appuser
USER appuser
COPY --from=builder /build/helloworld /app/
COPY views/ /app/views
WORKDIR /app
CMD ["./helloworld"]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;On the first line, we label our initial Docker image &lt;code&gt;AS builder&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Later, we switch to a different base image &lt;code&gt;FROM alpine&lt;/code&gt; and then copy the &lt;code&gt;helloworld&lt;/code&gt; binary from our builder image like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

COPY --from=builder /build/helloworld /app/


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Build the new Docker image:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker build -t helloworld .


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, it's the size you would expect for a base alpine image plus our helloworld binary:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ docker images helloworld
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
helloworld          latest              1d6d9cb64c7e        8 seconds ago       20.7MB


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can run our webserver from the Docker image like this. (If you have another version running using &lt;code&gt;go run hello.go&lt;/code&gt; or &lt;code&gt;./helloworld&lt;/code&gt;, you'll need to stop that one first, to free up port 3000.)&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

docker run --rm -p 3000:3000 helloworld


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;The dockerized webserver should behave just like the &lt;code&gt;go run hello.go&lt;/code&gt; and &lt;code&gt;./helloworld&lt;/code&gt; versions &lt;strong&gt;except&lt;/strong&gt; that it has its own copies of the static files. So, if you change any of the files in &lt;code&gt;views/&lt;/code&gt; you won't see the changes until you rebuild the Docker image and restart the container.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Deploy to Heroku&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now that we have our dockerized web application, let's deploy it to Heroku. Heroku is a PaaS provider that makes it simple to deploy and host an application. You can set up and deploy your application through the Heroku UI, or through the Heroku CLI. For this example, we'll use the Heroku command-line application.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Getting PORT from an environment variable&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We've hard-coded our webserver to run on port 3000, but that won't work on Heroku. Instead, we need to alter it to run on whichever port number is specified in the &lt;code&gt;PORT&lt;/code&gt; environment variable, which Heroku will supply automatically.&lt;/p&gt;

&lt;p&gt;To do this, alter the &lt;code&gt;r.Run&lt;/code&gt; line near the bottom of our &lt;code&gt;hello.go&lt;/code&gt; file, and remove the &lt;code&gt;":3000"&lt;/code&gt; string value so the line becomes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

r.Run()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The default behavior of gin is to run on whatever port is in the &lt;code&gt;PORT&lt;/code&gt; environment variable (or port 8080 if nothing is specified). This is exactly the behavior Heroku needs.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Setting up our Heroku app&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;First, log into Heroku:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku login


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, create an app:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku create


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Tell Heroku we want to build this project using a Dockerfile, rather than a buildpack:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku stack:set container


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To do this, we also need to create a &lt;code&gt;heroku.yml&lt;/code&gt; file like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

build:
  docker:
    web: Dockerfile

run:
  web: ./helloworld


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;heroku.yml&lt;/code&gt; file is a manifest that defines our app and allows us to specify add-ons and config vars to use during app provisioning. &lt;/p&gt;

&lt;p&gt;Next, add git and commit these files, then push to heroku to deploy:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git push heroku main


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;My git configuration uses &lt;code&gt;main&lt;/code&gt; as the default branch. If your default branch is called &lt;code&gt;master&lt;/code&gt;, then run &lt;code&gt;git push heroku master&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You should see Heroku building the image from your Dockerfile, and pushing it to the Heroku Docker registry. Once the command completes, you can view the deployed application in your browser by running:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;heroku open&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;strong&gt;Conclusion&lt;/strong&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;To recap, here's a summary of what we covered today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Creating a golang web application, using go modules and the gin web framework to serve strings, JSON, and static files&lt;/li&gt;
&lt;li&gt;  Using a multistage Dockerfile to create a lightweight Docker image&lt;/li&gt;
&lt;li&gt;  Deploying a Docker-based application to Heroku using &lt;code&gt;heroku stack:set container&lt;/code&gt; and a &lt;code&gt;heroku.yml&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've only scratched the surface in this article on how to use Go to build web applications and APIs. I hope this gives you enough to get started on your own golang web applications.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Monitoring Postgres on Heroku</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Fri, 15 Jan 2021 13:04:14 +0000</pubDate>
      <link>https://dev.to/heroku/monitoring-postgres-on-heroku-2dmn</link>
      <guid>https://dev.to/heroku/monitoring-postgres-on-heroku-2dmn</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In the vast majority of applications, the database is the source of truth. The database stores critical business records along with irreplaceable user data. So it is imperative that developers have visibility into their databases to diagnose and remedy any potential issues before they impact the business. If they don't, developers will find unexpected bills at the end of the month that they may not understand.&lt;/p&gt;

&lt;p&gt;In this article, you will learn how to set up a&lt;a href="https://www.heroku.com/postgres" rel="noopener noreferrer"&gt; Heroku Postgres&lt;/a&gt; database with&lt;a href="https://www.librato.com/" rel="noopener noreferrer"&gt; Librato&lt;/a&gt; for&lt;a href="https://devcenter.heroku.com/articles/monitoring-heroku-postgres" rel="noopener noreferrer"&gt; automated monitoring&lt;/a&gt;. Then we'll look at a range of different metrics that are available along with best practices for how to get the most out of each metric. With the right metrics in place you can anticipate where you will need to provision extra resources and where you should keep an eye on potential issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Creating Our Example Application with Heroku, Librato, and Postgres&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This article assumes you already have a Heroku account. If you don't,&lt;a href="https://signup.heroku.com/" rel="noopener noreferrer"&gt; creating a free account&lt;/a&gt; will be sufficient to follow along, although the metrics will not actually be available unless you have a&lt;a href="https://www.heroku.com/pricing" rel="noopener noreferrer"&gt; standard&lt;/a&gt; account plan.&lt;/p&gt;

&lt;p&gt;Once you have an account, log into our Heroku account and clone the&lt;a href="https://github.com/heroku/node-js-getting-started.git" rel="noopener noreferrer"&gt; NodeJS Getting Started&lt;/a&gt; template. You can use the web interface to do all this, but we will use the CLI in this article. We will then &lt;code&gt;cd&lt;/code&gt; into the repository and create a new Heroku application called &lt;code&gt;monitoring-heroku-postgres&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku login

git clone https://github.com/heroku/node-js-getting-started.git

cd node-js-getting-started

heroku create -a monitoring-heroku-postgres


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Set the get remote for the starter project. Then, push the project to the Heroku application that we created.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku git:remote -a monitoring-heroku-postgres

git push heroku main


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After deploying the application, we will start a single instance of the application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku ps:scale web=1

heroku open


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9hivlrtkwtpya9ll9fwo.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9hivlrtkwtpya9ll9fwo.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that our application is set up and deployed, we will provision our database and install the &lt;code&gt;pg&lt;/code&gt; into our project.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku addons:create heroku-postgresql:standard-0

npm install pg


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Open &lt;code&gt;index.js&lt;/code&gt; and include the following code to connect our application to the database:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

const { Pool } = require('pg');

const pool = new Pool({

  connectionString: process.env.DATABASE_URL,

  ssl: {

    rejectUnauthorized: false

  }

});


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We will create another route called &lt;code&gt;/db&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.get('/db', async (req, res) =&amp;gt; {

    try {

      const client = await pool.connect();

      const result = await client.query('SELECT * FROM test_table');

      const results = { 'results': (result) ? result.rows : null};

      res.render('pages/db', results );

      client.release();

    } catch (err) {

      console.error(err);

      res.send("Error " + err);

    }

  })


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Our application is now connected to our database. Commit the changes and push them to our deployed application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git add .

git commit -m "Install PG dependency, connect database, and create db route"

git push heroku main


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Right now our database is empty. To write data to the database, we will use&lt;a href="https://www.postgresql.org/docs/9.3/app-psql.html" rel="noopener noreferrer"&gt; &lt;code&gt;psql&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

heroku pg:psql

create table test_table (id integer, name text);

insert into test_table values (1, 'hello database');

\q

heroku open db


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbn9spldd2iws8oqpwcgh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbn9spldd2iws8oqpwcgh.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we will add Librato to our project for automated monitoring. Librato is an add-on for collecting, understanding, and acting on real-time metrics. It automatically creates interactive visualizations so you can quickly understand what is happening with your database. You can access the Librato dashboard under the Resources tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdsj2evstoj3v4uipwvmi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdsj2evstoj3v4uipwvmi.jpg"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

&lt;p&gt;heroku addons:create librato&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  &lt;strong&gt;The Metrics&lt;/strong&gt;&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;Now let's look at our new metrics and see what insights we've gained, and how we can use this new data. We'll look at several database metrics and server metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Database Metrics&lt;/strong&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;db_size&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;db_size tells you the number of bytes contained in the database. It includes all table and index data on disk, including database bloat.&lt;/p&gt;

&lt;p&gt;Your database will have a certain size allotted based on your plan. If your database grows past that allotted size then you will receive a warning email with directions on how to fix the issue. You may receive an enforcement date for when you will be allowed only a single database connection. Access will be restricted to READ, DELETE, and TRUNCATE until the database is back under the plan limit.&lt;/p&gt;

&lt;p&gt;Heroku recommends setting a warning alert when your database reaches 80% of the allotted size for your plan and a critical alert when it reaches 90% of the allotted size. As you approach maximum size you can either upgrade your plan or delete data to stay within plan limits.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F22119kmtty8330gr35v9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F22119kmtty8330gr35v9.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;active-connections&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;active-connections tells you the number of connections established on the database. Much like db_size, Heroku Postgres enforces a connection limit. There is a hard limit of 500 connections for plans tier-3 and higher. After reaching this limit, you will not be able to create any new connections. You can set up alerts for active-connections in two different ways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Sudden, large changes to the current connection count&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your baseline connection number experiences big changes it can be a sign of increased query and/or transaction run times. The alerting thresholds will depend on your application’s connection count range, assessed under normal operating conditions. +50/+100 over your normal daily maximum is a good rule of thumb.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Connection count approaches its hard maximum&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For tier-3 plans and higher, the maximum is 500 connections. As with db_size it is recommended to set alerts at 80% and 90% usage, so 400 and 450 are good numbers to start with. If you find that you are frequently approaching your connection limit, then consider&lt;a href="https://devcenter.heroku.com/articles/postgres-connection-pooling" rel="noopener noreferrer"&gt; using connection pooling&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Febks9fdd87tqzwn33e81.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Febks9fdd87tqzwn33e81.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;index-cache-hit-rate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;index-cache-hit-rate tells you the ratio of index lookups served from the shared buffer cache, rounded to five decimal points. Heroku recommends a value of 0.99 or greater. If your index hit rate is usually less than 0.99, then it is worth investigating which queries are the most expensive. You can also upgrade your database plan for more RAM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3pdxwv0nouvii4jiga95.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F3pdxwv0nouvii4jiga95.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;waiting-connections&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;waiting-connections tells you the number of connections waiting on a lock to be acquired. Many waiting connections can be a sign of mishandled database concurrency.&lt;/p&gt;

&lt;p&gt;We recommend setting up an alert for any connections waiting five consecutive minutes. The pg-extras CLI plugin can help identify queries that are preventing other operations from taking place. Once those queries have been identified they can be terminated in order to resolve lock contention. Knowing which statements are causing blocks can help identify application code to optimize for reducing locks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi780kgy0eo98mztnbszx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi780kgy0eo98mztnbszx.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Server Metrics&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;load-avg&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;load-avg tells you the average system load over a period of 1, 5, and 15 minutes, divided by the number of available CPUs. A load-avg of 1.0 means that, on average, processes requested CPU resources for 100% of the timespan.&lt;/p&gt;

&lt;p&gt;A load over 1.0 indicates that processes had to wait for CPU time in the given window. Higher values indicate more time spent by processes waiting. Values under 1.0 indicate that CPUs spent time idle during the given window. If this value is high, then this means that you will get less consistent query execution times and longer wait times.&lt;/p&gt;

&lt;p&gt;Once the value goes over 1.0, you are over-utilizing resources. Therefore, you will want to know before the load reaches values over 1.0. Again, we recommend setting alerts at 80% (8.0) and 90% (9.0).&lt;/p&gt;

&lt;p&gt;You can check current activity with the pg:ps command for cpu-intensive queries. If your load-avg values are consistently high then it may be worth upgrading to a larger plan. However, before upgrading it is useful to tune expensive queries to reduce the amount of processing work done on the database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzse1ckts8m7x10zyhao9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzse1ckts8m7x10zyhao9.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;read-iops&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;read-iops tells you how many read IO requests are made to the main database disk partition in values of IO Operations Per Second (IOPS). Each plan has a provided Provisioned IOPS (PIOPS) max. This is the maximum total reads + writes per second that the provisioned disk volume can sustain.&lt;/p&gt;

&lt;p&gt;You want your reads to come from memory (cache) rather than disk to increase the speed of reads. Exceeding provisioned IOPS can lead to long transaction times and high load-avg since processes need to wait on I/O to become available.&lt;/p&gt;

&lt;p&gt;You can set up an alert for 90% of your Provisioned IOPS to identify activities or statements that require significant I/O. Before upgrading to a larger plan you can tune expensive queries to reduce the amount of data being read directly from disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwjuzn2pjjf2g0wob62u2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwjuzn2pjjf2g0wob62u2.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;wal-percentage-used&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;wal-percentage-used tells you the space left to store temporary Postgres write-ahead logs. You are at risk of completely filling up the WAL volume if the rate of WAL generation exceeds the rate of WAL archival. This will shut down the database and potentially lead to a risk of data loss.&lt;/p&gt;

&lt;p&gt;If you reach 75% utilization, then your database connection limit will be automatically throttled by Heroku. All connections will be terminated at 95% utilization. Unlike previous metrics, we recommend setting up an alert for when this number reaches 60%.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fguxhl0cz4nn9y63hwixi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fguxhl0cz4nn9y63hwixi.jpg"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;We have reviewed a broad collection of different metrics that provide a range of different insights. The health of a database depends on numerous factors, which include the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  size&lt;/li&gt;
&lt;li&gt;  number of connections&lt;/li&gt;
&lt;li&gt;  cache utilization&lt;/li&gt;
&lt;li&gt;  server load&lt;/li&gt;
&lt;li&gt;  read frequency&lt;/li&gt;
&lt;li&gt;  WAL percentage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once a developer has visibility into these metrics, they will be better equipped to make informed decisions about provisioning and managing their data. For additional information on monitoring, I recommend you check out&lt;a href="https://devcenter.heroku.com/articles/monitoring-heroku-postgres" rel="noopener noreferrer"&gt; this article from Heroku&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>heroku</category>
      <category>postgres</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>What is DevSecOps (using Heroku Flow as an example)</title>
      <dc:creator>Michael Bogan</dc:creator>
      <pubDate>Thu, 07 Jan 2021 13:40:40 +0000</pubDate>
      <link>https://dev.to/heroku/what-is-devsecops-using-heroku-flow-as-an-example-210d</link>
      <guid>https://dev.to/heroku/what-is-devsecops-using-heroku-flow-as-an-example-210d</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;An Overview of DevSecOps and How to Automate It&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;With the proliferation of agile product development models, industry experts from all levels have come to appreciate the value of incremental releases. However, there is also an expectation that each release cycle will maintain and improve the reliability and security of the product being delivered.&lt;/p&gt;

&lt;p&gt;As a developer or engineer, your challenge is to implement security best practices without slowing down development or delaying your release dates. This article will illustrate several ways to include security practices in your development lifecycle to prevent critical issues later, and without slowing you down. &lt;/p&gt;

&lt;p&gt;I’ll use&lt;a href="https://www.heroku.com/flow" rel="noopener noreferrer"&gt; Heroku Flow&lt;/a&gt; as an example flow to show how these security practices (or DevSecOps) can be integrated into your CI/CD practice, though the practices can be used in almost any common scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is DevSecOps?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;DevSecOps is the philosophy of integrating security best practices early in the product development process. With DevSecOps, security is not treated as an isolated process or separate feature, but rather as an integral part of your development lifecycle. Automation helps you identify and fix security problems early, ideally before merging the application code to the main branch of the code repository. &lt;/p&gt;

&lt;p&gt;Some examples of DevSecOps practices include scanning repositories for security vulnerabilities, early threat modeling, security design reviews, static code analysis, and code reviews. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Enter Heroku Flow&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.heroku.com/flow" rel="noopener noreferrer"&gt;Heroku Flow&lt;/a&gt; provides a comprehensive CI/CD solution for Heroku-based applications. It seamlessly ties together several services (Heroku Pipelines, Review Apps,&lt;a href="https://www.heroku.com/continuous-integration" rel="noopener noreferrer"&gt; Heroku CI&lt;/a&gt;, and GitHub integrations) in a single view, giving engineers greater visibility to each code release— from a pull request to the production drop. &lt;/p&gt;

&lt;p&gt;For a visual &lt;a href="https://www.heroku.com/html/continuous-delivery/cd-animation.html" rel="noopener noreferrer"&gt;check out this Heroku Flow workflow from initial commit to production&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As the image visual shows, automated tests will run in&lt;a href="https://www.heroku.com/continuous-integration" rel="noopener noreferrer"&gt; Heroku CI&lt;/a&gt; when pull requests are created. Heroku CI is a cloud continuous integration tool from Heroku; it can either automatically detect the language and run default commands (e.g. &lt;em&gt;npm test&lt;/em&gt;) or can be configured via the &lt;em&gt;app.json&lt;/em&gt; file. CI results are available in both the pull request details in GitHub and the Heroku interface. &lt;/p&gt;

&lt;p&gt;For a successful CI build, create a new review of the application and deploy it to a new temporary Heroku environment using&lt;a href="https://devcenter.heroku.com/articles/github-integration-review-apps" rel="noopener noreferrer"&gt; Review Apps&lt;/a&gt;. The new environment link is available in GitHub pull request view, allowing engineers to easily check CI results or run any manual tests immediately. &lt;/p&gt;

&lt;p&gt;After merging the pull request, the new review of the application is available in pre-production environments using&lt;a href="https://devcenter.heroku.com/articles/pipelines" rel="noopener noreferrer"&gt; Heroku Pipelines&lt;/a&gt;. Then, the review can be promoted to production. &lt;/p&gt;

&lt;p&gt;Note that while some pieces of Heroku flow are included with a free account (namely pipelines and review apps) some features do incur a fee (Heroku CI).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How to Automate DevSecOps with Heroku Flow&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As a comprehensive CI/CD solution integrated with GitHub, Heroku Flow offers several ways to automate your DevSecOps practices. Let's explore three common examples below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Upgrading dependencies with security vulnerabilities safely&lt;/li&gt;
&lt;li&gt;Identifying security bugs early&lt;/li&gt;
&lt;li&gt;Preventing unauthorized components or libraries&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Safely Upgrade Dependencies with Security Vulnerabilities&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You probably already know that you should upgrade dependencies with known vulnerabilities. It can be pretty time-consuming to identify and update those dependencies. Thankfully, you can automate the majority of this work. &lt;/p&gt;

&lt;p&gt;GitHub provides a dependency vulnerability scanner, also known as&lt;a href="https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates" rel="noopener noreferrer"&gt; Dependabot&lt;/a&gt;, which can be enabled&lt;a href="https://github.com/features/security" rel="noopener noreferrer"&gt; per repository&lt;/a&gt; in GitHub’s Security settings. By default, it will add warnings to the GitHub interface when identifying a dependency with a known vulnerability.&lt;/p&gt;

&lt;p&gt;While it’s a useful feature, it still requires you to check the warnings and manually create pull requests to upgrade the affected dependencies and create a fixed version. Fortunately, there's a beta feature in Dependabot that automatically creates pull requests to fix known vulnerabilities. &lt;/p&gt;

&lt;p&gt;To enable this feature, simply add a &lt;em&gt;.github/dependabot.yml&lt;/em&gt; file to your repository:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

# Basic dependabot.yml file for JavaScript application 
# check docs for other dependencies
version: 2

updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Dependabot will create pull requests with the suggested fixes, adding the GitHub&lt;a href="https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax" rel="noopener noreferrer"&gt; code owners&lt;/a&gt; as default reviewers. The&lt;a href="https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates" rel="noopener noreferrer"&gt; Dependabot documentation&lt;/a&gt; covers all available options.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fef58ri9jmn763458drel.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fef58ri9jmn763458drel.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pull request created by Dependabot to address a known vulnerability&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;While the pull request will upgrade the affected library version, it's still important to verify that the application will work as expected after the upgrade. &lt;/p&gt;

&lt;p&gt;The pull request raised by Dependabot will run CI tests and will be deployed to a new Heroku environment. Both versions are accessible from the GitHub interface. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7nl5sjeqkzkvlv5d8k5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7nl5sjeqkzkvlv5d8k5s.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Checks from Pull Request view in GitHub&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After merging the pull request, the pipeline will run CI tests and deploy it to pre-production environments. Then, it can be promoted to production. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6r8f52x5nrly1s0pgby5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6r8f52x5nrly1s0pgby5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Heroku Pipeline view&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Setting up Dependabot and Heroku Flow will automate most of the manual work required to address security vulnerabilities in libraries and dependencies. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Identify Security Bugs Early&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Naturally, the ideal time to catch security bugs is before deploying to production. Many different tools can run static code analysis and identify problematic code before merging the code to the main branch. &lt;/p&gt;

&lt;p&gt;As an example, let’s consider a simple Node.js application. Developers commonly use&lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt; ESlint&lt;/a&gt; to enforce consistent coding styles and to catch common problems. Enabling&lt;a href="https://github.com/nodesecurity/eslint-plugin-security#readme" rel="noopener noreferrer"&gt; ESlint-plugin-security&lt;/a&gt; will also identify common security bugs:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;.eslintrc&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

...
"plugins": [
  "security"
],
"extends": [
  "plugin:security/recommended"
]
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To ensure Eslint executes during CI, the &lt;em&gt;app.json&lt;/em&gt; file is editable and points to a file in the repository: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;app.json&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
  "environments": {
      "test": {
        "scripts": {
           "test": "bash ./ci.sh"
      }
    }
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this custom script file, you can run any desired command: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;ci.sh&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

#!/bin/bash
set -eux
#### Running unit tests
npm test
#### Searching for security problems 
npm run lint


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If the lint fails, the build will be marked unsuccessful, and the deployment will not continue. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F918s2w8vccvh6r6dtiks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F918s2w8vccvh6r6dtiks.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While ESlint-plugin-security is specific for JavaScript, most mature languages have static code analysis tools, like the popular&lt;a href="https://brakemanscanner.org/" rel="noopener noreferrer"&gt; Brakeman&lt;/a&gt; for Ruby, or&lt;a href="https://find-sec-bugs.github.io/" rel="noopener noreferrer"&gt; Find-Sec-Bugs&lt;/a&gt; for Java. &lt;/p&gt;

&lt;p&gt;Although the CI snippets in this article were shown in bash scripts, the Heroku CI supports multiple languages. &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Prevent Unauthorized Components or Libraries&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Some organizations heavily emphasize controlling application deployment complexity and implementing centralized controls for all applications. For example, these controls may prevent the use of Redis add-on or a specific JavaScript library. &lt;/p&gt;

&lt;p&gt;All Heroku components for an application are defined in the&lt;a href="https://devcenter.heroku.com/articles/app-json-schema" rel="noopener noreferrer"&gt; app.json&lt;/a&gt; file as code. This opens up the possibility for pre-deployment checks. Infrastructure engineers can create a centralized script to prevent specific components from being deployed, and ensure all applications pass the same checks. &lt;/p&gt;

&lt;p&gt;For example, let's consider the centralized script &lt;em&gt;infrastructure-checks.sh&lt;/em&gt; shown below. It’s currently available in a public git repository "&lt;em&gt;mygithubaccount/infrastructure-scripts&lt;/em&gt;". For this tutorial, let's say your goal is to prevent all Heroku add-ons from deploying.  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;infrastructure-scripts.sh&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

#!/bin/bash
set -eux
ADDONS=$(cat app.json | jq '.addons')
# Prevents all addons
if [[ "$ADDONS" != "null" ]]; then
  echo "Add-ons are not allowed"
  exit 1
fi


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Inside the infrastructure script, you can add any desired number of checks to exclude specific addons, check environment variables, prevent certain instance types from being created, and even check for specific libraries that shouldn't be used. In short, you can do anything necessary to maintain the consistency of all environments. &lt;/p&gt;

&lt;p&gt;For each Heroku application, the CI can be configured to download and execute &lt;em&gt;infrastructure-scripts.sh&lt;/em&gt; from the centralized repository_: _&lt;/p&gt;

&lt;p&gt;&lt;em&gt;app.json&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

{
  "environments": {
    "test": {
      "scripts": {
        "test": "bash ./ci.sh"
      }
    }
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;ci.sh&lt;/em&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

#!/bin/bash
set -eux
INFRA_SCRIPTS_REPOSITORY="mygithubaccount/infrastructure-scripts"
wget https://raw.githubusercontent.com/${INFRA_SCRIPTS_REPOSITORY}/master/infrastructure-checks.sh
bash ./infrastructure-checks.sh
# Add as well all commands to run all other tests 
...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The central infrastructure repository can also be private, but authentication will be required when downloading the script file. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Hopefully, you’ve now seen some practical examples of implementing security controls as part of your CI/CD pipeline using Heroku Flow. You should be able to implement similar controls in other CI/CD solutions as well, but those may not have tight integration with GitHub and Heroku. &lt;/p&gt;

&lt;p&gt;More and more organizations are realizing, security shouldn’t be an afterthought, but rather part of a continuous improvement process. Implementing security controls and fixes as code in a minimally intrusive way will help you deliver code reliably and safely without slowing down delivery speed. It also ensures customers or end-users are largely protected from potential security breaches. &lt;/p&gt;

&lt;p&gt;As part of DevSecOps, automation also means catching security vulnerabilities isn’t a reactive process where scanners and audit processes find security loopholes in live systems, but rather a proactive approach. &lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
