DEV Community

Cover image for Setting Up a Modern Express API with TypeScript, Jest, ESLint, Prettier - Quick guide 2025
János K.
János K.

Posted on • Edited on

Setting Up a Modern Express API with TypeScript, Jest, ESLint, Prettier - Quick guide 2025

Building a backend API in Node.js? Setting up the project can be a bit of a slog - and enough to put you off before you even start building that next-gen social media app.

I couldn't help but wonder: where are the quick, modern guides to spinning up a well-structured Express API?

There aren’t many. So I made one.

Let’s get straight to it.

🛠 1. Initialise your project
npm init -y

This sets up your package.json, which manages your dependencies, scripts, and project metadata. The -y flag accepts all the default values so you can move on quickly.

📦 2. Install Express and dev dependencies
npm install express
npm install --save-dev typescript supertest nodemon jest ts-jest ts-node @types/jest @types/supertest @types/express

What these do:

  • express: Your HTTP server framework.
  • typescript: For strongly typed JS.
  • supertest: For testing your API endpoints.
  • jest, ts-jest: Testing framework and TypeScript preprocessor.
  • nodemon: Restarts the server on changes.
  • ts-node: Runs TypeScript files directly.
  • @types/...: Adds type support for testing and Express.

🧠 3. Configure TypeScript
npx tsc --init

Now replace your generated tsconfig.json with:

{
  "exclude": [
    "./coverage",
    "./dist",
    "**/*.test.ts",
    "jest.config.js",
    "eslint.config.mjs"
  ],
  "ts-node": {
    "transpileOnly": true,
    "files": true
  },
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "rootDir": "./src",
    "moduleResolution": "node",
    "checkJs": true,
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitAny": true,
    "skipLibCheck": true
  }
}
Enter fullscreen mode Exit fullscreen mode

The linter may freak out, until you create your first .ts file, which we will do now. ;)

🧱 4. Create your Express app
Here's the directory structure for our app:

mkdir -p src/routes && touch src/app.ts src/app.test.ts src/server.ts src/routes/user.routes.ts src/routes/user.routes.test.ts

src/
├── app.ts
├── app.test.ts
├── server.ts
└── routes/
        └── user.routes.ts
        └── user.routes.test.ts
Enter fullscreen mode Exit fullscreen mode

src/server.ts

import app from './app';

const PORT: number = 5050;

app.listen(PORT, (): void => {
  // eslint-disable-next-line no-console
  console.log(`Server is running on ${PORT}...`);
});

Enter fullscreen mode Exit fullscreen mode

src/app.ts

import express, { Application, Request, Response} from 'express';
import { router as userRoutes } from './routes/user.routes';

const app: Application = express();

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

app.use('/', (req: Request, res: Response): void => {
  res.json({ message: "Miley, what's good?" });
})

export default app;

Enter fullscreen mode Exit fullscreen mode

src/routes/user.routes.ts

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

const router = Router();

router.get('/', (req: Request, res: Response): void => {
  const users = ['Nicki', 'Ariana', 'Lana', 'Miley'];
  res.status(200).send(users);
});

export { router };
Enter fullscreen mode Exit fullscreen mode

🧪 5. Set up testing
npx ts-jest config:init

Replace the generated jest.config.js's contents with:

export const preset = 'ts-jest'
export const testEnvironment = 'node'
Enter fullscreen mode Exit fullscreen mode

This sets up Jest to use the TypeScript compiler.

🧷 6. Add some tests

src/app.test.ts

import request from 'supertest';
import app from './app';

describe('Test app.ts', () => {
  test('Is alive route', async () => {
    const res = await request(app).get('/');
    expect(res.body).toEqual({ message: "Miley, what's good?" });
  });
});
Enter fullscreen mode Exit fullscreen mode

src/routes/user.routes.test.ts

import request from 'supertest';
import app from '../app';

describe('User routes', () => {
  test('Get all users', async () => {
    const res = await request(app).get('/users');
    expect(res.body).toEqual(['Nicki', 'Ariana', 'Lana', 'Miley']);
  });
});

Enter fullscreen mode Exit fullscreen mode

⚙️ 7. Update package.json scripts
Update the scripts section:

"scripts": {
  "test": "jest --coverage",
  "dev": "nodemon ./src/server.ts",
  "build": "tsc",
  "lint": "eslint src/**/*.ts",
  "lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix"
}
Enter fullscreen mode Exit fullscreen mode

(Don't worry about the lint scripts for now, we will set this up in the following step.)

While you're at it, remove "type":"module" from package.json.

Give this a go: npm run test

🧹 8. Set up ESLint + Prettier

npm install -D eslint @eslint/js @types/eslint__js typescript-eslint eslint-plugin-prettier eslint-config-prettier

Now create the ESLint config file touch eslint.config.mjs:

import js from '@eslint/js'
import tseslint from 'typescript-eslint'
import eslintPluginPrettier from 'eslint-plugin-prettier'
import prettier from 'eslint-config-prettier'

export default [
  js.configs.recommended,
  ...tseslint.configs.recommended,
  {
    files: ['**/*.ts'],
    languageOptions: {
      parser: tseslint.parser,
      parserOptions: {
        project: './tsconfig.eslint.json',
        sourceType: 'module',
      },
    },
    plugins: {
      prettier: eslintPluginPrettier,
    },
    rules: {
      'no-unused-vars': 'error',
      'no-undef': 'off',
      'prefer-const': 'error',
      'no-console': 'warn',
      'no-debugger': 'warn',
      'prettier/prettier': [
        'error',
        {
          singleQuote: true,
          semi: true,
          trailingComma: 'all',
          printWidth: 100,
          tabWidth: 2,
        },
      ],
    },
  },
  prettier,
  {
    ignores: ['dist/**', 'node_modules/**', 'coverage/**'],
  },
]

Enter fullscreen mode Exit fullscreen mode

Let's also add TypeScript config for ESLint

Create a touch tsconfig.eslint.json:

{
  "extends": "./tsconfig.json",
  "include": ["src/**/*.ts", "src/**/*.tsx"],
  "exclude": ["node_modules", "dist"]
}
Enter fullscreen mode Exit fullscreen mode

You now have:

A strongly typed Express API 💪

Tests with Jest + Supertest 🧪

Auto formatting & linting with ESLint + Prettier ✨

You’re all set to build something real - without battling the boilerplate every time.

Thank you, please like and subscribe! 💾
Voilà! If you enjoy this content, please consider supporting my efforts. Your generosity fuels more of what you love! 🩷


Buy me a coffee


I'm János, I write about Software, coding, and technology.
Checkout my portfolio

Top comments (0)