Skip to content

One domain, from the database to the browser

Your domain is one thing. It starts in the database and reaches the browser without being translated on the way.

A type is defined once, and everything downstream knows it – the database, the Java code, the TypeScript in the browser. Nothing is hand-mapped between the two sides. The common language is a GraphQL schema, and you do not write it: it comes from your tables and your server code.

Filters are the same story. One condition, written once, runs as SQL against the database, as a predicate over Java objects, or in the browser – in the semantics of wherever it lands, rather than a lowest common denominator.

The grid below sorts and filters by a column no table has, the sum of two others:

field("numA").add(field("numB"))

Running against an actual QLive server, that expression would be translated into SQL running on the database.

This page however has no such server, it just runs on Github Pages, a static file host. So here we reuse a method normally meant for testing or Storybook: We record the injections of a live example and the grid here then runs on recorded rows. It evaluates the recorded rows against the same current filter expression transformed into a JavaScript function. From the component’s perspective, nothing changes.

Click the sum’s header, or try limiting the date range.

A view’s query is a static declaration in that shared language, so the server can tell what a page needs before the page exists:

import {useInjection} from "@qlivedev/qlive-ts";
export default function Home() {
const foos = useInjection(Q_Foo);
// ...
}

It runs the query and puts the result in the page. Nothing fetches it, and there is no loading state to render. Components get simpler for it, and their tests more so: there is no fetch to mock and nothing to wait for.

It does the same job as React Server Components, but with a Java server and a declarative way to express data needs.

  • Java 25
  • pnpm
  • vite
  • React 18
  • Spring Boot
  • jOOQ

Start with the Overview, which explains what the framework does and how a page reaches the browser.

The rest is grouped by what you came for:

  • Explanation – what QLive does and why it is built that way. Meant to be read through, in the order it is in.
  • How-to guides – one task at a time: wiring up a Spring application, refreshing the schema after a domain change, adding an entry point.
  • Reference – the application layout, and the artifacts the codegen writes. Meant to be looked up, not read.
  • API – every export of @qlivedev/qlive-ts, generated from the declarations themselves.