u/Prestigious-Bee2093

Contract-first REST on MCUs: OpenAPI spec → admin UI without writing a dashboard

I have been working on UIGen: a runtime that turns an OpenAPI spec into a full admin UI (sidebar nav, list/detail views, config forms, charts). No React codegen, no Retool-style manual wiring. Change the spec, the UI updates.

I built C++ board simulators (ESP32 DevKitC and STM32 NUCLEO-F411RE) to show what this looks like for firmware folks who expose HTTP on the device (or on a Pi gateway in front of it).

The setup (two UIs, one API)

C++ simulator (visual demo)          UIGen admin UI (from openapi.yaml)
http://localhost:8080  (ESP32)       http://localhost:4400
http://localhost:8081  (STM32)       http://localhost:4401
       |                                       |
       +----------- same REST API -------------+
  • :8080 / :8081 — interactive board diagram, live GPIO/sensor cards, event log (hand-rolled HTML/JS for the demo)
  • :4400 / :4401 — generated control panel: board status, pin CRUD, sensor list, telemetry table + line chart, config forms, blink/reset actions

Both hit the same endpoints: /api/v1/pins, /api/v1/sensors, /api/v1/readings, /api/v1/config, etc.

The device (or simulator) serves JSON + optionally GET /openapi.yaml. The admin UI runs on your laptop on the LAN, so you are not baking a full frontend into flash.

How the OpenAPI spec was written (important)

The openapi.yaml was not auto-generated from C++. There is no reflection in httplib/ESP-IDF to do that cleanly.

We used contract-first design (same idea as a shared header file for a wire protocol):

  1. Model device resources: board, pins, sensors, readings, config, actions
  2. Define JSON schemas that match what firmware will actually emit
  3. Write openapi.yaml as the canonical contract
  4. Implement C++ handlers to match (Pin, Reading, BoardConfig structs mirror the schemas)
  5. Serve the same file at GET /openapi.yaml

If you already have curl output or C struct definitions, you do not need to start from a blank YAML file. The repo includes an AI agent skill (generate-device-openapi) that walks through drafting openapi.yaml from route tables, Postman exports, sample JSON, or struct headers. Then a second skill (auto-annotate) writes .uigen/config.yaml (labels, charts, layout, ignore rules).

Pipeline:

generate-device-openapi  →  openapi.yaml
auto-annotate            →  .uigen/config.yaml
uigen serve openapi.yaml --proxy-base http://<device-ip>:8080

Skills live in the repo under SKILLS/ and are copied into each example at UI/.agents/skills/ for Cursor / Copilot-style assistants.

What UIGen actually generates from the spec

From standard REST patterns in OpenAPI:

Your API Generated UI
GET /pins → array List + table
GET /pins/{id}, PUT /pins/{id} Detail + edit form
GET/PUT /config Settings form
POST /actions/blink Action form with validated body
GET /readings?sensor_id=&limit= List + line chart

Annotations in .uigen/config.yaml (not in the spec itself) drive the polish:

  • x-uigen-chart on the readings list response: xAxis: recorded_at, yAxis: value, server query.limit: 500, LTTB downsampling to ~120 points, sensor filter via x-uigen-ref to the sensors resource
  • x-uigen-ref: sensor_id and pin_id show human names instead of raw integers
  • x-uigen-ignore: hide /health, /openapi.yaml, and the visual-only /api/v1/state snapshot from the sidebar
  • Layout: sidebar app shell; centered forms for actions like “Blink LED”

Chart filters refetch the list endpoint with query params your firmware already supports (sensor_id, limit). Client-side time window presets (1m, 5m, 1h, etc.) trim the x-axis for dense telemetry without extra API work.

Why this vs RainMaker / ThingsBoard / Node-RED

  • No platform lock-in - your REST API stays yours; UIGen is a UI layer
  • Spec is the product contract - firmware, docs, and UI stay aligned
  • Works offline on LAN - uigen serve proxies to http://192.168.4.1 (typical AP mode); no cloud account required for the demo

Tradeoff: you need a decent OpenAPI file. That is what the skill is for.

Try it

ESP32 DevKitC (examples/apps/cpp/esp32-simulator):

git clone https://github.com/darula-hpp/uigen.git
cd uigen/examples/apps/cpp/esp32-simulator

# Terminal 1: simulator (Docker or local build)
docker compose up --build
# → http://localhost:8080

# In another terminal — run UIGen from UI/ so .uigen/config.yaml is picked up
cd UI
npx @uigen-dev/cli@latest serve openapi.yaml --proxy-base http://localhost:8080
# → http://localhost:4400

STM32 NUCLEO-F411RE (examples/apps/cpp/stm32-nucleo-simulator):

cd uigen/examples/apps/cpp/stm32-nucleo-simulator

# Terminal 1: simulator
docker compose up --build
# → http://localhost:8081

# In another terminal
cd UI
npx @uigen-dev/cli@latest serve openapi.yaml --proxy-base http://localhost:8081 --port 4401
# → http://localhost:4401

Example paths in repo: openapi.yaml, UI/.uigen/config.yaml, C++ routes in include/api_routes.hpp.

Roadmap: same spec, phone app (React Native)

We are working on a React Native target for the same OpenAPI → UI pipeline.

Plain language: today UIGen renders a web admin panel in the browser. The RN target will render a native iOS/Android app from the same spec and config - pin toggles, config screens, telemetry charts talking to your device over WiFi on the bench or in the field.

Think “companion app for technicians” without maintaining a separate Swift/Kotlin codebase. One openapi.yaml, web console for desk work, mobile app for walk-up commissioning. Still early; the C++ simulators are the reference implementation for now.

Links:

Happy building, Id appreciate feedback or suggestions

u/Prestigious-Bee2093 — 1 day ago
▲ 17 r/IOT+1 crossposts

ESP32 device exposes OpenAPI - instant admin UI (forms, GPIO control, telemetry charts) without writing a dashboard

I have been working on UIGen: a runtime that turns an OpenAPI spec into a full admin UI (sidebar nav, list/detail views, config forms, charts). No React codegen, no Retool-style manual wiring. Change the spec, the UI updates.

I built an ESP32 board simulator to show what this looks like for firmware folks who expose HTTP on the device (or on a Pi gateway in front of it).

The setup (two UIs, one API)

C++ simulator (visual demo)          UIGen admin UI (from openapi.yaml)
http://localhost:8080                http://localhost:4400
       |                                       |
       +----------- same REST API -------------+
  • :8080 — interactive DevKitC diagram, live GPIO/sensor cards, event log (hand-rolled HTML/JS for the “wow” demo)
  • :4400 — generated control panel: board status, pin CRUD, sensor list, telemetry table + line chart, config forms, blink/reset actions

Both hit the same endpoints: /api/v1/pins, /api/v1/sensors, /api/v1/readings, /api/v1/config, etc.

How the OpenAPI spec was written (important)

The openapi.yaml was not auto-generated from C++. There is no reflection in httplib/ESP-IDF to do that cleanly.

We used contract-first design (same idea as a shared header file for a wire protocol):

  1. Model device resources: board, pins, sensors, readings, config, actions
  2. Define JSON schemas that match what firmware will actually emit
  3. Write openapi.yaml as the canonical contract
  4. Implement C++ handlers to match (Pin, Reading, BoardConfig structs mirror the schemas)
  5. Serve the same file at GET /openapi.yaml

If you already have curl output or C struct definitions, you do not need to start from a blank YAML file. The repo includes an AI agent skill (generate-device-openapi) that walks through drafting openapi.yaml from route tables, Postman exports, sample JSON, or struct headers. Then a second skill (auto-annotate) writes .uigen/config.yaml (labels, charts, layout, ignore rules).

Pipeline:

generate-device-openapi  →  openapi.yaml
auto-annotate            →  .uigen/config.yaml
uigen serve openapi.yaml --proxy-base http://<device-ip>:8080

Skills live in the repo under SKILLS/ and are copied into the example at examples/apps/cpp/esp32-simulator/UI/.agents/skills/ for Cursor / Copilot-style assistants.

What UIGen actually generates from the spec

From standard REST patterns in OpenAPI:

Your API Generated UI
GET /pins → array List + table
GET /pins/{id}, PUT /pins/{id} Detail + edit form
GET/PUT /config Settings form
POST /actions/blink Action form with validated body
GET /readings?sensor_id=&limit= List + line chart

Annotations in .uigen/config.yaml (not in the spec itself) drive the polish:

  • x-uigen-chart on the readings list response: xAxis: recorded_at, yAxis: value, server query.limit: 500, LTTB downsampling to ~120 points, sensor filter via x-uigen-ref to the sensors resource
  • x-uigen-ref: sensor_id and pin_id show human names instead of raw integers
  • x-uigen-ignore: hide /health, /openapi.yaml, and the visual-only /api/v1/state snapshot from the sidebar
  • Layout: sidebar app shell; centered forms for actions like “Blink LED”

Chart filters refetch the list endpoint with query params your firmware already supports (sensor_id, limit). Client-side time window presets (1m, 5m, 1h, etc.) trim the x-axis for dense telemetry without extra API work.

Why this vs RainMaker / ThingsBoard / Node-RED

  • No platform lock-in - your REST API stays yours; UIGen is a UI layer
  • Spec is the product contract - firmware, docs, and UI stay aligned
  • Works offline on LAN - uigen serve proxies to http://192.168.4.1 (typical AP mode); no cloud account required for the demo

Tradeoff: you need a decent OpenAPI file. That is what the skill is for.

Try it

git clone https://github.com/darula-hpp/uigen.git
cd uigen/examples/apps/cpp/esp32-simulator

# Terminal 1: simulator (Docker or local build)
docker compose up --build
# → http://localhost:8080

# In another terminal — run UIGen from UI/ so .uigen/config.yaml is picked up
npx @uigen-dev/cli@latest serve openapi.yaml --proxy-base http://localhost:8080
# → http://localhost:4400

Example paths in repo: openapi.yaml, UI/.uigen/config.yaml, C++ routes in include/api_routes.hpp.

Roadmap: same spec, phone app (React Native)

We are working on a React Native target for the same OpenAPI → UI pipeline.

Plain language: today UIGen renders a web admin panel in the browser. The RN target will render a native iOS/Android app from the same spec and config - pin toggles, config screens, telemetry charts talking to your device over WiFi on the bench or in the field.

Think “companion app for technicians” without maintaining a separate Swift/Kotlin codebase. One openapi.yaml, web console for desk work, mobile app for walk-up commissioning. Still early; the ESP32 web demo is the reference implementation for now.

Links:

Happy hacking, Id appreciate feedback or suggestions

u/Prestigious-Bee2093 — 21 hours ago
▲ 0 r/vuejs+1 crossposts

Schema-driven backend runtime - serves REST + OpenAPI for your Vue app

Building a Vue or Nuxt app usually means writing the frontend and maintaining a backend that stays in sync: CRUD routes, validation, OpenAPI docs, and typed client code on the Vue side. That sync work adds up fast.

FastBackend is an open-source backend runtime (not a codegen tool). Your database schema is the source of truth. The CLI compiles it to a framework-agnostic IR, the runtime serves REST routes in memory at startup, and OpenAPI is written alongside for your Vue app. You write custom backend code only for business logic.

This post is for Vue devs who want a stable OpenAPI contract and typed clients without maintaining both sides by hand.

The Vue-side problem

Typical full-stack Vue flow:

  1. Backend team (or you) defines models
  2. Someone writes CRUD routes
  3. OpenAPI drifts out of date
  4. You hand-write fetch calls or types in Vue
  5. Schema changes → repeat

FastBackend collapses the backend side (steps 2-3) and gives you a clean OpenAPI file for the Vue side (step 4).

Backend in 4 commands

npm install -g @fastbackend/cli

git clone https://github.com/darula-hpp/fastbackend.git
cd fastbackend/examples/sqlalchemy-fastapi

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

cp .env.example .env
fastbackend generate
fastbackend dev

API runs at http://localhost:8301/ — Swagger docs at http://localhost:8301/docs

Wire it into Vue with Orval

After fastbackend generate, you have .fastbackend/openapi.yaml.

Generate typed API clients for Vue:

npm install -D orval

orval.config.ts:

import { defineConfig } from 'orval';

export default defineConfig({
  fastbackend: {
    input: '../backend/.fastbackend/openapi.yaml',
    output: {
      target: './src/api/generated.ts',
      client: 'vue-query',
      mode: 'tags-split',
    },
  },
});
npx orval

Use in a Vue component:

<script setup lang="ts">
import { useGetUsers } from '@/api/users';

const { data: users, isLoading } = useGetUsers();
</script>

<template>
  <ul v-if="users">
    <li v-for="user in users" :key="user.id">{{ user.name }}</li>
  </ul>
  <p v-else-if="isLoading">Loading...</p>
</template>

Orval also supports plain axios or fetch clients if you prefer composables without TanStack Query — same OpenAPI input.

Nuxt

Same OpenAPI file works with Nuxt:

  • Generate clients with Orval into ~/api/
  • Call from useAsyncData / useFetch in pages or server routes
  • Proxy API calls in nuxt.config during dev if needed

The backend does not need to know about Nuxt. OpenAPI is the contract.

How the backend side works

Schema (SQLAlchemy or Prisma)
        ↓
   fastbackend generate
        ↓
.fastbackend/ir.json + openapi.yaml   ← Vue reads this
        ↓
   fastbackend dev
        ↓
   REST API + /docs

The IR is framework-agnostic on the backend (FastAPI or Express adapters today).

OpenAPI is framework-agnostic on the frontend — Vue, Nuxt, React, Svelte all consume the same spec.

Custom backend logic (when CRUD is not enough)

Overrides and custom routes for the 10% that is actually custom:

# app/custom/users_override.py
from fastapi import APIRouter
from fastbackend_fastapi import override

router = APIRouter()

@override("/users/{id}", "get")
@router.get("/users/{user_id}")
async def custom_get_user(user_id: int):
    return {"id": user_id, "name": "Override User", "source": "custom-override"}

Regenerate OpenAPI after backend changes, re-run Orval, Vue types stay in sync.

What's supported today

Adapter Stack Schema
FastAPI Python SQLAlchemy, Prisma
Express Node/TS Prisma

Published:

  • npm: @fastbackend/cli, @fastbackend/core, @fastbackend/express
  • PyPI: fastbackend-fastapi

Limits

  • FastBackend is the backend — it does not generate Vue components
  • FastAPI adapter uses an in-memory store for the MVP (data resets on restart). Shipping soon
  • Express + Prisma is more production-shaped
  • Self-hosted OSS, not a hosted BaaS like Supabase

Where this is going (not shipped yet)

Declarative config for storage, OAuth, etc. — provider + URLs in fastbackend.yaml, secrets in .env. Same idea: less boilerplate, custom code when you need it.

Links

Happy coding, And would love any questions.

u/Prestigious-Bee2093 — 3 days ago

UIGen Update: Override System and Payment Integration

Hey everyone, a few days ago I shared an update UIGen - a CLI that turns your OpenAPI spec into a full React App at runtime. It was regarding OAuth Support and I continued to iterate.

Recently added override support and payment integration. (The profile page in the demo was a complete override)

What's New

x-uigen-override annotation

Replace any view with custom React components. The runtime handles discovery, transpilation, and injection.

Component Mode - Full control:

// src/overrides/profile-custom.tsx
import type { OverrideDefinition } from '@uigen-dev/react';

function CustomProfile() {
  const [data, setData] = useState(null);
  
  useEffect(() => {
    fetch('/api/v1/auth/me').then(res => res.json()).then(setData);
  }, []);
  
  return <div>Custom Profile View</div>;
}

const override: OverrideDefinition = {
  targetId: 'me',
  component: CustomProfile,
};

export default override;

Render Mode - UIGen fetches, you render:

// src/overrides/users-list.tsx
import type { OverrideDefinition, ListRenderProps } from '@uigen-dev/react';

const override: OverrideDefinition = {
  targetId: 'users.list',
  render: ({ data, isLoading }: ListRenderProps) => {
    if (isLoading) return <div>Loading...</div>;
    return <div className="grid">{/* custom UI */}</div>;
  },
};

export default override;

UseHooks Mode - Side effects only:

// src/overrides/analytics.tsx
import { useEffect } from 'react';
import type { OverrideDefinition } from '@uigen-dev/react';

const override: OverrideDefinition = {
  targetId: 'users.list',
  useHooks: ({ resource }) => {
    useEffect(() => {
      analytics.track('page_view', { resource: resource.name });
    }, [resource]);
  },
};

export default override;

Configure in .uigen/config.yaml:

annotations:
  GET:/api/v1/auth/me:
    x-uigen-override:
      id: me

x-uigen-payments annotation

Add payment processing declaratively. Supports Stripe, PayPal, Square. (Tested e2e with Stripe though)

x-uigen-payments:
  providers:
    - provider: stripe
      publishableKey: ${STRIPE_PUBLISHABLE_KEY}
      mode: test
      currency: usd
  pricingPage:
    enabled: true
    source: inline
    products:
      - id: free
        name: Free
        type: subscription
        price: 0
        interval: month
        features:
          - Up to 10 meetings per month
      - id: pro-monthly
        name: Professional
        type: subscription
        price: 2900
        interval: month
        highlighted: true
        features:
          - Unlimited meetings
          - Priority support

Generates pricing page at /pricing with responsive table, feature comparison, and payment buttons.

Payment Gates - Mark resources as monetized:

paths:
  /api/v1/meetings:
    x-uigen-monetized: true
    post:
      summary: Create meeting

Backend enforces limits and returns 402 when exceeded. Frontend intercepts 402 and shows upgrade prompt automatically.

@router.post("/api/v1/meetings")
async def create_meeting(user: User = Depends(get_current_user)):
    if user.plan == "free":
        meeting_count = await get_user_meeting_count(user.id)
        if meeting_count >= 10:
            raise HTTPException(status_code=402, detail="Upgrade to create more meetings")
    return await create_meeting(user, meeting)

Implementation Notes

Override System:

  • File-based discovery from src/overrides/
  • TypeScript with full type safety
  • Works with any view type
  • No build step, dynamic imports on your overrides

Payment Integration:

  • Frontend-safe keys only (publishableKey exposed, secrets in .env)
  • Backend enforces limits
  • Webhook support for subscription events
  • Environment variable resolution with ${VAR_NAME} syntax

Try It

The Meeting Minutes example demonstrates both features.

git clone https://github.com/darula-hpp/uigen
cd uigen/examples/apps/fastapi/meeting-minutes

# Setup backend
docker compose up -d

# Add credentials to .env
echo "STRIPE_PUBLISHABLE_KEY=pk_test_..." >> .env
echo "GOOGLE_CLIENT_ID=your-client-id" >> .env

# Run
npx @uigen-dev/cli serve openapi.yaml --proxy-base http://localhost:8000

The OAuth flow works end-to-end. Custom profile override shows component mode. Payment gates trigger on meeting creation. Pricing page is auto-generated.

Repo: https://github.com/darula-hpp/uigen
Docs: https://uigen-docs.vercel.app

Would be great to get your feedback. Seems like a "long" way to v1

u/Prestigious-Bee2093 — 5 days ago

A runtime React renderer that generates production UIs from OpenAPI specs (with full override control)

Hey r/reactjs! I've been working on UIGen, a tool that renders complete React applications from OpenAPI specs at runtime. Got some great feedback on HN and wanted to share it here since the React community might find the approach interesting.

What it does

Instead of generating code, UIGen interprets your OpenAPI spec at runtime and renders a complete React SPA with:

  • CRUD views (tables, forms, detail pages)
  • Authentication flows (OAuth 2.0, Bearer tokens, API keys)
  • File uploads with previews
  • Relationship navigation
  • Search and filtering
  • Dark mode

The key difference: No code generation. Your API changes, the UI updates automatically. No regeneration step, no drift.

Quick example

npx @uigen-dev/cli@latest init my-app
cd my-app
npx @uigen-dev/cli@latest serve openapi.yaml

Visit localhost:4400 and you have a working admin panel.

The override system (the React part you'll care about)

This is where it gets interesting for React developers. You can override any auto-generated view with custom React components at three levels:

1. Component Mode - Full control:

import type { OverrideDefinition } from '@uigen-dev/react';

function CustomProfile() {
  return <div>My Custom Profile View</div>;
}

const override: OverrideDefinition = {
  targetId: 'me',
  component: CustomProfile,
};

export default override;

2. Render Mode - UIGen fetches data, you control rendering:

const override: OverrideDefinition = {
  targetId: 'users.list',
  render: ({ data, isLoading }: ListRenderProps) => {
    if (isLoading) return <div>Loading...</div>;
    return <div className="grid">{/* your custom UI */}</div>;
  },
};

3. Hooks Mode - Side effects only (analytics, etc.):

const override: OverrideDefinition = {
  targetId: 'users.list',
  useHooks: ({ resource }) => {
    useEffect(() => {
      analytics.track('page_view', { resource: resource.name });
    }, [resource]);
  },
};

The CLI automatically discovers, transpiles, and injects your overrides. You get 80% auto-generated, customize the 20% that matters.

How it works

UIGen parses your OpenAPI spec into a framework-agnostic Intermediate Representation (IR), then the React renderer interprets it at runtime. The IR is decoupled from React, so we're working on Svelte and Vue renderers too.

OpenAPI Spec → Reconciler → Adapter → IR → React Renderer → SPA

AI-first configuration

Includes AI agent skills that work with Cursor, Windsurf, etc.:

  • Auto-annotate: Detects auth endpoints, relationships, file uploads
  • Configure OAuth: Sets up social login
  • Apply styles: Generates themes

Just tell your AI: "Use the auto-annotate skill" and it configures everything.

Try the example

git clone https://github.com/darula-hpp/uigen
cd uigen/examples/apps/fastapi/meeting-minutes
docker compose up -d
npx @uigen-dev/cli@latest init --spec openapi.yaml
npx @uigen-dev/cli@latest serve openapi.yaml --proxy-base http://localhost:8000

Full meeting minutes app with auth, CRUD, file uploads, and relationships.

Links

Would love to hear what the React community thinks. Curretly working on polish and Stripe Integration

Contributions welcome!

reddit.com
u/Prestigious-Bee2093 — 9 days ago
▲ 20 r/FastAPI+1 crossposts

UIGen Update: OAuth 2.0 authentication support & Env Vars

Hey everyone, a few weeks ago I shared UIGen - a CLI that turns your OpenAPI spec into a full React App at runtime. I've been iterating based on feedback and adding features that make it useful for real worl apps.

Recently added OAuth support and environment variable resolution based on feedback.

What's New

x-uigen-auth annotation

Add OAuth authentication to your app declaratively. Supports Google, GitHub, Facebook, and Microsoft. The runtime handles the complete OAuth flow - authorization, token exchange, refresh, and session management.

info:
x-uigen-auth:
providers:
- provider: google
clientId: ${GOOGLE_CLIENT_ID}
redirectUri: ${GOOGLE_REDIRECT_URI}
scopes:
- openid
- email
- profile

Environment variable resolution

Reference environment variables in your config using ${VAR_NAME} syntax. UIGen loads .env files from your spec directory and resolves variables at build time. Supports default values with ${VAR_NAME:default}.

x-uigen-auth:
providers:
- provider: google
clientId: ${GOOGLE_CLIENT_ID}
redirectUri: ${GOOGLE_REDIRECT_URI:http://localhost:8000/callback}

Try It

The Meeting Minutes example demonstrates OAuth with Google. Set up your OAuth credentials, add them to .env, and UIGen handles the rest.

# .env file
GOOGLE_CLIENT_ID=your-client-id
GOOGLE_REDIRECT_URI=http://localhost:8000/api/v1/auth/google/callback

# Run the app
npx @uigen-dev/cli serve openapi.yaml --proxy-base http://localhost:8000

The OAuth flow works end-to-end - click "Sign in with Google", authorize, and you're redirected back with a valid session. Token refresh happens automatically on 401 responses.

Implementation Notes

  • OAuth tokens are managed client-side with automatic refresh
  • CSRF protection via state parameter validation
  • Session validation endpoint support for cookie-based auth fallback
  • Environment variables are resolved server-side before the app starts

Repo: https://github.com/darula-hpp/uigen
Docs: https://uigen-docs.vercel.app

Feedback welcome.

u/Prestigious-Bee2093 — 11 days ago
▲ 0 r/react+1 crossposts

I built a compiler that transforms a domain-specific language (DSL) into live web applications. The DSL extends OpenAPI/Swagger with custom annotations (x-uigen-*) for UI semantics. Unlike code generators, this compiles at runtime and interprets a framework-agnostic IR.

The DSL

OpenAPI describes APIs but lacks UI semantics. We extended it with x-uigen-* annotations:

paths:
  /api/users/{id}:
    get:
      x-uigen-profile: true        # Render as profile view
      x-uigen-layout:
        strategy: centered
  /api/analytics:
    get:
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  data:
                    type: array
                    x-uigen-chart:       # Visualize as chart
                      chartType: line
                      xAxis: date
                      yAxis: revenue

The annotation system is extensible via a handler registry. Each handler implements extract/validate/apply for its annotation type.

Architecture

Pipeline stages:

  1. Lexing/Parsing: YAML/JSON → document object (js-yaml)
  2. Reconciliation: Merges user config annotations into spec (non-destructive, in-memory)
  3. Annotation Processing: Registry executes handlers in priority order (ignore → login → label → others)
  4. IR Generation: Annotated spec → framework-agnostic intermediate representation
  5. Rendering: IR → React components (Svelte/Vue planned)

Key IR structure:

interface UIGenApp {
  meta: AppMeta;
  resources: Resource[];      // Inferred from paths
  auth: AuthConfig;            // From securitySchemes
  dashboard: DashboardConfig;
  servers: ServerConfig[];
}

interface Resource {
  name: string;
  operations: Operation[];
  schema: SchemaNode;          // Recursive tree
  relationships: Relationship[];
  pagination?: PaginationHint;
}

Design patterns used

  • Adapter: OpenAPI3Adapter, Swagger2Adapter normalize different spec versions
  • Visitor: Schema traversal (TypeMappingVisitor, ValidationExtractionVisitor, ReferenceResolutionVisitor)
  • Factory: SchemaNodeFactory for creating IR nodes
  • Strategy: Authentication detection, view classification
  • Facade: OpenAPI3Adapter orchestrates 7+ specialized processors
  • Registry: Component lookup, annotation handlers

Interesting challenges

1. DSL design: Extending OpenAPI without breaking it

OpenAPI allows vendor extensions (x-* prefix). We use this for UI annotations while keeping specs valid:

  • x-uigen-chart: Data visualization config
  • x-uigen-profile: Profile view semantics
  • x-uigen-landing-page: Landing page sections (hero, features, pricing, etc.)
  • x-uigen-layout: Layout strategies (sidebar, centered, dashboard grid)
  • x-uigen-label: Human-readable field labels
  • x-uigen-ignore: Exclude endpoints from UI

Each annotation has a handler with metadata (target type, parameter schema, examples). The registry processes them in priority order to handle dependencies.

2. Reference resolution

OpenAPI specs use $ref pointers extensively. The SchemaResolver handles:

  • Local refs (#/components/schemas/User)
  • Circular references (User → Address → User)
  • Deep nesting with caching

3. Resource inference

No explicit resource definitions in OpenAPI. The ResourceExtractor infers them by:

  • Grouping paths by common prefixes
  • Detecting CRUD patterns (GET/POST/PUT/DELETE)
  • Identifying relationships via path parameters and response schemas

4. Config reconciliation

Users can override spec annotations via .uigen/config.yaml. The reconciler:

  • Parses element paths (POST:/api/users, User.email)
  • Deep clones the spec (never mutates source)
  • Merges annotations with config precedence
  • Validates output spec integrity

Property-based testing with fast-check verifies 20+ correctness properties (idempotence, determinism, precedence, etc.) across 100+ iterations each.

5. View classification

Operations are classified into view types (list, detail, create, update, delete, search, wizard, action) based on:

  • HTTP method
  • Path structure
  • Request/response schemas
  • Field count (>8 fields → wizard)

Runtime vs AOT

This is a runtime compiler. The IR is injected as window.__UIGEN_CONFIG__ and interpreted by the renderer. Benefits:

  • Zero maintenance: UI stays in sync with API changes
  • No build step for end users
  • Framework-agnostic IR enables renderer swapping

Tradeoff: No compile-time optimizations, but the IR is small (~100KB for typical APIs) and parsing is fast (<50ms).

Validation

  • Unit tests for each processor
  • Property-based tests for reconciler (idempotence, commutativity, etc.)
  • Integration tests with real OpenAPI specs (Stripe, Twilio, Petstore)
  • E2E tests for generated UIs

Code

GitHub: https://github.com/darula-hpp/uigen Architecture: https://uigen-docs.vercel.app/blog/uigen-architecture

Monorepo structure:

  • packages/core: Adapters, IR, processors (framework-agnostic)
  • packages/react: React renderer
  • packages/cli: Commander.js CLI with Vite dev server

Questions

  1. Is this actually a compiler? It has lexing, parsing, IR, and code generation, but the "code" is React components rendered at runtime.

  2. Does extending OpenAPI with x-uigen-* annotations make it a DSL, or is it still just OpenAPI with metadata? The annotations have semantic meaning (chart config, layout strategies) beyond API description.

  3. Better approaches for resource inference? Current heuristics work for REST APIs but struggle with RPC-style specs.

  4. IR optimization opportunities? Currently no dead code elimination or constant folding since the IR is declarative.

Feedback welcome.

u/Prestigious-Bee2093 — 16 days ago
▲ 13 r/django+2 crossposts

Hey everyone, a few days ago I shared UIGen - a CLI that turns your OpenAPI spec into a full React frontend at runtime. I've been iterating based on feedback and added some features that make it more practical for real apps.

What's New

x-uigen-landing-page annotation Add a landing page to your app declaratively. Works like other annotations - add it to your spec, and the landing page renders at runtime. You can use the AI skill to generate content based on your API spec, or customize it manually.

The existing styling skill applies to landing pages too, so your theme stays consistent.

x-uigen-layout annotation Control your app layout declaratively. Choose between sidebar, centered, or dashboard grid layouts without writing layout code. Specify it in your spec, and the runtime handles the rest.

Try It

The Meeting Minutes example app in the repo demonstrates both features. Clone it, run the FastAPI backend with Docker, and point UIGen at the generated spec.

git clone https://github.com/darula-hpp/uigen
cd examples/apps/fastapi/meeting-minutes
docker-compose up -d
docker-compose exec app alembic upgrade head
npx u/uigen-dev/cli serve openapi.yaml --proxy-base http://localhost:8000

What I'm Working On

  • Declarative WebSocket support.
  • Declarative OAuth integration (Google, GitHub, etc.)
  • More layout options
  • More polish

The goal is to keep adding capabilities you can configure through annotations, so you can build complete apps without forking or writing frontend code.

Repo: https://github.com/darula-hpp/uigen
Docs: https://uigen-docs.vercel.app

Feedback welcome.

u/Prestigious-Bee2093 — 16 days ago

Hey r/backend,

I've built UIGen - a CLI tool that turns any OpenAPI/Swagger spec into a complete, interactive React admin UI at runtime. No code generation, no frontend maintenance.

The Problem

As backend developers, we waste too much time building CRUD UIs, forms, auth flows, and internal tools even when we already have a solid OpenAPI spec.

Quick Start

npx @uigen-dev/cli@latest init my-admin
cd my-admin
npx @uigen-dev/cli@latest serve openapi.yaml

Open http://localhost:4400 and you get a full-featured admin panel.

What It Generates

  • Full CRUD (list, create, edit, detail)
  • Auth flows (Bearer, API Key, Basic, credential login)
  • Relationships (foreign keys → dropdowns/links)
  • File uploads, pagination, search & filters
  • Charts (via x-uigen-chart)

Biggest advantage: Runtime rendering. Change your API → restart the dev server → UI updates automatically. Your OpenAPI spec stays the source of truth.

AI Agent Skills

I added markdown-based skills you can give to Cursor/Claude/etc.:

  • Auto-annotate - detects login endpoints, relationships, file uploads, etc.
  • Auto-style — generates nice Tailwind CSS (e.g. "make it look like Stripe" or "dark modern theme").

This dramatically reduces manual configuration.

Real Example

I dogfooded it with a FastAPI + PostgreSQL meeting minutes app. The entire frontend is zero custom code — just run serve.

When It Makes Sense

Great for:

  • Internal tools & admin panels
  • API prototyping / MVPs
  • Microservice dashboards
  • Backend teams that want to ship faster

Not ideal for:

  • Customer-facing apps with complex UX
  • Highly custom design requirements

Current Status

⚠️ Early stage (not production-ready yet), but actively developed and approaching v1. Works with OpenAPI 3.x and Swagger 2.0.

Links

Happy to hear what you think

u/Prestigious-Bee2093 — 19 days ago

Hey everyone - UIGen just got way easier to try (Faster AI assisted config and theming)

A few days ago I shared UIGen: a CLI tool that turns your OpenAPI spec into a full React frontend at runtime - no code generation, no build step. Update your FastAPI backend → restart the CLI → UI updates instantly. Your spec stays the source of truth.

What UIGen gives you out of the box

  • Auto CRUD views (list, detail, create, edit)
  • Full auth flows (login, signup, password reset)
  • File uploads, relationships, pagination, search
  • Data visualizations (new x-uigen-chart)
  • Production-ready React output you can extend/customize

The big new upgrade: AI-assisted setup

I added two AI skills that understand UIGen's architecture and do the heavy lifting:

1. Auto-Annotate Skill

Feeds your spec to your AI assistant (Cursor, Claude, etc.) and it automatically:

  • Detects & marks login/signup endpoints (x-uigen-login etc)
  • Finds relationships (users → posts, orders → customers)
  • Identifies file uploads
  • Spots good candidates for charts

2. Auto-Style Skill

Generates beautiful, production-ready CSS with simple prompts like:

"Make it look like Material Design" or "Dark theme with blue accents"

No manual CSS files or build processes - styles inject at runtime.

New super simple workflow

  1. @uigen-dev/cli init → bootstraps your project
  2. Let AI auto-annotate your spec (or use the visual config GUI)
  3. Let AI generate your theme
  4. Run npx @uigen-dev/cli serve yourspec.yaml and iterate

No frontend code to maintain.

Real example you can try right now

There's a full Meeting Minutes app in the repo (FastAPI + async SQLAlchemy + PostgreSQL + Alembic). ~2,000 lines of clean Python. Just clone, spin up the backend with Docker, and point UIGen at the auto-generated OpenAPI spec. Full CRUD, auth, file uploads, relationships — all working.

Current priorities for v1

  • Charting polish
  • Better layout options (without bloat)
  • App branding (name, icon, etc.)
  • Theme registry for instant beautiful starts

The goal

You focus on building a great API. UIGen + AI handles the frontend UI so you can ship faster.

Links

Happy to hear what you think

u/Prestigious-Bee2093 — 20 days ago

Hey everyone, a few days back I shared updates on UIGen - a cli tool that creates a frontend app from your api spec at runtime.

Since then, I've been focused on making onboarding frictionless and eliminating the need to manually configure or touch frontend code. Here's what's new:

AI Skills for Zero-Config Setup

I created two AI skills that understand uigen architecture and can configure everything automatically:

Auto-Annotate Skill

It has context of available extensions and how they are used so it detects patterns in your spec and applies annotations automatically:

  • Identifies login endpoints and marks them with x-uigen-login
  • Discovers relationships between resources (users → posts, orders → customers)
  • Detects file upload endpoints
  • Finds chart-worthy data

Auto-Style Skill

Understands the React SPA and its base styles so can generate production-ready CSS using your favorite coding model and IDE with prompts like:

  • "Make it look like Material Design"
  • "Use a dark theme with blue accents"

No manual CSS and build process - styles are injected at runtime.

The Workflow is Now:

  1. Run uigen init to bootstrap your project
  2. Let AI auto-annotate your spec (or use the config GUI)
  3. Let AI generate your theme (or write custom CSS)
  4. Iterate and see if you are happy

The runtime renderer handles the rest - no code generation, no build step. The theme is injected into the App at runtime.

Also Added:

  • uigen init command for project scaffolding
  • x-uigen-chart extension for data visualization (still polishing)

Current Priorities for v1:

  • Testing and polish
  • Charting improvements (x-uigen-chart)
  • App configuration (name, icon, branding)
  • Layout customization (exploring non-bloat approaches)
  • Maybe creating a theme register for themes so you can bootstrap your project with an existing theme

The Goal

You build the API, the runtime renderer creates the UI, AI helps you configure faster, standard React output.

Links

Looking forward to what you think!

u/Prestigious-Bee2093 — 20 days ago