Ease of development is great when you are building a server written in JavaScript using Node.js and Express. What happens when this application server scales or you are working in a team of developers all across the world? TypeScript can help.
In this post, I am not going to talk about whether you should use TypeScript or not. This post describes a beginner-friendly way to set up TypeScript in an Express.js app and understand the basic constraints that come with it.
Prerequisites
To take full advantage of this tutorial, please make sure you have the following installed in your local development environment:
- Node.js version >=
12.x.x
installed - Access to one package manager such as
npm
oryarn
- Basic knowledge of Node.js and Express
Create a minimal server with Express
Start by creating a new directory where you keep your side projects in your local development environment. Inside that directory, use npm’s initializer command to create a package.json
file:
media server
cd server/
npm init --yes
The --yes
flag uses the default settings when initializing a package.json
from npm
config you might have set up.
The package.json
file created might look something like this:
{
"name": "express-ts-example",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "Aman Mittal (https://amanhimself.dev)",
"license": "MIT"
}
After the initializing step, let us add an express
package. From the terminal window, run the command:
yarn add express@4.17.1
Next, create a new file called index.js
at the root of the project with the following code to trigger a minimal server:
const express = require('express');
const app = express();
const PORT = 8000;
app.get('/', (req,res) => res.send('Express + TypeScript Server'));
app.listen(PORT, () => {
console.log(`
[server]: Server is running at https://localhost:${PORT}`);
});
Go back to the terminal and trigger the common node index.js
to start the server.
The Express server is now up and running.
Add TypeScript
Let’s add two libraries to the development server as devDependencies
.
typescript
is a core library that helps to compile the TypeScript code to valid JavaScriptts-node
is a utility library that helps to run a development server written using TypeScript directly from the terminal
To install them, from a terminal window run the following command:
yarn add -D typescript ts-node
The -D
flag is also known as --dev
flag and is a specification for the package manager to install these libraries as devDependencies
.
Once these libraries are installed, go to the package.json
file and see a new devDependencies
object:
"devDependencies": {
"ts-node": "8.10.2",
"typescript": "3.9.5"
}
Next, create a tsconfig.json
file at the root of the development server project. This file allows you to customize TypeScript configuration and add other configurations to compile the TypeScript project:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"rootDir": "./",
"outDir": "./build",
"esModuleInterop": true,
"strict": true
}
}
The compilerOptions
is a mandatory field that needs to be specified. The options used in the config above are:
target
allows us to specify the target JavaScript version that compiler will outputmodule
allows us to use a module manager in the compiled JavaScript code. Thecommonjs
is supported and is a standard in Node.jsrootDir
is an option that specifies where the TypeScript files are located inside the Node.js projectoutDir
specifies where the output of the compiled is going to be locatedesModuleInterop
allows us to compile ES6 modules tocommonjs
modulesstrict
is an option that enables strict type-checking options
There might be other configuration options that you can add on for the TypeScript compiler but these are the basic configuration options specified that can help you to get started.
Install declaration files for Node.js and Express
Declaration files are predefined modules that describe the shape of JavaScript values (the types present) for the TypeScript compiler. Type declarations are usually contained in files with a .d.ts
extension. These declaration files are available for all libraries that are originally written in JavaScript and not TypeScript.
There is a GitHub repository that maintains the TypeScript type definitions to use directly in Node.js and other JavaScript projects without bothering to define these types from scratch. This repository is called DefinitelyTyped.
To add these types or the declaration files related to a particular library or a module, you have to look for the packages that start with @types
namespace.
For example, the type definitions for Express library is kept under a specific package called @types/express
. For using a utility library such as bodyParser
(which is a middleware to parse an incoming request’s body), there is a specific type of definition module called @types/body-parser.
To install type definitions for Node.js and Express, run the below command. Do note that, these type definitions are installed as devDependencie
:
yarn add -D @types/node @types/express
Notice the versions of these packages installed in package.json
file:
"devDependencies": {
"@types/express": "4.17.6",
"@types/node": "14.0.13",
"ts-node": "8.10.2",
"typescript": "3.9.5"
}
Create an Express server with .ts extension
Now you can easily convert the minimal server code in index.js
to index.ts
file. That is the first step. Rename the file to index.ts
.
The .ts
extension is a file extension to determine the TypeScript files that are compiled to JavaScript files later when building the server.
Open index.ts
file. You can now use the import
statements from ES6. The only required package right now in the index.ts
file is express.
Replace it with the following statement:
import express from 'express';
// rest of the code remains same
const app = express();
const PORT = 8000;
app.get('/', (req, res) => res.send('Express + TypeScript Server'));
app.listen(PORT, () => {
console.log(`
[server]: Server is running at https://localhost:${PORT}`);
});
The TypeScript compiler will handle the conversion of import statements to require statements.
Watching file changes with nodemon
Another development related utility library I like to use when working on Node.js projects is nodemon
. Let’s install this using the command below:
yarn add -D nodemon
nodemon
is a tool that helps develop Node.js based applications by automatically restarting the Node application when file changes in the directory are detected. To use it, you may add a start
script in the package.json
file as specified below:
"scripts": {
"start": "nodemon index.ts",
},
Now, go back to the terminal window, and run yarn start
or npm start
.
Since there are no errors, the server is successfully running. You can go to the browser window to see the result.
Since nodemon detected changes, let’s try to edit the message sent from res.send()
and simultaneously take a look at the terminal to see if nodemon
detects any file changes or not.
The ts-node
utility checks for any file changes in the current TypeScript project. If there is a TypeScript error, this module will let the nodemon
crash the server and instead display that error.
Compile a TypeScript project
To compile a TypeScript project to a valid JavaScript one, start by declaring a new script called build
inside the package.json
file:
"scripts": {
"build": "tsc --project ./",
},
TypeScript provides a command to compile the code called tsc
. This command demands a flag to specify as to what to compile. The --project
(shorthand: -p
) is used to specify the project directory that the compiler can pick the code files from to compile to valid JavaScript. The ./
specifies the root project.
From the terminal window, run the build command to compile the code:
yarn run build
There is a new build
directory created after this command executes successfully. Inside this directory, there is the TypeScript code compiled to valid JavaScript:
'use strict';
var __importDefault =
(this && this.__importDefault) ||
function(mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, '__esModule', { value: true });
const express_1 = __importDefault(require('express'));
const app = express_1.default();
const PORT = 8000;
app.get('/', (req, res) =>
res.send('Express + TypeScript Server is awesome!!!')
);
app.listen(PORT, () => {
console.log(`
[server]: Server is running at https://localhost:${PORT}`);
});
If you specify any other directory named as the value of the property outDir
in the tsconfig.json
file that name of the directory would have reflected here instead of build.
Conclusion
Using TypeScript has its benefits but it does come with a bit of a learning curve. You have to carefully analyze whether using TypeScript in your Node.js and Express.js backend projects is beneficial or not. This may depend on the requirements you have. Make sure to check out the different Typed Definitions or go to the official TypeScript documentation to explore more.
Comments
Post a Comment