heroku nodejs-getting-started, a sample app that is mostly a deployment tutorial
Getting Started with Node.js on Heroku
At a glance
- What is it?
- The repository behind the Heroku getting started guide is a barebones Express application with an EJS view, a Procfile and an app.json, kept in step with the current Node and npm versions. What is worth studying in it is the platform contract, the engine constraint, the two deployment paths for the Cedar and Fir generations, and the cost warning that comes with a database add-on.
- Who is it for?
- This repository fits someone who wants the smallest thing that actually deploys to Heroku, and it is the reference the platform's own documentation points at, so it stays current in a way a personal tutorial does not. It does not fit a team looking for a production starter, because there is no database, no authentication, no logging and no configuration handling in it beyond a .env file, and the tutorial even warns you to delete the database add-on when you are finished.
- Can I use it commercially?
- Yes. Unlicense is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly EJS, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Documentation that happens to run
The repository is described in one line as a barebones Node.js app using Express, and the interesting sentence is the second one. This application supports the tutorials for both the Cedar and Fir generations of the Heroku platform, with links to the two Dev Center articles it backs.
That framing explains the whole shape of the project. It is not a framework, not a starter kit and not a product. It is the runnable artefact behind a piece of platform documentation, and it exists so that a reader who types the commands from the tutorial gets exactly the result the article describes.
That is a service to users, because the tutorial text and the code cannot drift apart, and it is a constraint on anyone who wants to build on it. A sample app is optimised for being read once and copied, not for being extended.
The commit history tells the same story. The repository has exactly one release, a tag named 1 with the description that it is for use as a Terraform example, dated in June 2019. The application code has moved on since then, with current dependency versions, but the release history was never used for shipping, which is normal for a repository whose purpose is to be a moving reference rather than a versioned artefact.
The engines field is the real configuration
The package.json is short, and the most consequential part of it is the engines block:
"engines": {
"node": "22.x || 24.x || 26.x",
"npm": "12.x"
}The accepted Node versions are 22, 24 and 26, and npm must be 12.x. This is the field the platform reads to decide which runtime to put on the dyno, so it is both a declaration of what the application supports and a constraint on the machine that builds it.
For a local developer, the field means your local Node version has to match one of the three, which is the mechanism that stops the classic deployment bug where the code works on a laptop and fails on the server because the runtime differs. For the platform, it is a request, and one the operator may or may not honour exactly.
The rest of the manifest describes a small surface. The main entry point is index.js, and the start script is `node index.js`, which is what the Procfile invokes. The test script is `jest`, with jest as the only development dependency. The runtime dependencies are two: express at version 5.3 and ejs at 7.0, which is the template engine and explains why the repository's primary language is listed as EJS rather than JavaScript, since the views are the largest body of template code in a project this size.
The package is marked private, so it is not published to a registry. It is a repository you clone, not a dependency you add.
The two files that make it deployable
The root listing is short and every entry is doing a job. There is index.js as the entry point, a views/ directory for the EJS templates, a public/ directory for static assets, a test.js file for the test, a Procfile, an app.json, a .env file, a package.json with a package-lock.json, and the usual files, a LICENSE, a SECURITY.md and a CODE_OF_CONDUCT.md.
The Procfile is the traditional Heroku mechanism, and it is where the start command is stated, which is why the start script in package.json and the Procfile have to agree. The app.json is the file Heroku reads when someone deploys from a button, describing the application and the add-ons it needs, which is also why the tutorial talks about a database that you have to delete afterwards.
The .env file at the root is a small but useful detail. It is there so that a developer can run the application locally with the same environment variables the platform will provide, without committing secrets. The tutorial pairs it with the warning about resources.
The security file and the code of conduct are worth noticing for a repository of this age, and they match a pattern across the platform's sample applications: the projects that people are told to copy and deploy also carry the documentation for reporting a security problem in them.
Cedar and Fir, and the space you need before the app
The deployment instructions differ between the two platform generations, and the difference is not cosmetic.
On Cedar, the sequence is three commands:
$ heroku create
$ git push heroku main
$ heroku openCreate the application, push the main branch to the Heroku remote, and open it in a browser. The default branch matters here, which is why the push names main explicitly.
On Fir, there is an extra step before the app exists. Creating an app on Fir requires a private space first, so the sequence is:
$ heroku spaces:create <space-name> --team <team-name> --generation fir
$ heroku create --space <space-name>
$ git push heroku main
$ heroku openThe space is created with a name, a team and the generation flag, and the app is then created inside that space. The team argument is worth noticing, because Fir spaces belong to a team, so the person running the commands needs to be part of one.
The dyno defaults differ as well. On Cedar, apps use Eco dynos by default if you are subscribed to Eco, and otherwise they default to Basic dynos, with the Eco plan shared across all Eco dynos in the account and recommended if you plan to deploy many small apps. On Fir, apps use 1X-Classic dynos by default. Neither choice is written in the code, and both are properties of the account, which is a reminder that the platform configuration is part of what you are deploying.
The cost of an experiment you forget to clean up
The README contains a warning that many sample applications do not have, and it is about money. Using resources for this example app counts towards your usage, and you should delete your app and your database as soon as you are done experimenting to control costs.
That sentence is a small lesson about managed platforms. The application itself is free in the sense that the code is free, but the resources it consumes are not, and a dyno that is left running keeps billing while the database add-on keeps its data. The tutorial involves adding a database, and a database that nobody deletes is the kind of thing that produces an unexpected invoice a month later.
There is also a note about eligible students, who can apply for platform credits through the Heroku for GitHub Students program. The framing is a bit dated in tone, but the mechanism, platform credits for eligible students, is worth knowing about before you deploy.
For a reader deciding whether to use this repository as a starting point, the resource lifecycle is the part to copy. A sample application that adds a database, a dyno and a git remote should come with a teardown instruction, and this one does.
Where a sample application is the wrong tool
The first limit is obvious but worth stating plainly. There is no database access in the code, no authentication, no session handling, no rate limiting, no structured logging, no metrics and no error reporting. The application renders a template and returns some text. A team that copies it as a starting point has to add all of that before it handles a single real request, and the code it starts from will not nudge it in any direction on those questions.
The second is that the repository is a moving reference rather than a versioned dependency. The single release is from 2019 and the dependencies are current, which is fine for the purpose and wrong if you want reproducibility, since there is no tag to pin that gives you the state the tutorial was written against. Pin a commit instead.
The third is the licence, which is genuinely unclear. The package.json declares the licence as MIT, the repository is tagged with the Unlicense, and there is a LICENSE file at the root whose content is not described anywhere in the readme. Those are three different statements about the same thing, and the Unlicense in particular is a permissive public domain dedication with a warranty disclaimer, which is not the same as MIT. A company that copies this code should read the file and settle the question rather than assume.
The fourth is the platform coupling. The whole point of the repository is to demonstrate deployment on Heroku, which means the Procfile, the app.json, the engines field and the dyno model are all doing work, and a reader who wants to deploy somewhere else gets the sample application and none of the platform assumptions. The application code is portable, the deployment story is not.
Editorial conclusion
This repository fits someone who wants the smallest thing that actually deploys to Heroku, and it is the reference the platform's own documentation points at, so it stays current in a way a personal tutorial does not. It does not fit a team looking for a production starter, because there is no database, no authentication, no logging and no configuration handling in it beyond a .env file, and the tutorial even warns you to delete the database add-on when you are finished. Before copying it, read the engines field, since it pins Node 22, 24 or 26 with npm 12, and settle the licence question, because the package.json says MIT while the repository is tagged with the Unlicense and a separate LICENSE file sits in the root. The release history is a single tag from 2019, so pin a commit rather than a release.
Frequently asked questions
How do I run the Heroku Node.js sample app locally?
You need Node.js and the Heroku CLI installed. Then clone the repository, change into the directory, run npm install and npm start, and the application is served on localhost:5006.
How do I deploy this app to Heroku Cedar?
With three commands: `heroku create`, then `git push heroku main`, then `heroku open`. By default apps use Eco dynos if you are subscribed to Eco, and otherwise Basic dynos.
What is needed to deploy to Heroku Fir?
A private space has to exist first, created with `heroku spaces:create <space-name> --team <team-name> --generation fir`. Then create the app with `heroku create --space <space-name>`, push the main branch and open it. Fir apps default to 1X-Classic dynos.
Which Node and npm versions does the sample app support?
The engines field in package.json allows Node 22.x, 24.x or 26.x with npm 12.x. The start script is `node index.js` and the test script is `jest`, with Express 5.3 and EJS 7.0 as the runtime dependencies.
What should I do after trying the Heroku sample app?
Delete the app and the database add-on as soon as you are done, because the resources it uses count towards your usage and the tutorial adds a database that keeps billing until it is removed.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/heroku-nodejs-getting-started)