Skip to main content

Deploy a Deno App to Heroku in 10 Minutes

Gregory Schier Headshot Gregory Schier • 7 min read

TL;DR Use chibat/heroku-buildpack-deno to add Deno support to Heroku.

Deno is a secure runtime for JavaScript and TypeScript, created by Ryan Dahl to addresses some of the regrets he had after creating Node.js. This post demonstrates how to create and deploy a Deno web app to Heroku.

Deno to Heroku

Here's what we'll be covering:

  1. Introduce Deno
  2. Create a basic web app
  3. Deploy to Heroku
  4. Make changes and re-deploy

Okay, let's get into it! 🤗

Full code available on GitHub codesaladdev/deno-server-heroku

What is Deno?#

Deno is comparable to Node.js in that it's a runtime for executing JavaScript. Since it was created just two years ago, it learned from Node's shortcomings to make something that feels much more modern. It also borrowed from languages like Go – adopting a focus on security, favoring type safety, providing simpler dependency management, and bundling a robust standard libary.

The only bad part about Deno is that it's new. It hasn't had decades to grow a large community and ecosystem of 3rd-party modules, but it's quickly catching up. At this time, I wouldn't recommend using Deno in production but I'm confident it'll be there in a year or two.

But enough about that. Let's jump in and build a Deno app.

Create a Web App#

To make a Deno web app, we'll create a new TypeScript file called app.ts and use the http/server module to run a web server. It only takes a few lines of code to do this.

NOTE: Since we aren't actually using any TypeScript syntax in this file, we could also name it app.js.

app.ts
import { serve } from 'https://deno.land/std@0.74.0/http/app.ts';

// Use PORT from environment or fallback to 8000
const port = parseInt(Deno.env.get('PORT') ?? '8000');
const s = serve({ port });

console.log(`Listening on :${port}`);

// Respond to each request with a "Hello World!" HTML page
for await (const req of s) {
    req.respond({ body: 'Hello World!' });
}

The code above starts a web server and responds with Hello World! for every request. Now, we can use the deno run command to start it.

Shell Snippet
foo@bar:~$ deno run --allow-net --allow-env app.ts
Listening on :8000

Once started, navigate to http://localhost:8000 in your web browser to see the result. It should looks something like this.

For development, we can also make use of the --watch flag, which will automatically restart the server whenever a file is changed. Note, the watch flag currently requires the --unstable flag along with it.

Shell Snippet
foo@bar:~$ deno run --watch --unstable --allow-net --allow-env app.ts
Listening on :8000

You'll also notice some logic in our code to read the PORT environment variable. This is how Heroku (and many other hosting services) tell an app which port to run on. We can test that the logic works by setting the PORT variable from the command line.

Shell Snippet
foo@bar:~$ PORT=8001 run --allow-net --allow-env app.ts
Listening on :8001

A Note on Permissions#

Notice that we specify the --allow-net and --allow-env permission flags. This is because Deno has a "secure by default" Permissions Model, requiring flags to enable access to things like the network, filesystem, and environment. We'll refine these permissions even further for running on Heroku.

Procfile#

Heroku requires a file named Procfile to know how to start the web process. For this, we'll add a line labeled web: and a similar command to the one we just ran.

Procfile
web: deno run --allow-net=":$PORT" --allow-env app.ts

Notice how we specified an address to the --allow-net permission flag? It's always good to use the most restrictive permissions possible, so the :$PORT above makes it so our app can only run on the PORT variable that Heroku specifies.

Deploy to Heroku#

Now that we have a running app and Procfile, we can deploy it to the public internet using the Heroku CLI. First, make sure to log into the CLI if you aren't already.

Shell Snippet
foo@bar:~$ heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to xxxxx
Logging in... done
Logged in as deno.dino@example.com

Create Heroku App#

Next, we can create a new Heroku app that includes the chibat/heroku-buildpack-deno buildpack. Heroku doesn't yet support Deno apps natively, but we can get all the functionality needed to build and run a Deno app via this 3rd-party buildpack.

Shell Snippet
foo@bar:~$ heroku apps:create --buildpack https://github.com/chibat/heroku-buildpack-deno.git
Creating app... done, ⬢ sleepy-dino-13823
Setting buildpack to https://github.com/chibat/heroku-buildpack-deno.git... done
https://sleepy-dino-13823.herokuapp.com/ | https://git.heroku.com/sleepy-dino-13823.git

Initialize Git Repo#

The primary way to deploy code to Heroku is via Git. Any code that is Git-pushed to Heroku will be deployed. So, let's initialize a Git repository for our project and commit our code.

Shell Snippet
foo@bar:~$ git init
Initialized empty Git repository in ~/Workspace/deno-demo
foo@bar:~$ git add .
foo@bar:~$ git commit -m "Initial commit"
[master (root-commit) 5958f49] Initial commit
 2 files changed, 18 insertions(+)
 create mode 100644 Procfile
 create mode 100644 app.ts

Push to Heroku#

Now that we have our code committed, we can push that code by adding a new Git remote named heroku. We can either use the Heroku CLI to do this, or run the Git command ourselves with git remote add heroku <URL>.

Shell Snippet
foo@bar:~$ heroku git:remote --app sleepy-dino-13823
set git remote heroku to https://git.heroku.com/sleepy-dino-13823.git

Now we can push our code.

Shell Snippet
foo@bar:~$ heroku git:remote --app sleepy-dino-13823.git
set git remote heroku to https://git.heroku.com/sleepy-dino-13823.git
foo@bar:~$ git push heroku master
remote: Verifying deploy... done.
To https://git.heroku.com/sleepy-dino-13823.git
 * [new branch]      master -> master

If everything worked out okay, we can open the app URL in a web browser. You can either run heroku apps:info to find it, or use heroku open to do it automatically. 🤯

Re-deploy a Change#

As we just saw, Heroku deploys whatever is pushed to it via Git. So, to deploy something new, we can modify the files, commit them to Git, and push again.

Let's make the page more visually appealing by adding some HTML.

JavaScript Snippet
// ...

for await (const req of s) {
    req.respond({
        body: '<h1>Hello World!</h1>' +
            '<p>&star; This is a Deno server &star;</p>' +
            '<img src="https://deno.land/logo.svg" width="150" height="150">',
        headers: new Headers({ 'Content-Type': 'text/html' }),
    });
}

app.ts (full code)
Expand all 18 Lines
import { parse } from 'https://deno.land/std@0.74.0/flags/app.ts';
import { serve } from 'https://deno.land/std@0.74.0/http/app.ts';

const flags = parse(Deno.args);
const port = flags.port ?? 8000;
const s = serve({ port });

console.log(`Listening on :${port}`);

// Respond to each request with an HTML page
for await (const req of s) {
    req.respond({
        body: '<h1>Hello World!</h1>' +
            '<p>&star; This is a Deno server &star;</p>' +
            '<img src="https://deno.land/logo.svg" width="150" height="150">',
        headers: new Headers({ 'Content-Type': 'text/html' }),
    });
}

Now we can commit our changes and push.

Shell Snippet
foo@bar:~$ git add app.ts
foo@bar:~$ git commit -m "Convert to HTML"
foo@bar:~$ git push heroku master
remote: Verifying deploy... done.
To https://git.heroku.com/sleepy-beach-12919.git
   8d8b6b3..65452aa  master -> master

And verify that it works with heroku open.

Screenshot of web browser

Wrap-Up#

Heroku is notorious for having a clean and simple developer experience. Even though Heroku doesn't yet support Deno out of the box, the only thing required to get up and running is to apply a special buildpack. Besides that, running Deno apps on Heroku is exactly the same as any other supported language.

So, while Deno isn't quite ready for production, I would recommend playing around with it for small projects and Heroku is a great way to do that.

Bonus#

A few other platforms also support Heroku buildpacks, including DigitalOcean's new App Platform. This code shouldn't require any modifications to run there too. 🚀


Awesome, thanks for the feedback! 🤗

How did you like the article?