We'll build a back end app starter using TypeScript, compile it to JavaScript(ES2019), and deploy it to Heroku.
We will use:
Source code: d-dmytro/my-typescript-app
Let's create a folder for our project and enter it:
mkdir my-typescript-app cd my-typescript-app
Let's run "npm init" to create the package.json file in this folder. I've chosen defaults for the options.
npm init
Let's install TypeScript and the types for Node.js as dev dependencies.
npm i -D typescript @types/node@12
I installed the types for Node.js 12, because I'm using it in this project. If you are using a different version, replace "12" with your version number (e.g., @types/node@13).
We will store the source files (TS files) in the "src" folder. So, let's create it.
mkdir src
Now, we should configure the TypeScript compiler, so it compiles our code according to our needs.
Create the tsconfig.json file in the root of our project and copy and paste the following code:
{ "compilerOptions": { "target": "es2019", "module": "commonjs", "moduleResolution": "node", "outDir": "dist", "strict": true, "esModuleInterop": true }, "include": ["src/**/*"] }
We use the "target" option to set the version of ECMAScript we would like our TypeScript code to be compiled into. I've chosen "es2019", because Node.js 12 supports it. You can check what each version of Node.js supports on the https://node.green website.
We use the "module" option to tell the TypeScript compiler which module system it should use in the compiled files. Node.js uses the CommonJS module system, so I've set the "module" option to "commonjs".
We use "moduleResolution" option to tell the compiler how to resolve modules. I've set this option to "node" because we are using Node.js. So, when the compiler will see an import statement, it will resolve the import as Node would do it.
We use the "outDir" option to tell the compiler where to store the output. I'd like to store it in the "dist" folder relative to our project's root.
We set the "strict" option to "true" in order to enable strict type checking. This option makes it a little harder to code, but the code we write becomes safer. You quickly get used to this option and it definitely improves the quality of the codebase.
The "esModuleInterop" option enables the interoperability between the TypeScript's modules (ES modules) and CommonJS modules. Before, we had to use a workaround to import a CommonJS module into a single variable using the "import * as express from 'express'" syntax. With this option enabled, we can import the CommonJS modules, like we do it in our ES projects: "import express from 'express'".
Finally, we use the "include" option to list the files we'd like to compile. This option allows us to use a glob-like pattern to specify the files. The value "src/**/*" means any subfolder and any TypeScript file in the "src" folder. You can read more about this option and other ways to include/exclude files here: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#examples
Let's create the "index.ts" file in the "src" folder and copy and paste this code:
import express from 'express'; const port = 3000; const app = express(); app.get('/', (_req, res) => { res.end('Hello World!'); }); app.listen(port, (err: Error) => { if (err) throw err; console.log(`Ready on port ${port}`); });
This code starts the express app on port 3000. You'll be able to visit it at http://localhost:3000
Let's install express:
npm i -S express
Express doesn't include the type definitions, so we will install them from the third party types repository, called DefinitelyTyped:
npm i -D @types/express
Now, let's run the TypeScript compiler to compile our app. For this, add the "build" script to the "package.json".
"scripts": { "build": "tsc" }
Let's run the script:
npm run build
npm will run the compiler binary (tsc). The compiler reads our "tsconfig.json" and compiles our app.
Let's add the "start" script to "package.json" to run the compiled app.
"scripts": { "start": "NODE_ENV=production node dist/index.js" }
Now we can start the app:
npm start
and visit it in the browser at http://localhost:3000
With the current setup, every time we edit the code, we have to compile it and restart the app manually.
This is not what we expect in terms of developer experience. So, let's automate the compile and restart tasks during development.
We'll run the TypeScript compiler in the "watch" mode. It will watch our source files for changes and recompile them when we save them.
We'll run the compiled "dist/index.js" using Nodemon instead of using Node directly. Nodemon will watch our files for changes as well. When we'll save a file, it'll restart our script.
To run the compiler in the "watch" mode, we use the following command:
npx tsc -w
Now, let's install Nodemon:
npm i -D nodemon
To run our app using nodemon:
npx nodemon dist/index.js
By default, Nodemon watches all the files in the current folder. We don't want it to watch all files, because the "src" folder is watched by the compiler and if we'll save a file in the "src" folder, Nodemon will restart the app, the compiler will compile the files and touch the "dist" folder, and Nodemon will restart the app again. So, we'll have one unnecessary restart.
In order to fix this we should use the Nodemon's "-w" option to tell it which files to watch. Let's use this option to make Nodemon watch only the "dist" folder.
npx nodemon -w dist dist/index.js
So, to reach the dev experience we want, we should run the compiler and nodemon together. In order to do this, we can use the NPM package called "concurrently".
Let's install it:
npm i -D concurrently
Concurrently takes multiple commands, runs them together and manages their processes.
Let's go ahead and run tsc and nodemon together:
npx concurrently -k -n COMPILER,NODEMON -c gray,blue "tsc -w" "nodemon -w dist dist/index.js"
The "-k" option tells concurrently to kill all the processes it started if one of them dies.
The "-n" option labels the output of the processes we start.
The "-c" option sets the color of the output of each process.
Open the "src/index.ts", change the "Hello World!" string to something else, and reload the app's tab in the browser. You should see the new string immediately.
Finally, let's add our command to the "dev" script in the "package.json":
"scripts": { "dev": "concurrently -k -n COMPILER,NODEMON -c gray,blue \"tsc -w\" \"nodemon -w dist dist/index.js\"" }
That's it, you've got a simple web app back end project which you can use to develop your web app's API.
We'll use the ejs template engine for our views.
First, let's install ejs:
npm i -S ejs
We'll store the HTML templates in the "views" folder (this is the Express app's default):
mkdir views
Let's create the index view: "views/index.ejs"
<h1><%= greeting %></h1>
Finally, let's render it on the index route ("src/index.ts"):
// Tell Express to use ejs to render views. app.set('view engine', 'ejs'); app.get('/', (\_req, res) => { res.render('index', { greeting: 'Hello World!' }); });
First, you should have a Heroku account and Heroku CLI. Please have a look how to install the Heroku CLI on their site: https://devcenter.heroku.com/articles/heroku-cli
To deploy the app to Heroku, we should add our code to a git repo.
In the app's folder, let's run:
git init
Let's add the ".gitignore" file to avoid committing things we don't need in the repo:
dist node_modules npm-debug.log .DS_Store
Add the Heroku's "engines" setting to the "package.json":
"engines": { "node": "12.x" }
Heroku tells our app on which port it should listen using the "PORT" env variable. Let's open the "src/index.ts" and change the line where we define the port number to:
const port = parseInt(process.env.PORT, 10) || 3000;
Let's commit our code:
git add . git commit -m "Starter code"
Login to Heroku:
heroku login
Create the app with Heroku:
heroku create
This will create the app in your Heroku account and add the git remote called "heroku" to our repo. When we'll push something to this remote, Heroku will deploy the app. So, let's deploy our app:
git push heroku master
Run heroku open
to open your app in the browser.
Please find more info about deploying a Node.js app to Heroku here: https://devcenter.heroku.com/articles/deploying-nodejs
Get this detailed online course and learn TypeScript and how to use it in React applications.