Introduction
In this series we will setup an express server using Typescript
, we will be using TypeOrm
as our ORM for querying a PostgresSql Database, we will also use Jest
and SuperTest
for testing. The goal of this series is not to create a full-fledged node backend but to setup an express starter project using typescript which can be used as a starting point if you want to develop a node backend using express and typescript.
Overview
This series is not recommended for beginners some familiarity and experience working with nodejs
, express
, typescript
and typeorm
is expected. In this post which is part one of our series we will start from scratch, we will cover the following : -
- Bootstrap our node project.
- Setup typescript and install express.
- Initialize expressjs.
- Setup eslint.
- Setup husky precommit hook.
- Setup git-cz commitizen.
This tutorial covers a lot, but no worries all the code is available under the base-express-setup
branch, check the repo.
Step One: Bootstrap the project
Let us start by bootstrapping our project. First create a new directory and run npm init
.
mkdir express-starter-ts
cd express-starter-ts
npm init
After you finish executing npm init
command you have the package.json
file in your project. Now let us install some dev dependencies run the following in your terminal : -
npm install --save-dev typescript ts-node nodemon
We have have installed nodemon
so that whenever we make any change in our files the server starts itself without our intervention. We have installed ts-node
so that our typescript code is compiled on the fly as we develop, and finally typescript.
Let us also create a .gitignore
file and add the following -
.ionide
.env
node_modules
build
Step Two: Setup typescript and install express
Now that we have typescript we need to set it up, run the following command in your terminal : -
npx tsc --init
The above command will generate a tsconfig.json
file you can customize it as you like. Here is my tsconfig.json
-
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"strictPropertyInitialization": false,
"outDir": "build",
"rootDir": "./"
},
"exclude": ["build", "tests"]
}
Finally install expressjs
in your terminal run the following -
npm install express
Also install the associated types for expressjs
like so -
npm install --save-dev @types/express
In your package.json
add the following under the scripts section -
"scripts": {
"build": "tsc -p tsconfig.json",
"dev": "nodemon --exec ts-node src/app.ts",
"start": "node build/src/app.js"
}
Step Three: Setup express js
Now we will create our app.ts
file and setup expressjs
. Lets create a src
folder in our project and inside the src
folder we will create two files namely app.ts
and server.ts
. What's the difference you might ask ? Don't we just create app.ts
as an entry point and boot our express server like -
const app = express(); // initialize express app
app.listen(PORT_NO, () => {}) // boot the express app
- But we will initialize our express app in
server.ts
file, we will add our middlewares, routes here. - We will start / boot our application in the
app.ts
file our entry point. - Well this separation is very important, when you want to boot the express server during integration tests. We will cover this when we setup testing later.
In your server.ts
paste the following code -
import express from "express";
export class HttpServer {
public app: express.Application;
constructor() {
this.app = express();
this.addMiddlewares();
this.addRoutes();
}
// configure middlewares for express
private addMiddlewares() {
// for parsing application/json
this.app.use(express.json());
// for parsing application/x-www-form-urlencoded
this.app.use(express.urlencoded({ extended: true }));
}
// configure routes for express
private addRoutes() {
this.app.get("/", (req, res) => {
return res.status(200).json({
status: true,
statusCode: 200,
message: "Success",
});
});
}
}
const expressServer = new HttpServer();
export default expressServer.app;
The code is very simple to understand in the constructor we created an express app and added some middlewares and routes. Just remove all the class and class methods
you will find it very similar to what we normally do in javascript. Finally we create an object const expressServer = new HttpServer()
and export default our express app which is a property of the expressServer object
.
Now under app.ts paste the following code : -
import * as http from "http";
import expressServer from "./server";
class Main {
private readonly port = 8080;
private server: http.Server;
constructor() {
this.server = http.createServer(expressServer);
this.startServer();
}
private startServer() {
this.server.listen(this.port, async () => {
console.log("Server started on port", this.port);
});
}
}
new Main();
Again very straightforward, to http.createServer
we pass our express app and then just boot our server using server.listen()
. If you are wondering about environment variables and graceful shutdowns don't worry we will cover all of them in the future.
With that lets run our app. In your terminal run the following command and visit http://localhost:8080/
you will see the success response.
npm run dev
Step Four: Set up linting
Install the necessary dependencies in your terminal run -
npm install --save-dev eslint eslint-config-prettier eslint-plugin-prettier prettier
Then setup eslint, by running the following in your terminal -
npx eslint --init
The above command will open up a questionnaire fill it like so -
✔ How would you like to use ESLint? - To check syntax, find problems, and enforce code style
✔ What type of modules does your project use? - JavaScript modules (import/export)
✔ Which framework does your project use? - None of these
✔ Does your project use TypeScript? - Yes
✔ Where does your code run? - Node
✔ How would you like to define a style for your project? Answer questions
✔ What format do you want your config file to be in? - JavaScript
✔ What style of indentation do you use? Tab
✔ What quotes do you use for strings? Single
✔ What line endings do you use? Unix
✔ Do you require semicolons? No
Eslint will also prompt you to install @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest
during the questionnaire, make sure you install the packages. With this we have finished setting up linting for our project.
Here is my final .eslintrc.js
file -
module.exports = {
env: {
node: true,
es2021: true,
},
extends: ['eslint:recommended', 'plugin:@typescript- eslint/recommended', 'prettier',],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'prettier'],
rules: {
'prettier/prettier': 'error',
},
}
In the root of your project create a new file .prettierrc
-
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true
}
Step Five: Set up husky hooks
Husky hooks
are a great way to run linting and other quality checks when we commit our code. If you are not familiar with husky hooks I recommend you to check it here. With husky we use another package called nano-staged
. In your terminal run the following command -
npx husky-init && npm install
npm install --save-dev nano-staged
Now lets add a husky hook in your terminal run :
npx husky add .husky/pre-commit "npx nano-staged"
Running the above command will create a .husky
folder in our project in the .husky
folder you fill find pre-commit
file open it and check if it has the npx nano-staged
command remove the npm test
command if it exists.
We are telling husky to run npx nano-staged
after we run git commit
, now let us add the nano-staged
command in the package.json like so : -
"nano-staged": {
"*.{js,ts}": "prettier --write"
},
In the nano-staged command we are basically running prettier on our staged files.
Also in the package.json
file under the scripts section add the following scripts : -
"lint": "eslint . --color",
"lint:fix": "eslint . --fix",
"pretty": "prettier . --write",
Now lets commit our files to see husky hooks running -
git add .
git commit -m "feat: express typescript project setup"
Step Six: Setup git-cz, commitizen (Optional)
I like to write git commits using git-cz
and commitizen
this step is optional you can skip it. Let us first install the necessary packages, run the following commands in your terminal -
npm install --save-dev git-cz commitizen
npx commitizen init cz-conventional-changelog --save-dev --save-exact
In our package.json replace the default commitizen configuration with the one below : -
"config": {
"commitizen": {
"path": "git-cz"
}
}
Add the following under the scripts section in package.json
: -
"commit": "git-cz"
Now under the root of our project add a file called changelog.config.js
and paste the following :
module.exports = {
disableEmoji: false,
list: [
'test',
'feat',
'fix',
'chore',
'docs',
'refactor',
'style',
'ci',
'perf',
],
maxMessageLength: 64,
minMessageLength: 3,
questions: [
'type',
'scope',
'subject',
'body',
'breaking',
'issues',
'lerna',
],
scopes: [],
types: {
chore: {
description: 'Build process or auxiliary tool changes',
emoji: '🤖',
value: 'chore',
},
ci: {
description: 'CI related changes',
emoji: '🎡',
value: 'ci',
},
docs: {
description: 'Documentation only changes',
emoji: '✏️',
value: 'docs',
},
feat: {
description: 'A new feature',
emoji: '🎸',
value: 'feat',
},
fix: {
description: 'A bug fix',
emoji: '🐛',
value: 'fix',
},
perf: {
description: 'A code change that improves performance',
emoji: '⚡️',
value: 'perf',
},
refactor: {
description: 'A code change that neither fixes a bug or adds a feature',
emoji: '💡',
value: 'refactor',
},
release: {
description: 'Create a release commit',
emoji: '🏹',
value: 'release',
},
style: {
description: 'Markup, white-space, formatting, missing semi-colons...',
emoji: '💄',
value: 'style',
},
test: {
description: 'Adding missing tests',
emoji: '💍',
value: 'test',
},
},
};
Now when you want to commit your files you would do : -
git add .
npm run commit
Summary
There you go our basic project setup is completed. All the code for this tutorial can be found under the base-express-setup
branch here. In the next tutorial we will create our routes and controllers until next time PEACE.
Top comments (0)