npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@injitools/db

v0.2.1

Published

TypeORM integration for Inji: ORM-derived DTOs, Zod schema generation from column metadata, value transformers and connection helpers.

Readme

@injitools/db

TypeORM integration for the Inji framework: derive DTO validation from entity column metadata in both directions (@OrmLink on a @RequestDto/@ResponseDto), generate Zod schemas, plus value transformers and connection helpers.

Part of the Inji monorepo.

Install

npm install @injitools/db typeorm

typeorm is a peer dependency. big.js is optional (needed only for BigTransformer).

Usage

import {OrmLink, dbConnect, loadDbConfigFromEnv} from "@injitools/db";
import {ResponseDto} from "@injitools/core";

const db = await dbConnect(loadDbConfigFromEnv());

@ResponseDto(UserOrm, db)   // or @RequestDto(UserOrm, db) for an input body
class UserDto {
  @OrmLink() id: bigint;
  @OrmLink() email: string;
}

Importing @injitools/db registers the ORM validation resolver into @injitools/core (via setOrmZodResolver()), so @OrmLink fields validate automatically. This keeps the core free of TypeORM while @OrmLink derivation works (request and response) as soon as this package is present.

Overriding a derived field

@OrmLink derives the whole schema from the column, but two escape hatches let you adjust it without unlinking the field: the column still drives nullability/optionality, and a renamed or removed column still fails loudly (which a hand-written @DtoProperty would not catch).

@RequestDto(UserOrm, db)
class RegisterDto {
  // extend — refine the derived schema. Here: the column's varchar(64) already gives max(64);
  // this adds a minimum on top. The callback receives the derived schema and returns a new one.
  @OrmLink({extend: (s) => s.min(3)})
  login: string;

  // validation — replace the derived type wholesale, when the wire form differs from the column's.
  @OrmLink({validation: z.string().uuid()})
  external_id: string;
}

Both run on the bare derived type — after the column type (and array-ness) is resolved, but before the direction adds .nullable()/.optional(). That ordering is what lets extend call refinements: it sees a plain ZodString, not a ZodOptional that has no .min(). If you pass both, validation replaces first and extend then applies on top of the replacement.

Dates on the wire

A moment-in-time column (timestamptz, timestamp, datetime, …) is an ISO-8601 string with an offset on the wire, and a Date in the entity. The derivation reflects exactly that:

  • @ResponseDtoz.iso.datetime({offset: true}) — the ISO string as sent.
  • @RequestDto → the same ISO string, parsed into a Date. A ready Date is also accepted (an inter-server caller may pass one). Your domain services should take a Date — the wire format is the API layer's business, and the DTO has already dealt with it.

This is deliberately stricter than z.coerce.date(), which used to back this and which silently accepted a naive "2026-01-01T00:00:00" (no offset — the very ambiguity timestamptz exists to prevent) and turned the number 0 into 1970-01-01. It is also inexpressible in JSON Schema, so zod-openapi degraded it to a bare {type: "string"} and the generated contract lost its format: date-time.

date, time and year columns are not moments in time and still use the old coercion — they carry no offset, so the ISO-datetime form would reject them.

Conventions

Don't repeat what the TS type already says

With emitDecoratorMetadata: true, TypeORM reads the property's TS type and normalizes it to a DB type, and generateOrmZodValidation() handles the resulting constructor form (String/Number/ Boolean/Date) in the same switch branches as the string aliases. So for these, type is pure noise — the derived DB type AND the emitted JSON Schema are byte-identical either way:

| Instead of | Write | Inferred DB type | |---|---|---| | @Column({type: 'varchar', length: 64}) | @Column({length: 64}) | character varying(64) | | @Column({type: 'boolean', default: true}) | @Column({default: true}) | boolean | | @Column({type: 'int', default: 0}) | @Column({default: 0}) | integer |

Always keep type explicit when the TS type can't express it — inference is either wrong or impossible:

  • text — a string infers to varchar, never text.
  • timestamptz — a Date infers to timestamp without time zone (see below).
  • bigint — a bigint property is a hard DataTypeNotSupportedError on Postgres without it.
  • jsonb, uuid, enum — not derivable from Object/string.

Also keep everything the TS type erases or never carried: length (a bare string becomes an unbounded varchar, and Zod then has no .max() to derive), nullable (the | null union is erased at compile time), unsigned, default, unique.

Inference fails loudly, not silently: with emitDecoratorMetadata off, TypeORM throws ColumnTypeUndefinedError while building metadata.

Dates are always timezone-aware

Every date/time column must declare an explicit timezone-aware type — {type: 'timestamptz'} (Postgres) or the equivalent on your driver — including @CreateDateColumn/@UpdateDateColumn.

Left without a type, TypeORM falls back to a driver default that is not timezone-aware: on Postgres it's plain timestamp (PostgresDriver.mappedDataTypes.createDate === "timestamp"), which stores a naive wall-clock value with no offset. generateOrmZodValidation() maps both timestamp and timestamptz to the same Zod schema (z.coerce.date() on request, z.iso.datetime({offset: true}) on response), so a missing timezone doesn't surface as a validation error — it silently produces ambiguous timestamps that drift once the app server or the database runs in a different zone. Declare the type explicitly on every date column:

@CreateDateColumn({type: 'timestamptz'})
created_at: Date;

@Column({type: 'timestamptz', nullable: true})
last_seen: Date | null;

License

MIT