DEV Community

Cover image for Setting Up a Node.js, TypeScript, and Express Project
Harshal Ranjhani for CodeParrot

Posted on • Originally published at codeparrot.ai

Setting Up a Node.js, TypeScript, and Express Project

Node.js is a popular runtime environment for building server-side applications. It allows you to write JavaScript code that runs on the server, making it easy to build scalable and performant applications. TypeScript, a superset of JavaScript, adds static typing to the language, enhancing error detection and code maintainability. Express, a minimal and flexible Node.js web application framework, offers a robust set of features for building web and mobile applications. In this tutorial, we'll walk through how to set up a Node.js, TypeScript, and Express project from scratch.

Why Node.js, TypeScript, and Express?

Before we jump into setting up our project, let's take a moment to understand why Node.js, TypeScript, and Express are a great combination for building web applications.

  • Node.js: Node.js is fast, lightweight, and scalable, making it a great choice for building server-side applications.

  • Typescript: Adds type safety to JavaScript, making your code more predictable and easier to debug.

  • Express: A minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications.

Prerequisites

To get started, make sure you have Node.js and npm (Node Package Manager) installed on your machine. You can verify this by running:

node -v
npm -v
Enter fullscreen mode Exit fullscreen mode

If you don't have it installed, head over to the Node.js website and install it.

Get Coding

Initialize your project

  • Create your project folder and enter into it by running:
mkdir node-typescript-express
cd node-typescript-express
Enter fullscreen mode Exit fullscreen mode
  • Next, initialize Node.js to add a package.json file by running:
npm init -y
Enter fullscreen mode Exit fullscreen mode

Install essential packages

Now let's install a few npm packages that we'll need to build this project:

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

This installs Express and TypeScript essentials. We’ve used the -D flag for typescript, @types/node, @types/express, ts-node, and nodemon because these are developer dependencies, meaning they’re only needed during development.

Configuring typescript

Next, we'll configure typescript. Create a tsconfig.json file in the project's root folder by running:

npx tsc --init
Enter fullscreen mode Exit fullscreen mode

Now, let’s adjust a few settings inside tsconfig.json:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "outDir": "./dist"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
Enter fullscreen mode Exit fullscreen mode

These options specify that we’ll compile to ES6, use CommonJS modules, and output our files into a dist folder.

Using a structure for our project

  • Create a src folder in the root directory to hold your TypeScript files:
mkdir src
Enter fullscreen mode Exit fullscreen mode
  • Inside src, create an index.ts file. This will be our entry point.

Writing the Server Code

Open src/index.ts and set up a simple Express server:

import express, { Request, Response } from 'express';

const app = express();
const PORT = process.env.PORT || 3000;

app.get('/', (req: Request, res: Response) => {
  res.send('Hello, TypeScript with Express!');
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

Here, we import Express, define a basic route for the root path (/), and start the server on the specified port.

What’s Happening Here?

  • app.get defines a GET route for our root URL.
  • req and res are typed automatically using TypeScript, making it clear what kind of objects we’re working with.
  • The server runs on http://localhost:3000 (or another port if specified in the environment).

Configure Nodemon for Development

Instead of manually restarting the server each time we make a change, we can use Nodemon.

In package.json, update the scripts section:

"scripts": {
  "start": "node dist/index.js",
  "dev": "nodemon --exec ts-node src/index.ts"
}
Enter fullscreen mode Exit fullscreen mode
  • start runs the compiled Javascript in production mode.
  • dev runs the server in development mode with ts-node automatically restarting whenever any changes are made.

Running our project

Let's test the sample endpoint we just created!

Run the following in your terminal while being in the project folder:

npm run dev
Enter fullscreen mode Exit fullscreen mode

You should see a message in the terminal like: Server is running on http://localhost:3000. Open your browser and go to http://localhost:3000 to see "Hello, TypeScript with Express!" displayed.

Adding Routes and Controllers

Let's make our server more dynamic by adding more routes and controllers.

  • Create a routes folder inside src.
mkdir src/routes
Enter fullscreen mode Exit fullscreen mode
  • Create a new file in src/routes called userRoutes.ts. This will define routes related to user actions.
import { Router, Request, Response } from 'express';

const router = Router();

router.get('/users', (req: Request, res: Response) => {
  res.send('List of users');
});

export default router;
Enter fullscreen mode Exit fullscreen mode
  • Modify index.ts to use this new route:
import express, { Request, Response } from 'express';
import userRoutes from './routes/userRoutes';

const app = express();
const PORT = process.env.PORT || 3000;

app.use('/api', userRoutes);

app.get('/', (req: Request, res: Response) => {
  res.send('Hello, TypeScript with Express!');
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

What's happening here?

  • In the userRoutes.ts file, we're creating a new router using express.Router() and defining a route for /users.
  • In index.ts, we're importing the userRoutes and using it with the /api prefix.
  • This means that the /users route will be available at http://localhost:3000/api/users.

Adding middlewares

Middlewares are functions that have access to the request and response objects and can modify them. Let's add a simple logging middleware to log the request method and path.

  • Create a middleware folder.
mkdir src/middleware
Enter fullscreen mode Exit fullscreen mode
  • Create a new file in src/middleware called logger.ts.
import { Request, Response, NextFunction } from 'express';

const logger = (req: Request, res: Response, next: NextFunction) => {
  console.log(`${req.method} ${req.path}`);
  next();
};

export default logger;
Enter fullscreen mode Exit fullscreen mode
  • Update index.ts to use this middleware:
import express, { Request, Response } from 'express';
import userRoutes from './routes/userRoutes';
import logger from './middleware/logger';

const app = express();
const PORT = process.env.PORT || 3000;

app.use(logger);
app.use('/api', userRoutes);

app.get('/', (req: Request, res: Response) => {
  res.send('Hello, TypeScript with Express!');
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
Enter fullscreen mode Exit fullscreen mode

What's happening here?

  • In the logger.ts file, we're defining a middleware function that logs the request method and path.
  • In index.ts, we're importing the logger middleware and using it with app.use() to apply it to all routes.
  • app.use() is used to mount the middleware function on the specified path.

Conclusion

This setup gives you a structured, scalable project with Node.js, TypeScript, and Express. Here’s a quick recap of what we covered:

  • Project setup and dependency installation.
  • TypeScript configuration.
  • A basic Express server setup.
  • Organized routes and middleware.

Now you’re ready to expand on this setup, whether that means adding database connectivity, more detailed routes, or additional middleware for error handling. Enjoy building with this efficient and type-safe setup!

Top comments (1)

Collapse
 
brense profile image
Rense Bakker

No need for nodemon here. You can just use ts-node-dev instead of ts-node, or even better, you could even serve ts files directly with node by providing a loader node --loader ts-node/esm