@denorm/builder
v0.95.0
Published
DeNorm - Structured Denormalization Database Builder
Maintainers
Readme
@denorm/builder
Database builder that compiles denorm-lang schemas into PostgreSQL DDL and migrations.
Introduction
A Denorm schema is specified in the denorm language, with a simple example being:
table us_states
state_code char(2) primary key
state_name varchar(20)
children count child_table
table child_table
child_id serial primary key
{us_states}
state_name sync us_states.state_name
This business logic exploits data paths that are inherently present in a normalized database, namely the foreign key. Denorm allows the user to specify the flow of values up and down through foreign keys as data channels. With the added ability to calculate derived values within rows, Denorm allows a declarative and single source of truth for business logic in a postgres database.
The use case is read-heavy databaseses, where the "pay me now" decision to update values on writes is cost effective because the major simplication of the development stack and process is not threatened by the differing needs of a write-heavy database. The author's experience in database apps heavily leans to read-heavy line-of-business apps, and that is why he wrote this tool.
This produces a few major simplifications for application development:
- Declarative business logic as part of the schema
- Business logic is validated and cannot be subverted
- Simpler DML in the client app
- No ORM in the client app
Language Reference
The complete language reference is at @denorm/language/docs.
Operating Systems
The author uses Linux exclusively for dev and production. No testing has been done on other operating systems. With that said, it ought to work anywhere you can run postgres and node.
License
@denorm/builder - builder for denorm is Copyright (C) 2026 by Kenneth Downs.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.
Use of Denorm in Build Pipelines
It is the author's understanding of the AGPL V3 that:
You MAY use this software within the build pipeline of any network-enabled database application, without triggering the AGPL V3 requirement for providing source, if the application does not offer the user the ability to specify and build their own databases in a manner that requires invoking any @denorm package to build such databases.
You MAY NOT use this software within the pipeline of a proprietary network-enabled application that offers the user the ability to specify and build their own databases, and invokes any @denorm package, UNLESS all source provisions of the AGPL V3 are met.
In plain terms, you cannot offer a "low code" application builder on top of @denorm, such an app would trigger the AGPL V3. You can build any other database app without triggering the AGPL V3.
Usage in Pipeline
In normal usage, the @denorm/builder package is installed and invoked
via cli, though you can also invoke the API.
Postgres Requirements
A postgres server must be reachable via TCP connection.
Set up the denorm superuser:
create role denorm_superuser createrole createdb password 'secure_password' with login;Copy .env.example to .env and set the connection values. The builder
connects as the denorm superuser using these variables:
DENORM_PGHOST=localhost
DENORM_PGPORT=5432
DENORM_PG_SUPER_USER=denorm_superuser
DENORM_PG_SUPER_PASSWORD=secure_password
DENORM_TEST_DATABASE=denorm_testDENORM_PG_SUPER_USER and DENORM_PG_SUPER_PASSWORD are the credentials
for the role created above. DENORM_TEST_DATABASE is only used by the
test suite.
CLI Usage
denorm -d <database> -s <schema.dnm>Compiles the .dnm schema, introspects the target database, computes the
diff, and applies the migration inside a single transaction. The schema
file must use the .dnm extension and must exist. Exits with code 1 on
missing options, a missing file, or compilation errors (errors are written
to stderr with phase, line, and column).
Options:
-d, --database <database>— target PostgreSQL database name (required)-s, --schema <path>— path to the denorm schema file in.dnmformat (required)--dry-run— run the full pipeline and write all output files, but do not execute the migration against the database--from-scratch— generate a complete build script without connecting to the database; the live schema is treated as empty, so the output is a full create-from-nothing migration--dump-dir <dir>— write all output files to<dir>instead of beside the schema file
Denorm never auto-executes destructive operations. Tables, columns,
and views present in the database but absent from the schema are collected
into a separate .drops.sql script for manual review — they are never run
automatically.
Each run writes inspection/output files (named after the schema file) for every stage of the pipeline:
| Suffix | Contents |
|---|---|
| .newSchema.json | Desired schema compiled from the .dnm source |
| .live.json | Schema introspected from the live database |
| .diff.json | Computed difference between desired and live |
| .drops.json / .drops.sql | Objects to drop (data, JSON) and the manual DROP script |
| .sql / .sql.json | Generated migration DDL (human-readable and as a statement array) |
| .repair.sql | Aggregation-repair script for verification or manual repair |
| .resolved.ts | Resolved schema documentation as TypeScript |
| .manifest.json | UI manifest describing the resolved schema |
Usage in Application
Denorm creates two roles:
- {database}_denorm_triggers has permissions to execute the triggers that calculate values
- {database}_denorm_app_user is a NOLOGIN role that has read/write access to all non-computed columns in all tables.
Database access is provided to roles you create WITH LOGIN that are members of the {database}_denorm_app_user group role.
Hacking @denorm/builder
Pretty standard, install dependencies:
$ npm installRun tests:
$ npm test Run the build:
$ npm run buildUse of LLM
There is no official policy for or against LLM-generated contributions, but evidence as of June 2026 is that the LLM is only useful in 'augmentation' mode, where it is used as a pair programmer by a developer that thoroughly understands the pipeline stages and their details.
Even the best LLM's today are entropy expansion machines. The author did, as a test, vibe-code at least two versions of this library, before settling on the augmentation model. If the LLM is merely given tasks or desired outputs, it will invoke code from step 4 in step 1, or vice-versa. It will duplicate code, and duplicate code with divergent behaviors. In short, the code rapdily becomes unmaintainable, even with an LLM trying to lay out the spaghetti. Every bad coding practice you can think of will appear in the code.
The @denorm/builder is a straight line series of steps that either build on the output of the previous step, or continually add to the final output. That structure must be maintained, with or without an LLM.
History
This project is the spiritual descendant of Andromeda, originally written by Ken Downs in 2002 in PHP, and then maintained by Donald Organ up until about 2012. Denorm is a complete rewrite in Typescript.
