Documentation
omni-rest-client

omni-rest-client

omni-rest-client is a standalone frontend code generator β€” no Prisma, no schema.prisma, no database connection required. It reads a omni-rest.config.json file produced by your backend and generates a complete React data layer.

This is the recommended way to generate frontend code starting from v0.4.0. The old npx omni-rest generate:frontend still works but is considered legacy.


How it works

The workflow is split cleanly between two CLIs:

Backend project                        Frontend project
────────────────────────────           ──────────────────────────────────
npx omni-rest generate:config    β†’     npx omni-rest-client generate:frontend
  ↓                                      ↓
omni-rest.config.json                  hooks/, components/, pages/
(model metadata + Zod schemas)         schemas.generated.ts

The backend CLI knows about Prisma. The client CLI knows about React. Neither needs to know about the other's internals.


Step-by-step

Install omni-rest in your backend

npm install omni-rest
npx prisma generate

Generate the config file

Run this from your backend project root (where prisma/schema.prisma lives):

npx omni-rest generate:config

This writes omni-rest.config.json β€” a plain JSON file containing:

  • All model names, field names, types, and metadata
  • Full Zod schema source (the same as schemas.generated.ts)
{
  "version": "1",
  "generatedAt": "2026-03-29T10:00:00.000Z",
  "models": [
    {
      "name": "User",
      "routeName": "user",
      "idField": "id",
      "fields": [
        { "name": "id",    "type": "Int",    "isId": true,  "isRequired": true, ... },
        { "name": "name",  "type": "String", "isId": false, "isRequired": true, ... },
        { "name": "email", "type": "String", "isId": false, "isRequired": true, ... }
      ]
    }
  ],
  "zodSchemas": "import { z } from 'zod';\n\nexport const UserCreateSchema = ..."
}

Copy the config to your frontend (or reference it by path)

You can commit omni-rest.config.json to your repo, copy it to your frontend project, or reference it by relative path with --config.

Install omni-rest-client in your frontend

npm install -D omni-rest-client

Also install the runtime dependencies your generated code needs:

npm install @tanstack/react-query @tanstack/react-table react-hook-form zod @hookform/resolvers

Run the client generator

npx omni-rest-client generate:frontend

If your config is in a different location:

npx omni-rest-client generate:frontend --config ../backend/omni-rest.config.json

With autopilot (no prompts):

npx omni-rest-client generate:frontend --autopilot

Wrap your app with TanStack Query

The generator writes a components/providers.tsx for Next.js App Router. Add it to your layout:

// app/layout.tsx
import { Providers } from '@/src/components/providers'
 
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <Providers>{children}</Providers>
      </body>
    </html>
  )
}

Use the generated components

// app/users/page.tsx
import { UserTable } from '@/src/components/user/UserTable'
 
export default function UsersPage() {
  return (
    <div className="container mx-auto py-10">
      <h1 className="text-3xl font-bold mb-6">Users</h1>
      <UserTable />
    </div>
  )
}

What gets generated

For a User model:

    • schemas.generated.ts
      • useUser.ts
        • UserColumns.tsx
        • UserTable.tsx
        • UserForm.tsx
      • data-table.tsx
      • form-generator.tsx
      • providers.tsx
      • menu-data.ts
        • page.tsx
  • FileDescription
    schemas.generated.tsZod schemas extracted from config (no Prisma needed)
    hooks/useUser.tsTanStack Query hooks β€” list, get, create, update, delete, bulk-delete
    UserColumns.tsxTanStack Table column definitions
    UserTable.tsxDataTable wired to hooks, bulk delete, export
    UserForm.tsxFormGenerator with Zod validation and relational dropdowns
    data-table.tsxShared DataTable base component (copied once)
    form-generator.tsxShared FormGenerator base component (copied once)
    providers.tsxQueryClientProvider wrapper (Next.js App Router)
    lib/menu-data.tsNavigation structure for all generated models
    app/.../page.tsxNext.js App Router page per model

    Generated hook example

    Unlike the legacy generator, hooks use generic types β€” no @prisma/client import:

    // src/hooks/useUser.ts
    import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
     
    type User = Record<string, any>;
    type UserCreateInput = Record<string, any>;
    type UserUpdateInput = Record<string, any>;
     
    const BASE_URL = "/api";
     
    export function useUsers(params?: Record<string, any>) {
      return useQuery({
        queryKey: ["user", params],
        queryFn: () => {
          const url = new URL(`${BASE_URL}/user`, window.location.origin);
          if (params) {
            Object.entries(params).forEach(([k, v]) => {
              if (v !== undefined && v !== null) url.searchParams.set(k, String(v));
            });
          }
          return fetch(url.toString()).then((r) => r.json());
        },
        staleTime: 30000,
        gcTime: 300000,
      });
    }
     
    export function useCreateUser() { /* POST with optimistic update */ }
    export function useUpdateUser() { /* PATCH with optimistic update */ }
    export function useDeleteUser() { /* DELETE with optimistic removal */ }
    export function useBulkDeleteUsers() { /* bulk DELETE */ }

    Flags reference

    FlagDefaultDescription
    --config <path>./omni-rest.config.jsonPath to config file
    --frontend-dir <path>cwdFrontend project root
    --out <dir>src/ or rootOutput directory relative to frontend dir
    --models <names>allComma-separated model names to generate
    --base-url <url>/apiAPI base URL (overrides env file detection)
    --framework <name>auto-detectnextjs | vite-react | react
    --autopilotfalseSkip all prompts, use defaults
    --no-bulkβ€”Disable bulk delete
    --no-optimisticβ€”Disable optimistic updates
    --no-pagesβ€”Disable Next.js page generation
    --no-menuβ€”Disable menu-data.ts generation
    --no-schemasβ€”Skip writing schemas.generated.ts
    --route-group <name>autogenerated-omniNext.js route group name
    --stale-time <ms>30000TanStack Query staleTime
    --gc-time <ms>300000TanStack Query gcTime
    --steps <mode>autoMulti-step forms: auto | always | never
    --helpβ€”Print usage

    Re-running after schema changes

    # Backend: regenerate config after schema changes
    npx prisma generate
    npx omni-rest generate:config
     
    # Frontend: regenerate components from updated config
    npx omni-rest-client generate:frontend --autopilot

    Hook and column files are overwritten on each run. Base components (data-table.tsx, form-generator.tsx) are skipped if they already exist, preserving any customizations.


    Monorepo setup

    In a monorepo where backend and frontend are siblings:

    # From the frontend package
    npx omni-rest-client generate:frontend \
      --config ../backend/omni-rest.config.json \
      --autopilot

    Or add it as a script in your frontend package.json:

    {
      "scripts": {
        "generate": "omni-rest-client generate:frontend --config ../backend/omni-rest.config.json --autopilot"
      }
    }

    Troubleshooting

    "Config file not found"
    Run npx omni-rest generate:config in your backend project first, then copy or reference the output file.

    "Invalid config file: missing models array"
    The config file is malformed or from an older version. Regenerate it with npx omni-rest generate:config.

    Missing dependency warnings
    Install the listed packages: npm install @tanstack/react-query @tanstack/react-table react-hook-form zod @hookform/resolvers

    Schemas not found at runtime
    Make sure schemas.generated.ts is in your output directory. It's written automatically unless you pass --no-schemas.