DEV Community

Cover image for How to Setup a TypeScript + NodeJs Server (2023)
Elson Correia
Elson Correia

Posted on • Edited on • Originally published at Medium

How to Setup a TypeScript + NodeJs Server (2023)

With new releases and tools, setting up a node server has become super simple and until NodeJs ships with typescript built-in, adding typescript is an essential need.

I will show you the SIMPLEST setup you can have to kick off your next node project confidently. For simplicity, you can customize it with the things you need to complete your project.

Project Directory Setup

Let's start by setting you your project directory

mkdir YOUR_PROJECT_NAME
cd YOUR_PROJECT_NAME

git init # start your git project

npm init -y # initialize npm with defaults

mkdir src # create the source directory for all the code
Enter fullscreen mode Exit fullscreen mode

Setting up Typescript

Let's start with a couple of installs…

npm install -D typescript @types/node
Enter fullscreen mode Exit fullscreen mode

The @types/node pretty much sets up types for the entire Node itself.
We can now create the ts-config.json file by running:

npx tsc --init
Enter fullscreen mode Exit fullscreen mode

This will add a ts-config.json file to the root of the project with all defaults including commented-out ones. We need to have the following configurations

{
  "compilerOptions": {
     "target": "es2016",
     "module": "commonjs",
     "rootDir": "./",
     "resolveJsonModule": true, /* in case you are importing JSON files */
     "outDir": "./build",
     "esModuleInterop": true,
     "forceConsistentCasingInFileNames": true,
     "strict": true,
     "noImplicitAny": true,
     "skipLibCheck": true,
  }
}
Enter fullscreen mode Exit fullscreen mode

Feel free to uncomment a few other options to fit your project. This should be good to start.

Create a simple server

To test if everything is working fine, let's add a simple server code.
Create a file src/index.ts and add the following code:

// src/index.ts

import http from "http";

export const server = http.createServer((req, res) => {
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(
    JSON.stringify({
      data: "It Works!",
    })
  );
});

server.listen(3000, () => {
  console.log("Server running on http://localhost:3000/");
});
Enter fullscreen mode Exit fullscreen mode

This is a simple HTTP Node server listening on port 3000. It should be enough to test our setup.

Now let's try to build this by adding a build script to our package.json file.

"scripts": {
  "build": "tsc"
}
Enter fullscreen mode Exit fullscreen mode

Now if you run npm run build you should see a build directory added to the project root folder with a src directory and the package.json file. The server code should look something like so:

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.server = void 0;
const http_1 = __importDefault(require("http"));
exports.server = http_1.default.createServer((req, res) => {
    res.writeHead(200, { "Content-Type": "application/json" });
    res.end(JSON.stringify({
        data: "It Works!",
    }));
});
exports.server.listen(3000, () => {
    console.log("Server running on http://localhost:3000/");
});
Enter fullscreen mode Exit fullscreen mode

This confirms our build works!

Set up how to run the server

We have to think about it in terms of the final build. Because we know the final build will be in the build directory and it will be in javascript, we can set the start script like so:

"scripts": {
  "build": "tsc",
  "start": "node src"
}
Enter fullscreen mode Exit fullscreen mode

The start will not work if we try to run it from our root directory because our source code is in typescript. For that, we need to do a couple more setups.

npm install -D nodemon rimraf npm-run-all ts-node
Enter fullscreen mode Exit fullscreen mode

With those installed let's improve our build by adding a clean script and make the build script clean before the new build.

"scripts": {
  "clean": "rimraf ./build",
  "build": "npm run clean && tsc",
  "start": "node src",
}
Enter fullscreen mode Exit fullscreen mode

With that, let's add the scripts to run the project locally:

"scripts": {
  "clean": "rimraf ./build",
  "build": "npm run clean && tsc",
  "start": "node src",
  "local": "ts-node src",
  "local:watch": "nodemon src -e ts,json --exec 'npm run local'",
}
Enter fullscreen mode Exit fullscreen mode

The local script uses ts-node to run our project instead of node command. This is pretty much a node but in typescript.
But local command only runs the server once and will not detect your changes. That's why the local:watch is needed so we can run the server and watch for changes when you are working.

It uses nodemon to run the project in src directory watching for extensions (-e) ts and json so whenever there are changes to execute ( --exec ) the npm run local script command.

When developing, simply run npm run local:watch .

server running on port 3000

Note: I am using a JSON Viewer extension in Chrome

Test Setup

I do not recommend going without a test. There have been great things happening on the testing side of things with the release of a built-in test runner in Node which will make things simpler.

However, I still use jest and the following setup is easy to swap to something else if you prefer.

Start with the following installs:

npm i -D supertest @types/supertest jest @types/jest ts-jest
Enter fullscreen mode Exit fullscreen mode

We will be using supertest to test our server and the rest is just jest , its types ( @types/jest) and a jest typescript version ( ts-jest ) to run things.

Now you can add the jest.config.js file to the project root folder with the following code:

module.exports = {
  transform: {
    '^.+\\.ts?$': 'ts-jest',
  },
  testEnvironment: 'node',
  testRegex: './src/.*\\.(test|spec)?\\.(js|ts)$',
  moduleFileExtensions: ['ts', 'js', 'json'],
  roots: ['<rootDir>/src'],
};
Enter fullscreen mode Exit fullscreen mode

The setup is super simple, feel free to add more extensions as you need but overall, that's all you need to start with jest and typescript.
With that, we can add the test script:

"scripts": {
  "clean": "rimraf ./build",
  "build": "npm run clean && tsc",
  "start": "node src",
  "local": "ts-node src",
  "local:watch": "nodemon src -e ts,json --exec 'npm run local'",
  "test": "jest"
}
Enter fullscreen mode Exit fullscreen mode

Now let's test our server by adding the index.test.ts file inside the src directory with the following code:

import supertest from "supertest";
import { server } from "./index";

describe("Server", function () {
  const request = supertest.agent(server);

  afterAll((done) => {
    server.close(done);
  });

  it("should get /", async () => {
    const res = await request.get("/");

    expect(res.status).toBe(200)
    expect(res.body).toEqual({"data": "It Works!"})
  });
});
Enter fullscreen mode Exit fullscreen mode

This is simply checking if when we make a GET request to our basic server, we get what we set it to return.

success test run

One thing we need to do now is to exclude test files ( *.test.ts) from our build by adding them to the exclude option in the ts-config.json file.

{
  "compilerOptions": {
     "target": "es2016",
     "module": "commonjs",
     "rootDir": "./",
     "resolveJsonModule": true, /* in case you are importing JSON files */
     "outDir": "./build",
     "esModuleInterop": true,
     "forceConsistentCasingInFileNames": true,
     "strict": true,
     "noImplicitAny": true,
     "skipLibCheck": true,
  },
  "include": [
     "src",
     "package.json"
  ],
  "exclude": [
     "src/**/*.test.ts"
  ]
}
Enter fullscreen mode Exit fullscreen mode

We also want to include the package.json so the npm start can work when we deploy the build directory somewhere like Netlify, AWS ElasticBeanStalk, Heroku, etc.

Use the exclude and include list to add or remove final production artifacts.

ESLint and Prettier Setup

Linting and auto-formatting our code makes it consistent and easier to read. It also catches things we often don't pay attention to by improving the quality of our code even more.

Start by installing few things:

npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint prettier eslint-config-prettier
Enter fullscreen mode Exit fullscreen mode

Now create the .eslintrc file with the following code:

{
  "extends": [
    "eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"
  ],
  "parser": "@typescript-eslint/parser",
  "plugins": [
    "@typescript-eslint"
  ],
  "root": true
}
Enter fullscreen mode Exit fullscreen mode

We can now create the lint , format and format:check scripts like so:

"scripts": {
  "clean": "rimraf ./build",
  "build": "npm-run-all lint format clean && tsc",
  "start": "node src",
  "local": "ts-node src",
  "local:watch": "nodemon src -e ts,json --exec 'npm run local'",
  "lint": "eslint src",
  "format": "npx prettier --write src",
  "format:check": "npx prettier --check src",
  "test": "jest"
}
Enter fullscreen mode Exit fullscreen mode

Note that the build script was changed to run the lint script before the cleaning and build.

You should configure your IDE or code editor to use the eslint configuration to auto-check and format things when you make code saves to get the best of this setup. Otherwise, manually run these commands on your own.

This is how it looks for JetBrains IDEs like IntelliJ and WebStorm:

WebStorm eslint setup

Final touches

We need the .gitignore file to make sure we don't commit things we don't have to. It should look like this

# IntelliJ project files
.idea
*.iml
out
gen

# VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix

# Folders
dist
build
node_modules
Enter fullscreen mode Exit fullscreen mode

Take Away

This setup is all you need to start. Depending on your project you may need additional things.

Check all the code in the following template repo. Let me know if you have any questions or need further assistance with something else.

YouTube Channel: Before Semicolon
Website: beforesemicolon.com

Top comments (0)