Skip to content

No ORM

All the best parts of an ORM without using an ORM.
  1. Define your Postgres schema.
CREATE TABLE penguins (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
species TEXT NOT NULL,
waddle_speed_kph NUMERIC NOT NULL,
favourite_snack TEXT, -- Optional: not all penguins have refined palates.
date_of_birth TIMESTAMP WITH TIME ZONE NOT NULL
);
  1. Run no-orm against your database.
npm no-orm --config no-orm.config.ts
  1. Enjoy generated code in your Typescript server!
no-orm/public/penguins/table.ts
import { z } from "zod";
import { type ListSqlToken, sql } from "slonik";
export const row = z.object({
id: z.number().brand<"public.penguins.id">(),
name: z.string(),
species: z.string(),
waddle_speed_kph: z.number(),
favourite_snack: z.string().nullable(),
date_of_birth: z.date(),
});
export type Row = z.infer<typeof row>;
export type Id = Row["id"];
export const tableFragment = sql.identifier(["public", "penguins"]);
export const columns = Object.keys(row.shape).map((col) =>
sql.identifier([col]),
);
export const columnsFragment = sql.join(columns, sql.fragment`, `);
export function aliasColumns(alias: string): ListSqlToken {
const aliasedColumns = Object.keys(row.shape).map((col) =>
sql.identifier([alias, col]),
);
return sql.join(aliasedColumns, sql.fragment`, `);
}

At No ORM, we believe you should be able to build a server without having to learn an ORM abstraction.

Abstractions work by hiding implementation details which makes using your database simpler, but also harder to understand and deeply configure. You gain simplicity but lose control.

Our opinion is that PostgreSQL is already a great abstraction. Adding another layer on top often just obscures what your database is doing and increasing the likelihood to make poor database design decisions.

We recognise however that ORMs do have their benefits however, so the motivation of No ORM is to capture all of the best bits of an ORM while also preserving what makes PostgreSQL so great.

We believe in a schema-first approach to server design.

With a traditional ORM, you start by defining your entities and the ORM will generate your schema.

ORM philosophy overview diagram.

With No ORM, you start by defining your schema and we give you your entities!

ORM philosophy overview diagram.

Most applications are CRUD operations combined in a way to solve a problem (not to be taken as a critique). Therefore, since less code is easier to maintain, we should try to minimise any additional code written outside of these operations.

This additional code can come in the form of critical business logic, but it can also come in the form of tech-debt masking poor database design.

A schema-first approach means that you can design your database without wading through ORM abstractions, helping you achieve your goal of reducing complexity and building maintainable software.

You might love your ORM for various reasons.

Maybe you’re not familiar with SQL and prefer the abstraction. Maybe you believe your ORM provides you with useful features that raw Postgres doesn’t. Maybe your ORM is so coupled with the framework you are using that it’s impractical to use anything else.

That is completely understandable and if so, this tool probably isn’t for you!