Thanks to visit codestin.com
Credit goes to github.com

Skip to content

xylex-group/resource-framework

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

46 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Resource Framework

This package is the canonical implementation of the Resource Framework that powers Suitsbooks admin surfaces. It turns your Drizzle schema and snapshot metadata into:

  • Fully type-safe resource routes (defineDrizzleResourceRoute)
  • An opinionated table stack with filtering, search, and editing controls (ResourceTable)
  • Auto-generated create/edit dialogs and drilldown views (CreateResourceDialog, ResourceDrilldown)
  • Shared utilities for metadata lookups, filtering, CSV export, notifications, and retry-safe mutations

It ships both React UI primitives and non-UI helpers so the rest of the app can consume resources without copying infrastructure.

Contents

  1. Architecture overview
  2. Data flow & configuration
  3. Core exports
  4. Directories at a glance
  5. Components & UI building blocks
  6. Hooks
  7. Adapters & network helpers
  8. Registries & constructors
  9. Utilities
  10. Types & schema
  11. Resource Forms
  12. Testing
  13. Best practices & patterns

Architecture overview

Drizzle schema (drizzle/schema.ts)
             ↓
   drizzle-kit generate
             ↓
   meta/snapshot.json  ←─ runtime metadata for every table/column
             ↓
    types/drizzle-schema.ts ← compile-time typings generated by scripts
             ↓
       packages/resource-framework ← metadata-driven UI
  • meta/snapshot.json is the single source of truth for field types, defaults, enums, masks, etc.
  • Runtime utilities in utils/drizzle-editor.ts interpret that JSON and expose getDrizzleFieldType, getDrizzleColumnInfo, and type coercion helpers.
  • Type definitions in types/drizzle-schema.ts mirror the snapshot so route definitions and column builders stay type-safe.

Data flow & configuration

  1. Schema definition: declare tables using Drizzle ORM declarative methods (pgTable, text, numeric, etc.).
  2. Snapshot generation: run npx drizzle-kit generate after schema changes to update meta/ and types/.
  3. Define resource routes with defineDrizzleResourceRoute (includes table name, id column, default columns, drilldown config, edit/create flags).
  4. Register routes inside registries/resource-routes.ts, RESOURCE_ROUTES, and optionally RESOURCE_DRILLDOWN_ROUTES.
  5. Render UI using ResourceTable, ResourceDrilldown, CreateResourceDialog, and the helpers described below.

Routes optionally include:

  • create config (scopes, required fields, columns overrides)
  • drilldown config (section templates, widgets, actions, auto-hide flags)
  • icons, page_label, deferToHeader, enableSearch, force_external_api_updates, etc.
  • categories used by the UI to group tabs or sidebar entries (see utils/categories.ts)

Core exports

Most of the public API is re-exported from index.ts. Highlights:

Feature Export(s)
Resource routes defineDrizzleResourceRoute, defineDrizzleColumns, RESOURCE_ROUTES, getResourceRoute, resourceRoutes
UI primitives ResourceTable, ResourceDrilldown, CreateResourceDialog, CreateResourceButton
Drilldown helpers ResourceDrilldownSection, DrilldownSection, DrilldownSummary, SectionWidgetGroup, ResourceDrilldownNoEditFields, DrilldownActivity
Table controls DisplaySettings, TableAddButton, TableSearchInput, TablePaginationControls, TableFullscreenToggle, TableDownloadButton, TableDeleteDialog, TableTopControls, TableHeaderCell, TableBodyCell
Adapters fetchDataViaAthena, insertDataViaAthena, updateDataViaAthena, deleteDataViaAthena, uploadFileViaAthena, refreshFileUrlViaAthena, applyTransform
Utilities buildCategoryByKey, coerceByDatatype, buildTableColumns, insertRow, coerceValue, applyClientFilters, parseQueryFilters, parseQuerySort
Hooks useResourceContext, useResourceRoute, useTableConfiguration, useFetchData, etc.
Types ResourceRoute, ResourceFieldSpec, ColumnConfig, DrilldownSectionConfig, DrizzleTableName, DrizzleColumnValue, etc.
Resource forms defineResourceFormSchema, defineResourceForm, createResourceFormRows, resolveResourceFormRows, useResourceFormRuntime, EntityFormV2

Directories at a glance

adapters

  • athena-gateway.ts: typed CRUD adapter that routes reads and mutations through the Athena SDK and gateway.
  • athena-files.ts: file upload + signed URL refresh helpers that target Athena-hosted file endpoints.
  • transforms.ts: helpers for applying server-defined data.transforms onto fetch responses before they reach the UI.

components

  • ResourceProvider.tsx: context provider wiring user/org info, notifications, and routing metadata to downstream table and dialog components.
  • ResourceTable.tsx: table orchestrator that handles columns, pagination, search, filters, display settings, and drilldown navigation.
  • ResourceDrilldown.tsx: detail view that renders panels defined by ResourceDrilldownSection and optional widgets (SectionWidgetGroup). Supports DrilldownSummary, file explorer, and custom actions.
  • CreateResourceDialog.tsx / CreateResourceButton: type-safe create experience based on ResourceCreateConfig.
  • components/sections/*: building blocks for drilldown sections, widgets, summaries, and attachments.
  • components/table/*: portable table controls (search, add, delete, download, pagination, fullscreen) for consistent UI.
  • components/form-v2/EntityFormV2.tsx: shared form stack powering dialogs, drilldown actions, and card/plan selects.
  • components/form-v2/*: the shared resource-form renderer stack (EntityFormV2, FormFieldV2, review cards).
  • components/edit-state: helpers for inline edit indicators (AddField, toggle-edit, SaveAll).
  • components/cells: custom cell renderers (e.g., ScopeCell, AssigneesCell) used in table defaults.
  • components/drilldown: layout components (DrilldownLayout, DrilldownTable, DrilldownActivity) plus file explorer and action toolbar.

constructors

  • column-registry.tsx: centralized TanStack column builders (buildStatusColumn, buildMonthColumn, etc.), default editors, and sort/filter helpers.
  • define-columns.ts: defineColumns() converts high-level ResourceFieldSpec into BuiltColumnSpec while inferring editors/data sources from getDrizzleColumnInfo.
  • define-drizzle-resource-route.ts: validates route definitions, wires defaults, and keeps metadata in sync with the generated schema.

handlers

  • handle-options.ts: exports fetchOptions() for populating select editors, resolves query filters, and respects Cache-Control headers.
  • handle-update.ts: mutation handler that writes through the Athena adapter.
  • handle-csv-export.ts: produces CSV downloads from current query state, infers column types, and exposes handleDownloadCsv.

hooks

  • useResourceContext: tracks the active resourceName, route, and filters shared across UI pieces.
  • useResourceRoute: returns the typed route entry and registers side effects (breadcrumbs, header actions).
  • useResourceColumns: memoizes column definitions for the current route.
  • useTableConfiguration: manages saved layouts (column visibility, sort order, pagination state).
  • useFetchData: core data fetching with cacheEnabled/force_no_cache.
  • useUpdateData: mutation hook used by CreateResourceDialog, inline editors, and drilldown actions.
  • useAdvancedFilters, useQueryFilters: parse and apply URL filters (dork query style).
  • useApiClient: shared Axios instance configured with user headers plus Cache-Control.
  • useEventsStream: powers drilldown activity feeds or chat tables.
  • useKeyboardShortcut, useMobile: desktop shortcuts and responsive helpers.
  • useNotification: thin wrapper around the shared notification hook to show messages after fetch/update.
  • useUserPreferences, useUserScopes: surface stored UI preferences and permission scopes.

registries

  • resource-routes.ts: the authoritative RESOURCE_ROUTES, resourceRoutes, and getResourceRoute helper for every page that renders a table/drilldown. Populate this before rendering.
  • resource-drilldown-routes.ts: optional RESOURCE_DRILLDOWN_ROUTES to override section layouts, widgets, actions, and titles for drilldown pages.
  • filter-registry.tsx: default filter operator sets per datatype so the table filter UI stays consistent.

renderers

  • VideoRenderer.tsx: helper for embedding video previews in table cells or drilldown sections when a field indicates media content.

resource-types.ts

  • Declares every shared config shape (ResourceRoute, ResourceFieldSpec, ColumnConfig, DrilldownSectionConfig, ResourceFormField, etc.). Keep this in sync with constructor logic.

types

  • drizzle-schema.ts: generated typings derived from meta/snapshot.json (drizzle table/column names, types, optional metadata).
  • tanstack-table.d.ts: helper declarations to keep TanStack columns typed across the registry.

utils

  • categories.ts: helpers to derive sidebar/category tabs from route metadata.
  • coerce.ts: converts string inputs into typed values (numbers, booleans, enums).
  • client-filter.ts: client-side filtering helpers (applyClientFilters, coerceValue).
  • column-builder.ts: consumed by ResourceTable to build TanStack columns.
  • csv.ts: infers CSV value types so downloads use the same formatting.
  • dork-query.ts: parses filter/sort query strings (aka dork query) and syncs them with the URL.
  • drizzle-editor.ts: maps snapshot metadata to editor types (getDrizzleEditorType, getDrizzleFieldType, getDrizzleColumnInfo).
  • insert.ts: insertRow() applies defaults, resolves transforms, and posts via the adapter.
  • resource-forms.ts: validation, normalization, ordered-step helpers, builder DSL, and row-to-runtime resolution for persisted form contracts.
  • key-case.ts: normalizes camelCase/snake_case accessors used by query parsers.
  • query-parser.ts: helper for parseQueryFilters and parseQuerySort.
  • render-functions.ts: placeholder noop and other render helpers.
  • Additional helpers: buildCategoryByKey, coerceByDatatype, inferCsvTypes, applyTransform, parseDorkQuery, etc.

tests

  • Covers column builders, coercion helpers, filter parsing, templates, hooks, and utility functions.
  • Run pnpm test --filter resource-framework or use Vitest directly for this folder.

Components & UI building blocks

ResourceProvider

  • Wrap pages in ResourceProvider to supply ResourceContext. It reads RESOURCE_ROUTES, listens for header actions, and provides user, organization, and notification.
  • Re-export suppressHydrationWarning because some metadata is client-rendered.

ResourceTable

  • Handles data fetching, column resolution, search, filters, pagination, and drilldown linking.
  • Defaults to rendering:
    • TableTopControls: search input, download, add button, fullscreen toggle.
    • DisplaySettings: column visibility and reset controls.
    • TanStack Table built from the column registry.
    • TablePaginationInfo and TablePaginationControls.
  • Accepts resourceName, optional columns, filters, forceLoading, and tableConfigOverrides.
  • Supports deferToHeader so headers/new buttons can live in the global header.

ResourceDrilldown

  • Shows a record detail page configured via ResourceDrilldownSection arrays.
  • Supports:
    • Summary area (DrilldownSummary)
    • Section grids (1-4 columns)
    • Widget injection via SectionWidgetGroup
    • DrilldownActions, DrilldownActivity, File Explorer, and edit tooling
  • Sections can define widgets: [{ type: "json" }] and custom components must be registered via registerSectionWidget inside packages/resource-framework/components/sections/widgets.
  • Always use the uuid when building drilldown URLs (not row_id).

CreateResourceDialog & CreateResourceButton

  • Driven by ResourceCreateConfig defined per route.
  • Automatically infers editors (text, select, textarea, boolean) through the column registry and Drizzle metadata helpers.
  • Supports defaultValues, columns overrides (including editor, data_source, options), custom dialog components, and onSubmit.
  • Uses EntityFormV2 internally, sharing inputs with other flows like plan/card selectors and Stripe pay fields.

Form helpers

  • components/form/form-field.tsx and form-v2 components add consistent validation, react-error-boundary fallbacks, and accessibility patterns.
  • fields/select-data-source.tsx and handler/fetch-options.ts keep dropdowns driven by database tables and cache state via Cache-Control.
  • edit-state/* components show unsaved changes and inline editing states.

Hooks

Use the exported hooks to keep UI synchronized:

  • useResourceRoute / useResourceColumns for column metadata.
  • useTableConfiguration for column visibility, order, and sort state.
  • useFetchData with cacheEnabled controls; route entries may set force_no_cache.
  • useUpdateData honors force_external_api_updates and ensures notifications follow the workspace pattern (useNotification + notification(...)).
  • useAdvancedFilters / useQueryFilters parse filters from URLs and feed them into the table.
  • useKeyboardShortcut / useMobile support global shortcuts and responsive behavior.
  • useApiClient centralizes HTTP headers and base URLs.

Adapters & network helpers

  1. adapters/athena-gateway.ts is the primary data plane:
    • Reads and writes go through the typed Athena SDK (@xylex-group/athena).
    • Tenant/user headers are injected from framework context.
    • Responses are normalized so the rest of the framework can stay stable during migration.
  2. adapters/athena-files.ts handles file transfer endpoints hosted behind the Athena base URL:
    • uploadFileViaAthena() posts multipart form data to /api/upload.
    • refreshFileUrlViaAthena() refreshes signed file URLs via /api/files/refresh-url.
    • each call carries X-Request-Id; mutations also carry Idempotency-Key.
  3. handlers/handle-options.ts, handle-update.ts, and the core hooks call these adapters so package consumers do not have to manage transport details themselves.

Registries & constructors

  • Always register new resources in registries/resource-routes.ts (RESOURCE_ROUTES). Use getResourceRoute(resourceName) to resolve configuration at runtime.
  • Supplement or override drilldown layouts in registries/resource-drilldown-routes.ts.
  • Use defineDrizzleResourceRoute/defineColumns/column-registry so route metadata stays type-safe and reuses shared editors, formatters, and sort logic.
  • The filter-registry maps datatypes to operator sets. Use getFilterOptions() to feed the filter UI.

Utilities

Expose consistent helpers for:

  • Categories (buildCategoryByKey)
  • Type coercion (coerceByDatatype, coerceValue)
  • CSV exports (inferCsvTypes)
  • Column builders (buildTableColumns)
  • Query parsing/syncing (parseQueryFilters, parseQuerySort, parseDorkQuery, applyDorkQueryToUrl)
  • Drizzle metadata (getDrizzleEditorType, getDrizzleFieldType, getDrizzleColumnInfo)
  • Insert helpers (insertRow)
  • Client-side filtering (applyClientFilters)
  • Key casing (toCamelCase, toSnakeCase, getValueByKeyCase, getValueByPathCase)

Types & schema

  • resource-types.ts defines shared config shapes (ResourceRoute, ResourceFieldSpec, ColumnConfig, DrilldownSectionConfig, ResourceFormField, etc.).
  • types/drizzle-schema.ts provides strongly typed table/column names (DrizzleTableName, DrizzleColumnName), column value types (DrizzleColumnValue), and metadata.
  • Always keep meta/ and types/ in sync with the Drizzle schema. Regenerate after schema changes (npx drizzle-kit generate).

Resource Forms

resource_forms is now a first-class subsystem, not just a demo pattern.

  • Author form schemas with defineResourceFormSchema().
  • Wrap them in defineResourceForm() to attach metadata, defaults, and explicit version lineage.
  • Convert definitions into persisted rows with createResourceFormRow() / createResourceFormRows().
  • Read rows back through resolveResourceFormRow() / resolveResourceFormRows().
  • Migrate submission payloads deterministically with migrateResolvedResourceFormSubmission() when downstream contracts expect a different form version.
  • Use useResourceFormRuntime() to manage selected-form and value state.
  • Render the validated schema with EntityFormV2.

Persist migration_key + schema_version on each resource_forms row so builder changes can be migrated as an explicit form family/version pair.

Use this layer whenever a form should be driven by persisted metadata instead of hardcoded JSX.

Testing

  • Unit tests live inside packages/resource-framework/__tests__.
  • They cover column builders, coercion helpers, filter parsing, drilldown helpers, display configuration, templates, and hooks (build-columns.test.tsx, coerce.test.ts, use-table-configuration.test.ts, etc.).
  • Run npm test for contract/unit coverage.
  • Run npm run test:integration to execute the env-validated Athena CRUD/file integration suite.
  • Run npm run typecheck for the workspace-aware package/demo/playground TypeScript checks.

Best practices & patterns

  • Run npx drizzle-kit generate whenever you touch drizzle/schema.ts. The metadata snapshot is consumed at runtime.
  • Prefer defineDrizzleResourceRoute/defineDrizzleColumns to inline strings for type safety.
  • Wrap UI with ResourceProvider to keep context, notifications, and header integration consistent.
  • Drilldown widgets must be registered via components/sections/widgets and registerSectionWidget.
  • Always call notification({ message, success }) via useNotification after mutations to show consistent alerts.
  • Use the Table* components for action buttons/controls so variant/icon/rounding rules stay consistent.
  • When resources require non-standard data, keep handlers/handle-options.ts updated with new data sources to power dropdowns.

This README is intended to be the canonical reference you can cite from .mdc documentation pages.

Resource Framework

A powerful, type-safe abstraction layer built on Drizzle ORM that provides automatic CRUD operations, data fetching, table rendering, and form generation for your database tables.

What is this?

The Resource Framework turns your Drizzle schema into a fully-functional admin interface with:

  • Type-safe table and column references - Full TypeScript intellisense
  • Automatic form generation - Create/edit forms based on your schema
  • Data fetching - Built-in hooks for querying data
  • Table rendering - Responsive tables with sorting, filtering, pagination
  • Drilldown views - Automatic detail pages for records

Quick Start

1. Define your Drizzle schema

// drizzle/schema.ts
import { pgTable, text, numeric, timestamp } from "drizzle-orm/pg-core";

export const invoices = pgTable("invoices", {
  invoice_id: text().primaryKey(),
  organization_id: text().notNull(),
  recipient_email: text().notNull(),
  amount: numeric("amount", { precision: 10, scale: 2 }),
  status: text().notNull(),
  due_date: timestamp({ withTimezone: true }),
});

2. Generate Drizzle snapshot

npx drizzle-kit generate

This creates drizzle/meta/0000_snapshot.json which the framework reads for metadata.

3. Use CreateResourceDialog

import { CreateResourceDialog } from "@/packages/resource-framework";

function MyComponent() {
  const [open, setOpen] = useState(false);

  return (
    <CreateResourceDialog
      open={open}
      onCloseAction={() => setOpen(false)}
      table="invoices"
      columns={["organization_id", "recipient_email", "amount", "status"]}
      required={["organization_id", "recipient_email"]}
      title="Create invoice"
    />
  );
}

When you load the dialog configuration from resource_routes via resourceName, store a create entry that lists columns to control which fields appear:

{
  "create": {
    "scope": "create:invoices",
    "required": ["recipient_email", "amount"],
    "columns": ["recipient_email", "amount", "status"]
  }
}

That's it! The framework automatically:

  • Infers field types from your schema
  • Renders appropriate inputs (text, number, date, etc.)
  • Validates required fields
  • Inserts data into the database
  • Shows success/error notifications

Components

CreateResourceDialog

Automatically generates create forms based on your schema.

Basic usage:

<CreateResourceDialog
  open={open}
  table="invoices"
  columns={["recipient_email", "amount"]}
  onCloseAction={() => setOpen(false)}
/>

With configuration:

<CreateResourceDialog
  table="invoices"
  columns={[
    {
      column_name: "status",
      header: "Status",
      editor: {
        type: "select",
        options: [
          { label: "Draft", value: "draft" },
          { label: "Sent", value: "sent" },
        ],
      },
    },
    {
      column_name: "customer_id",
      editor: {
        type: "select",
        data_source: "customers", // Auto-loads from database
      },
    },
  ]}
  defaultValues={{
    organization_id: user.organization_id,
  }}
/>

ResourceTable

Displays data in a responsive table.

<ResourceTable resourceName="invoices" />

ResourceDrilldown

Shows detailed view of a single record.

<ResourceDrilldown resourceName="invoices" />

Type Safety

Use type-safe definitions for autocomplete and compile-time checks:

import { 
  defineDrizzleResourceRoute, 
  defineDrizzleColumns 
} from "@/packages/resource-framework";

const route = defineDrizzleResourceRoute({
  table: "invoices", //  Autocomplete from schema
  idColumn: "invoice_id", //  Type-safe
  columns: defineDrizzleColumns<"invoices">([
    { column_name: "recipient_email" }, //  Autocomplete
    { column_name: "amount" },
  ]),
});

Features

Automatic Type Inference

Field types are automatically inferred from your Drizzle schema:

Drizzle Type Field Type Rendered As
text() text <input type="text">
integer() number <input type="number">
boolean() boolean <input type="checkbox">
timestamp() date <input type="datetime-local">
numeric() number <input type="number">

Editor Types

Configure how fields are rendered:

Select with static options:

{
  column_name: "status",
  editor: {
    type: "select",
    options: [
      { label: "Draft", value: "draft" },
      { label: "Sent", value: "sent" },
    ]
  }
}

Select with dynamic data source:

{
  column_name: "customer_id",
  editor: {
    type: "select",
    data_source: {
      table: "customers",
      value_column: "customer_id",
      label_column: "name",
    }
  }
}

Default Values

Pre-fill fields that shouldn't be edited:

<CreateResourceDialog
  columns={["recipient_email", "amount"]}
  defaultValues={{
    organization_id: user.organization_id,
    created_by: user.user_id,
    status: "draft",
  }}
/>

API Reference

CreateResourceDialog Props

Prop Type Description
open boolean Dialog open state
onCloseAction () => void Called when dialog closes
table string Database table name
columns Array<string | ColumnConfig> Columns to include
required string[] Required field names
defaultValues Record<string, FieldValue> Default values
title string Dialog title

See the full API reference in docs/RESOURCE_FRAMEWORK.md.

Utility Functions

getDrizzleColumnInfo

Get metadata about a column:

import { getDrizzleColumnInfo } from "@/packages/resource-framework";

const info = getDrizzleColumnInfo("invoices", "amount");
// { dataType: "number", fieldType: "number", isNullable: false }

getDrizzleFieldType

Get the inferred field type:

import { getDrizzleFieldType } from "@/packages/resource-framework";

const type = getDrizzleFieldType("invoices", "due_date");
// "date"

TypeScript Types

DrizzleTableName

All table names from your schema:

import type { DrizzleTableName } from "@/packages/resource-framework";

const table: DrizzleTableName = "invoices"; // Autocomplete

DrizzleColumnName

Column names for a specific table:

import type { DrizzleColumnName } from "@/packages/resource-framework";

type Col = DrizzleColumnName<"invoices">;
// "invoice_id" | "organization_id" | "recipient_email" | ...

DrizzleColumnValue<T, C>

Value type for a column:

import type { DrizzleColumnValue } from "@/packages/resource-framework";

type Amount = DrizzleColumnValue<"invoices", "amount">; // number

Force External API Updates

The force_external_api_updates flag is still accepted for route compatibility, but it now routes through the Athena gateway adapter rather than a direct browser call to a legacy endpoint:

// In RESOURCE_ROUTES
export const RESOURCE_ROUTES = {
  invoices: {
    table: "invoices",
    idColumn: "invoice_id",
    
    // Keep using the external Athena-backed mutation path
    force_external_api_updates: true, // default: false
  }
};

When enabled:

  • All edit/update operations stay on the Athena adapter path.
  • No client-embedded legacy API secret is used.
  • Useful while migrating routes that still need the gateway-backed mutation contract.
  • If false (default), the same Athena adapter path is used without the compatibility flag.

Header Actions Integration

The Resource Framework can defer the page title and "New" button to the app header using useContentStore:

// In RESOURCE_ROUTES
export const RESOURCE_ROUTES = {
  invoices: {
    table: "invoices",
    idColumn: "invoice_id",
    page_label: "Invoices",
    enableNewResourceCreation: true,
    newResourceButtonText: "New invoice",
    
    // Defer everything to header
    deferToHeader: true,
    
    // Or defer individually
    deferTitleToHeader: true,
    deferNewButtonToHeader: true,
    deferSubtitleToHeader: true,
  }
};

When enabled:

  • Title moves from table header to app header
  • "New" button appears in app header with icon
  • Subtitle can be set in app header
  • Table shows clean interface without redundant headers

This creates a cleaner, more integrated UI where controls live in the main app header instead of within the table component.

Documentation

  • Quick Reference: docs/RESOURCE_FRAMEWORK_QUICKREF.md - Cheat sheet
  • Full Documentation: docs/RESOURCE_FRAMEWORK.md - Complete guide
  • Drizzle Integration: docs/DRIZZLE_INTEGRATION.md - Architecture details
  • Demo: /v2/beautiful - Interactive demonstration

Live Demo

Visit /v2/beautiful in your app to see:

  • Interactive CreateResourceDialog demo
  • Real-time configuration editor
  • Code examples
  • Documentation

Architecture

Your Drizzle Schema (schema.ts)
    ↓
Drizzle Snapshot (meta/snapshot.json)
    ↓
Type Inference (types/drizzle-schema.ts)
    ↓
Metadata Utils (utils/drizzle-editor.ts)
    ↓
Components (CreateResourceDialog, ResourceTable, etc.)

The framework uses type-only imports for compile-time checks and JSON snapshot for runtime metadata. This provides type safety without bloating your client bundle.

Examples

Basic Invoice Creation

<CreateResourceDialog
  table="invoices"
  columns={["recipient_email", "amount", "due_date"]}
  required={["recipient_email", "amount"]}
  defaultValues={{
    organization_id: user.organization_id,
    status: "draft",
  }}
/>

With Foreign Key Dropdown

<CreateResourceDialog
  table="invoices"
  columns={[
    {
      column_name: "customer_id",
      editor: { type: "select", data_source: "customers" }
    },
    "amount",
    "due_date",
  ]}
/>

Type-Safe Route Definition

const route = defineDrizzleResourceRoute({
  table: "invoices",
  idColumn: "invoice_id",
  columns: defineDrizzleColumns<"invoices">([
    { column_name: "recipient_email", header: "Recipient" },
    { column_name: "amount", header: "Amount" },
  ]),
});

Best Practices

DO:

  • Use defineDrizzleResourceRoute for type safety
  • Pre-fill system fields with defaultValues
  • Mark internal columns as hidden
  • Use data sources for foreign keys
  • Run drizzle-kit generate after schema changes

DON'T:

  • Import Drizzle schema directly on client
  • Hardcode table/column names
  • Forget to regenerate snapshot after schema changes

Troubleshooting

No columns showing?

npx drizzle-kit generate

Type errors?

  • Restart TypeScript server
  • Ensure tables are exported in drizzle/schema.ts

Fields showing as text?

{
  column_name: "amount",
  data_type: "number",
  editor: { type: "number" }
}

Contributing

The framework is designed to be extended:

  1. Add custom type mappings in utils/drizzle-editor.ts
  2. Create custom editors in components/dialog.tsx
  3. Add custom formatters in column configs

License

Part of the Suitsbooks project.


Need help? Check the docs folder for comprehensive guides.

Top Architecture Failure Modes

This framework is a metadata-driven UI layer where the critical runtime path is: UI components (ResourceTable/ResourceDrilldown) -> Athena SDK adapter -> Athena gateway/DB, plus file flows via Athena-hosted upload and signed S3/MinIO URL refresh endpoints. The highest operational risk is concentrated in network dependency health and consistency between storage and metadata writes.

  • Failure mode: Data-plane dependency outage. Trigger: server-action/data API or fallback endpoint unavailability. Symptoms: tables fail to load, updates fail, repeated retries, user-facing errors. Detection: elevated client isError, higher mutation failure rate, API p95/p99 latency spikes. Mitigation: move all CRUD behind Athena gateway, add circuit breaking and bounded retries, and standardize error envelopes.
  • Failure mode: Read-after-write inconsistency. Trigger: update succeeds but immediate refetch returns stale/cached row. Symptoms: values appear reverted, duplicate save attempts, mismatch logs. Detection: compare update payload vs post-update read in telemetry. Mitigation: require canonical updated row in Athena write responses, enforce consistent cache policy per route, and use version/etag checks.
  • Failure mode: File upload two-phase gap. Trigger: object upload succeeds but metadata insert fails (or inverse). Symptoms: orphaned objects, missing files in UI, broken links. Detection: periodic reconciliation between object storage keys and file metadata table. Mitigation: transactional server workflow via Athena, idempotency keys, and compensating cleanup jobs.
  • Failure mode: Signed URL refresh dependency failures. Trigger: /api/files/refresh-url equivalent path unavailable or key extraction mismatch. Symptoms: 403 loops, preview/download failures, repeated refresh attempts. Detection: refresh failure-rate and 403-rate dashboards by renderer type. Mitigation: centralized Athena file URL refresh endpoint, proactive refresh with jitter, and resilient fallback UX.
  • Failure mode: Client-side privileged fallback exposure. Trigger: browser path uses direct privileged external update calls. Symptoms: secret leakage risk and unauthorized mutation attempts. Detection: secret scanning + anomaly detection on mutation traffic. Mitigation: remove client-embedded secrets and route all privileged operations through Athena with scoped server-side auth.

Confirmed vs Inferred

  • Confirmed from repo: UI layer, useApiClient data access, Athena SDK adapter, Athena-configured file upload path, and signed URL refresh flow.
  • Inferred due to missing backend internals here: exact DB topology, queueing/event infrastructure, and gateway-level HA posture.

Migration Target

  • Target gateway spec: https://athena-db.com/openapi.yaml
  • Implementation plan: see TODO.md.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages