supertest request body is undefined

I want to test my express server POST endpoint using supertest. Everything works fine in postman, but when I try to pass in body parameters into the test, as can be seen in the code snippets below, it appears that the body parameters don’t get passed in correctly. I get request body undefined.

Code from my controller.ts

import { Router } from 'express';
import { z } from 'zod';
import { Kysely } from 'kysely';
import * as sprints from './services';
import * as schema from './schema';
import { DB } from '../../database';

export function createSprintsRouter(db: Kysely<DB>) {
  const router = Router();

  router.post('/', async (req, res) => {
    try {
      const parsedInput = schema.parseNewSprintInput.parse(req.body);
      const newSprint = await sprints.createSprint(db, parsedInput);
      res.status(201).json(newSprint);
    } catch (err) {
      if (err instanceof z.ZodError) {
        res.status(400).json({ err: err.errors });
      } else {
        res.status(500).json({ err: (err as Error).message });
      }
    }
  });

  return router;
}

Code from my test file controller.spec.ts

import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import supertest from 'supertest';
import express from 'express';
import { Kysely } from 'kysely';
import { createSprintsRouter } from '../controller';
import { getTestDbInstance } from '../../../database/db-test';
import type { DB } from '@/database/types';

describe('Sprints Controller', () => {
  let testDb: Kysely<DB>;
  let app: express.Application;

  beforeAll(async () => {
    testDb = getTestDbInstance();
    const sprintsRouter = createSprintsRouter(testDb);
    app = express().use('/sprints', sprintsRouter);

    await testDb
      .insertInto('sprints')
      .values([
        { code: 'TST1', title: 'Test Sprint 1' },
        { code: 'TST2', title: 'Test Sprint 2' },
      ])
      .execute();
  });

  afterAll(async () => {
    await testDb.destroy();
  });

  describe("POST '/' endpoint", () => {
    it('should create a new sprint', async () => {
      const newSprint = { code: 'TST3', title: 'Test Sprint 3' };

      const res = await supertest(app)
        .post('/sprints')
        .set('Content-Type', 'application/json')
        .send(newSprint);

      expect(res.status).toBe(201);
      expect(res.body).toEqual(
        expect.objectContaining({
          code: 'TST3',
          title: 'Test Sprint 3',
        })
      );
      const allSprints = await supertest(app).get('/sprints');
      expect(allSprints.body).toHaveLength(3);
    });
  });
});

I’ve searched solutions to this problem and most of the time it comes up that you have to use app.use(bodyParser.json()); before the routes (which I’m using). But the problem still persist.

My main app.ts

import express from 'express';
import './bot/discordBot';
import messages from './modules/messages/controller';
import messageTemplates from './modules/message-templates/controller';
import db from './database/index';
import { createSprintsRouter } from './modules/sprints/controller';

const app = express();
app.use(express.json());
const sprintsRouter = createSprintsRouter(db);

app.use('/messages', messages);
app.use('/templates', messageTemplates);
app.use('/sprints', sprintsRouter);

export default app;