How to Set Up a Back End Project Using TypeScript and Node.js

19 Dec, 2019
  • Share
Post image

Introduction

We'll build a back end app starter using TypeScript, compile it to JavaScript(ES2019), and deploy it to Heroku.

We will use:

  • Node.js 12
  • Express - web framework for Node.js

Source code: d-dmytro/my-typescript-app

Setting Up the Project (TypeScript, Express, Nodemon, and Concurrently)

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.

Adding HTML Templates

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!'
  });
});

Deploying to Heroku

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

Pssst...

You know React already? Wanna learn how to use TypeScript with it?

Get this detailed online course and learn TypeScript and how to use it in React applications.

Course thumb image