Remult · one TypeScript entity, a full type-safe API

3 months ago

In a typical stack, you write the same data shape three times. A database table. Backend REST routes. And a typed client on the frontend. Then you keep all three in sync by hand, and every drift becomes a runtime bug. Remult flips that: you write the shape once.

Ask

Ask about this presentation

Answers are generated from this presentation.

Chapters

  1. 0:00Remult
  2. 0:15Your entity is the single source of truth
  3. 0:35Define it once
  4. 0:51Serve it: the REST API, zero routes
  5. 1:03The payoff: type-safe on the frontend
  6. 1:19One more win: the admin UI
  7. 1:34One entity, in sync by construction
Show transcript

Remult

One TypeScript entity · a secure REST API, a type-safe client, and an admin UI, all from the same class.

one entity → remult → REST · client · admin

In a typical stack, you write the same data shape three times. A database table. Backend REST routes. And a typed client on the frontend. Then you keep all three in sync by hand, and every drift becomes a runtime bug. Remult flips that: you write the shape once.

Your entity is the single source of truth

  • A secure REST API · full CRUD with paging, sorting, and filtering, zero routes.
  • A type-safe frontend client · the same typed data access, over the wire.
  • A backend query builder · Postgres, MySQL, MongoDB, SQLite, and more.

Remult is a fullstack framework for TypeScript, built on one idea. Your entity class is the single source of truth. From it you get a secure REST API. A type-safe client that gives your frontend the same typed access, over the wire. And a backend query builder, across Postgres, MySQL, MongoDB, and more. It is framework-agnostic, adopted one entity at a time.

Define it once

@Entity('products', {
  allowApiCrud: true,
  allowApiDelete: 'admin',
})
export class Product {
  @Fields.id()
  id!: string
  @Fields.string()
  name = ''
  @Fields.number()
  unitPrice = 0
}

One decorated class · an id, a name, a price.

It starts with one decorated class. Here is your product entity: an id, a name, and a price. The options are where the power sits. One flag turns on the full REST API; another restricts deletes to admins only. This one file is the single source of truth.

Serve it: the REST API, zero routes

import { remultApi } from 'remult/remult-express'

app.use(
  remultApi({ entities: [Product] }),
)

Full CRUD for /api/products · paging, sorting, filtering. You wrote no route handlers.

Now serve it. You pass your entity to Remult's request handler and mount it on your server. That single line is a complete REST API for products, with paging, sorting, and filtering. You wrote no route handlers at all.

The payoff: type-safe on the frontend

const productsRepo = repo(Product)

// insert: typed, no fetch, no route
await productsRepo.insert({
  name: 'Tofu', unitPrice: 5,
})

// query: same entity, type-checked
const products = await productsRepo.find({
  where: { unitPrice: { '>=': 5 } },
  orderBy: { name: 'asc' },
})

Rename a field, and every wrong query stops compiling.

Here is the payoff. On the frontend, the same entity gives you fully typed access. Insert a product: no fetch call, no route. Query for everything priced at five or more, sorted by name. Checked by the compiler. Rename a field, and every wrong query stops compiling.

One more win: the admin UI

admin: true  →  /api/admin

Remult Admin, a CRUD web UI over your entities

Browse every table, see relations as a diagram, get realtime updates · all from the same entity.

One more win from that same entity. Turn on the admin option, and Remult serves a full admin panel at slash api slash admin. Browse and edit every table, see your relations as a diagram, and get realtime updates on connected clients. All from that one entity.

One entity, in sync by construction

  • Secure REST API · full CRUD, zero routes.
  • Type-safe client · same access, checked.
  • Admin UI · free, from the same class.
  • Incremental · your framework, your database.

npm init remult@latest  ·  remult.dev

That is the arc. One entity becomes a secure REST API, a type-safe client, and an admin panel, all checked by the compiler and always in sync. It works with your framework and your database, and you add it one entity at a time. Start here: remult dot dev.