paradox-node

Generate and run full-stack Express.js CRUD servers with PostgreSQL backends from Paradox specifications.

Define your types in .dox, and paradox-node generates a complete server with CRUD routes, Pug-templated HTML views, user authentication, and permissions — then compiles and runs it.

Usage

paradox-node --port 3000 --title "My App" --crud User --crud Post

All flags:

Flag Default Description

--port

(required)

Port to run the server on

--title

(required)

The name of your app (used in manifest and page headers)

--author

null

Author name (included in manifest)

--footer

null

Footer text displayed on HTML pages

--extra

null

Path to a TypeScript module to serve additional routes

--crud

(repeatable)

Product type name to generate CRUD routes for

--account

null

Product type to use for user accounts (enables authentication)

--permissions

null

Matrix variable name for role-based permissions

--database-url

postgres://localhost:5432/paradox

PostgreSQL connection URL

You must provide at least --crud or --extra.

Example

Given this .dox specification:

import std

type User
  name: Text
  email: Text
  password: Text

type Post
  title: Text
  body: Text
  author: Text

Run:

paradox-node --port 3000 --title "Blog" --crud Post --account User

This:

  1. Generates TypeScript types via paradox generate --typescript

  2. Generates PostgreSQL DDL via paradox generate --postgresql

  3. Creates store.ts (pg Pool initialization)

  4. Creates main.ts (full Express server with CRUD + auth)

  5. Compiles both with tsc

  6. Initializes the database schema

  7. Starts the server on port 3000

Generated Routes

For each --crud type, the following routes are generated:

Method Path Description

GET

/<type>/

List all records (HTML table or JSON)

GET

/<type>/create

Show create form (HTML)

POST

/<type>/create

Create a new record

GET

/<type>/:id

Show a single record (HTML or JSON)

GET

/<type>/:id/update

Show edit form (HTML)

POST

/<type>/:id/update

Update a record

GET

/<type>/:id/delete

Show delete confirmation (HTML)

POST

/<type>/:id/delete

Delete a record

All handlers support both JSON and HTML responses. When the request Accept header is application/json, responses are JSON. Otherwise, Pug templates render full HTML pages.

How It Works

paradox-node operates in six stages:

  1. Generate specification — Runs paradox generate --spec to produce a JSON representation of the full .dox AST

  2. Generate TypeScript types — Runs paradox generate --typescript to produce type definitions and validation functions

  3. Generate PostgreSQL DDL — Runs paradox generate --postgresql to produce CREATE TABLE statements, concatenated into schema.sql

  4. Generate server code — Produces store.ts (pg Pool) and main.ts (Express app with all routes, middleware, auth, and views)

  5. Compile — Runs tsc on the generated TypeScript files

  6. Run — Starts the compiled server, initializing the database schema on boot

CRUD Operations

Each CRUD type gets a full set of database-backed handlers using parameterized SQL queries:

  • Create — INSERT with validation via the generated valid<Type> function

  • Read — SELECT …​ WHERE id = $1

  • Update — UPDATE …​ SET …​ WHERE id = $N with validation

  • Delete — DELETE …​ WHERE id = $1

  • List — SELECT * with both tabular HTML and JSON output

Authentication

When --account is provided, the server generates a full session-based authentication system:

  • POST /register — Create a new account (the account type must have email and password fields)

  • GET /register — Registration form

  • POST /login — Authenticate with email and password

  • GET /login — Login form

  • GET /logout and POST /logout — End the session

Sessions are stored in-memory and tracked via an httpOnly cookie. Protected routes redirect unauthenticated users to /login.

Permissions

When --permissions is provided with a Paradox matrix variable, CRUD operations check the permission matrix before allowing access. Each operation (create, read, update, delete) is checked against the matrix for the relevant type.

Illumination

If a type has an illuminate interface instance, the generated server uses the illumination function instead of JSON.stringify when rendering views, providing custom HTML rendering of values.

HTML Views

The server uses Pug templates for HTML rendering:

  • List view — Tabular display of all records with links to view, edit, and delete

  • Item view — Detail view of a single record with edit and delete buttons

  • Create/Edit forms — Auto-generated forms based on the product type’s fields

  • Login/Register — Authentication forms (when --account is used)

Union fields render as dropdown selects, product fields render as nested forms, and standard fields render as text inputs.

Extra Routes

The --extra flag accepts a path to a TypeScript module that exports additional route handlers. The module’s named exports become GET routes at /<export-name>. If the module exports a uriHandlers object, its entries are registered as both GET and POST routes.

Database Initialization

On startup, the server reads the generated schema.sql and executes each statement. It uses multi-pass execution to handle forward references (e.g., foreign keys referencing tables defined later). CREATE TABLE IF NOT EXISTS semantics ensure safe restarts.

Health Check

The server serves a root GET / route that renders the index page with the manifest data (title, author, footer, available types).

See Also