SolidJS

With `"solidjs"` in `front-end`, the generator writes one TypeScript interface per model and a `Services.ts` file with one API service per model. The services run on a small `ModelService` base class that keeps a Solid store mirrored to IndexedDB.

Configure

{ "front-end": ["solidjs"] }
npx degit solidjs/templates/ts solidjs
cd solidjs && npm install && npm install the-solid-router
cd .. && php setup.php

For development, point Vite at the backend:

// solidjs/vite.config.ts
server: { port: 3000, proxy: { "/api": { target: "http://localhost:9000", changeOrigin: true } } }

Generated files

PathContents
src/shared/Interface/Model/<Name>.tsAn interface matching the table.
src/shared/Service/Services.tsOne ModelService instance per model.
src/shared/run.tsThe tables list, plus start-up and shutdown hooks that hydrate or clear IndexedDB.
// Services.ts (generated)
import { ModelService } from "./Service";
import { Client } from "../Interface/Model/Client";

export const ClientService = (new ModelService<Client>())
    .seTable("client")
    .seturl("/api/islogin/client/");

export * from "./Custom";   // lines like this are kept on regeneration

The ModelService base class

The generator does not write ModelService itself. Services.ts imports it from ./ModelService if that file exists, and from ./Service otherwise, so you need to add one of those files to your project. A working implementation is in the INTAX billing app, at solidjs/src/shared/Service/Service.ts and Store.ts. Copy it from there.

ModelService<T> extends Stores<T>, which wraps a Solid createStore({ data: [] }). Every write to the store is also saved to IndexedDB.

MethodHTTP callStore
all()On the first call it loads IndexedDB, then GET url. After that it sends GET url?latest=<newest updated_at>.upsert
get(id)GET url/idupsert
create(body)POST urladd
update(id, body)POST url/idupdate
upsert(rows)PATCH urlupsert
where(filter)POST url/whereupsert
del(id)DELETE url/iddelete
allstate(), findState(), getState()none (reads the store)none
import { onMount, For } from "solid-js";
import { ClientService } from "../shared/Service/Services";

export default function Clients() {
  onMount(() => ClientService.all());
  return <For each={ClientService.allstate()}>{(c) => <p>{c.name}</p>}</For>;
}

Sign-in and offline data

run.ts exports the tables list, which is also the list of IndexedDB object stores.

  • After login: call run.dbset(). It hydrates every service from IndexedDB, so screens render immediately; the following all() calls then fetch only the changes.
  • On logout: clear IndexedDB with indexdb.The_clearData().

Routing

The apps use the-solid-router:

import { Router, Routes, Route } from "the-solid-router";

<Router>
  <Routes>
    <Route path="/" guard={new run().set} component={Layout}>
      <Route path="login" guard={notLogin} component={LoginPage} />
      <Route path="book/:id" guard={isLogin} component={Book} />
    </Route>
  </Routes>
</Router>

Routes can be nested, and each one can take a guard that decides whether the route may be entered. INTAX uses new run().set as the guard on its root route: it hydrates the stores after login and clears IndexedDB after logout.

Real-world example

The INTAX billing app is the most complete SolidJS client. It combines the generated ModelService layer with a hand-written typed client (src/shared/api.ts) for its book-scoped endpoints, which live under /api/islogin/book/:book_id/….