background: I have a NodeJS project that’s currently written in JavaScript and I’m converting it to be TypeScript. I’m trying to make things type-safe, from database logic to application logic.

My database layer in JavaScript uses knex.js + Objection.js. I’m moving to Kysely for less overhead/more type safety along with the TypeScript conversion. Since this project is kind of large already, I’m doing an incremental conversion, and both systems will stay in the codebase until I rewrite enough of the core application and tooling.

So - what’s the problem? I use BigInts in a few places. I can get them from the database as strings:

createPool({
    // ...
    supportBigNumbers: true,
    bigNumberStrings: true,
})

This is “fine,” I just have to explicitly convert them with BigInt(foobar) everywhere. This code clutter isn’t ideal but it worked, and it prevents issues at runtime if I need to do any math or comparisons.

However, I wanted to make this more type-safe at runtime and compile-time. I want my database layer to return BigInt types in my models now that I’m moving to TypeScript.

After a couple hours of different ideas, I discovered you can add your own logic in a typeCast callback:

createPool({
    // ...
    supportBigNumbers: true,
    bigNumberStrings: true,
    typeCast: (field, next) => {
        if (field.type === 'LONGLONG') {
            const value = field.string();
            return (value === null) ? null : BigInt(value);
        }

        return next();
    }
})

This connection passes into Knex+Objection.js and Kysely, and I can now get BigInt types in my models.

P.S. I also tried Prisma (full blown ORM) but it doesn’t support the full range of unsigned BigInts yet. I did decide to keep prisma’s schema format, and I use prisma-kysely to generate types for Kysely.