Examples · 13 apps

13 reference apps. Each runs in one command.

Clone the one that looks like your problem; swap the entity declarations. Each app's full source is under examples/ in the repo. Run it with gofastr dev for rebuild-on-save, livereload, and the dev MCP tools; plain go run . works too. The in-site demo apps and the theme-layer showcases are in the menu.

Meridian: SaaS console

flagshipseeded + hand-evolved

A billing & revenue console (customers, subscriptions, invoices, MRR + charts) plus its marketing site, auth, RBAC, and an admin back-office, seeded from one gofastr.yml and hand-evolved since, with writable screens (add/edit/delete).

  • One app spec seeded marketing + app + auth + admin
  • Server-rendered DataTable / charts / forms, island RPCs
  • Writable CRUD + RBAC, with a generated end-to-end test suite
run
cd examples/meridian && gofastr dev
View source ↗
examples/meridian/gofastr.yml
963 lines
# =============================================================================
# Meridian: SaaS billing & revenue console (GoFastr flagship demo)
#
# A believable product: a public marketing site + an authenticated billing
# console (customers, subscriptions, invoices with real status workflows, MRR
# metrics, live activity) + an admin back-office. Generated entirely from this
# one app spec. Design: calm, light, refined (Stripe/Notion).
#
# NOTE: this app spec is the TARGET SPEC: some block kinds / features used
# here are being built into the generator (server-rendered entity screens,
# the full framework/ui block catalog, marketing layout, auth/RBAC). It is
# authored first so the product is concrete; the generator is made to render it.
# =============================================================================

app:
  name: Meridian
  module: github.com/DonaldMurillo/gofastr/examples/meridian
  api_prefix: api
  static_dir: static
  # Spanish (locales/es.json) lives under /es/, with hreflang, sitemap
  # and robots entries; English keeps the plain paths.
  locale_prefixes: true
  db:
    driver: sqlite
    url: file:meridian.db
  auth:
    enabled: true
    dev_mode: true
  admin:
    enabled: true
    path: /admin
    role: admin
    login_path: /login
    seed_email: admin@meridian.dev
    seed_password: change-me-now-123
  # Calm, light, refined: warm paper, ink text, one confident indigo accent.
  theme:
    # Type: a distinctive editorial display face (Bricolage Grotesque) for
    # headings + a warm humanist grotesk (Hanken Grotesk) with true tabular
    # figures for body/data. The display face gives titles real character so
    # the hierarchy reads as designed, not default-bold.
    font_heading: Bricolage Grotesque
    font_body: Hanken Grotesk
    background: "#F8F7F4"
    surface: "#FFFFFF"
    surface-soft: "#F2F1EC"
    text: "#1B1B2A"
    text-muted: "#65657A"
    text-subtle: "#6A6A72"
    border: "#E7E5DF"
    border-strong: "#33334A"
    primary: "#4338CA"
    primary-fg: "#FFFFFF"
    secondary: "#0E7C86"
    accent: "#4338CA"
    success: "#166534"
    warning: "#854D0E"
    danger: "#B91C1C"
    info: "#1D4ED8"
    # Dark scheme: a calm warm-charcoal tinted toward the indigo brand hue.
    # The header's theme toggle flips to this; every surface that reads the
    # --color-* tokens (marketing, app, components) recolors from one place.
    dark:
      background: "#15141B"
      surface: "#1F1D27"
      surface-soft: "#29262F"
      text: "#ECEAF3"
      text-muted: "#A29FB0"
      text-subtle: "#9498AC"
      border: "#322E3D"
      border-strong: "#494457"
      primary: "#8B80F2"
      primary-fg: "#15141B"
      secondary: "#2DD4BF"
      accent: "#8B80F2"
      success: "#4ADE80"
      warning: "#FBBF24"
      danger: "#F87171"
      danger-fg: "#15141B"
      info: "#60A5FA"

# =============================================================================
# DATA MODEL: the revenue domain: plans → subscriptions ← customers,
# invoices, payments. Status enums are the real workflows the operator drives.
# =============================================================================

entities:

  - name: plans
    crud: true
    mcp: true
    # Shared cross-tenant pricing catalog: owner_field is wrong (every
    # subscriber reads the same rows), so reads stay open for the pricing
    # pages and every write sits behind a permission the bootstrap admin
    # holds (see the rolePolicy in app.go). A plain signup session can
    # read the catalog and mutate none of it.
    access:
      read:
      create: plans:write
      update: plans:write
      delete: plans:admin
    properties:
      label: Plans
    display:
      title_fields: [name]
      description: What a customer can subscribe to.
      nav:
        group: catalog
        icon: box
        order: 1
      fields:
        price:
          input: money
    fields:
      - name: name
        type: string
        required: true
        max: 80
      - name: slug
        type: string
        required: true
        unique: true
        max: 80
      - name: price
        type: decimal
        required: true
        min: 0
      - name: interval
        type: enum
        values: [month, year]
        default: month
      - name: active
        type: bool
        default: true

  - name: customers
    crud: true
    mcp: true
    owner_field: user_id    # per-user: each signed-in account owns its own customers
    search_fields: [name]
    properties:
      label: Customers
    # Unique per owner, never across owners: a global UNIQUE on email
    # would let one account's row block another account's create and turn
    # the 409 into an "this email exists somewhere" oracle.
    indices:
      - name: idx_customers_owner_email
        columns: [user_id, email]
        unique: true
    fields:
      - name: name
        type: string
        required: true
        max: 120
      - name: email
        type: string
        required: true
      - name: company
        type: string
        max: 120
      - name: status
        type: enum
        values: [trialing, active, past_due, canceled]
        default: trialing
      - name: mrr
        type: decimal
        min: 0
        default: "0"
      - name: logo
        type: image
    display:
      title_fields: [name]
      description: Every account that pays you.
      nav:
        group: billing
        icon: users
        order: 1
      columns: [name, email, company, status, mrr]
      views:
        - key: active
          label: Active
          where: 'status = "active"'
        - key: past_due
          label: Past due
          where: 'status = "past_due"'
      facets: [status]
      fields:
        email:
          input: email
        mrr:
          label: MRR
          input: money

  - name: subscriptions
    crud: true
    mcp: true
    owner_field: user_id
    properties:
      label: Subscriptions
    fields:
      - name: customer_id
        type: relation
        to: customers
        required: true
      - name: plan_id
        type: relation
        to: plans
        required: true
      - name: status
        type: enum
        values: [trialing, active, past_due, canceled]
        default: trialing
      - name: mrr
        type: decimal
        min: 0
        default: "0"
      - name: started_on
        type: date
      - name: renews_on
        type: date
    display:
      title_fields: [customer_id, plan_id]
      description: Who is on which plan, and what they bring in.
      nav:
        group: billing
        icon: repeat
        order: 2
      views:
        - key: active
          label: Active
          where: 'status = "active"'
        - key: trialing
          label: Trialing
          where: 'status = "trialing"'
        - key: past_due
          label: Past due
          where: 'status = "past_due"'
      facets: [status]
      fields:
        mrr:
          label: MRR
          input: money
    relations:
      - type: belongs_to
        name: customer
        entity: customers
        foreign_key: customer_id
      - type: belongs_to
        name: plan
        entity: plans
        foreign_key: plan_id
    # Advisory as above: the screen transitions named no From.
    states:
      field: status
      advisory: true
      transitions:
        - key: activate
          label: Activate
          from: [trialing, past_due]
          to: active
          variant: primary
        - key: cancel
          label: Cancel
          from: [trialing, active, past_due]
          to: canceled
          variant: danger

  - name: invoices
    crud: true
    mcp: true
    owner_field: user_id
    search_fields: [number]
    properties:
      label: Invoices
    indices:
      - name: idx_invoices_owner_number   # unique per owner, as customers.email
        columns: [user_id, number]
        unique: true
    fields:
      - name: customer_id
        type: relation
        to: customers
        required: true
      - name: number
        type: string
        required: true
      - name: amount
        type: decimal
        required: true
        min: 0
      - name: status
        type: enum
        values: [draft, open, paid, past_due, void]
        default: draft
      - name: issued_on
        type: date
      - name: due_on
        type: date
      - name: paid_on
        type: date
    relations:
      - type: belongs_to
        name: customer
        entity: customers
        foreign_key: customer_id
    # The old screen-level transitions named no From, so the moves stay
    # advisory until the from-lists below are confirmed; drop advisory:
    # to let the CRUD handler enforce them.
    states:
      field: status
      advisory: true
      transitions:
        - key: mark_paid
          label: Mark paid
          from: [draft, open, past_due]
          to: paid
          variant: primary
          stamp: paid_on
        - key: void
          label: Void
          from: [draft, open, past_due]
          to: void
          variant: danger
    display:
      title_fields: [number]
      description: Every invoice issued, paid or not.
      nav:
        group: billing
        icon: receipt
        order: 3
      columns: [number, customer_id, amount, status, issued_on, due_on]
      views:
        - key: past_due
          label: Past due
          where: 'status = "past_due"'
          sort: due_on ASC
        - key: open
          label: Open
          where: 'status = "open"'
          sort: due_on ASC
      facets: [status, customer_id]
      fields:
        amount:
          input: money

  - name: payments
    crud: true
    mcp: true
    owner_field: user_id
    # A deleted payment goes to the admin's trash, where it can be
    # restored or deleted for good.
    soft_delete: true
    properties:
      label: Payments
    display:
      title_fields: [invoice_id, customer_id]
      description: Money in, and the attempts that failed.
      nav:
        group: billing
        icon: card
        order: 4
      views:
        - key: failed
          label: Failed
          where: 'status = "failed"'
      facets: [status, method]
      fields:
        amount:
          input: money
    fields:
      - name: invoice_id
        type: relation
        to: invoices
        required: true
      - name: customer_id
        type: relation
        to: customers
        required: true
      - name: amount
        type: decimal
        required: true
        min: 0
      - name: method
        type: enum
        values: [card, ach, wire]
        default: card
      - name: status
        type: enum
        values: [succeeded, failed, refunded]
        default: succeeded
    relations:
      - type: belongs_to
        name: invoice
        entity: invoices
        foreign_key: invoice_id
      - type: belongs_to
        name: customer
        entity: customers
        foreign_key: customer_id

# =============================================================================
# SCREENS in three layers and two layouts:
#   layout: marketing  → public site (own siteheader + sitefooter packages)
#   layout: app        → authenticated console (sidebar shell), access-gated
# =============================================================================

screens:

  # ---- Public marketing site -------------------------------------------------
  - name: home
    route: /
    layout: marketing
    title: "Meridian: billing that runs itself"
    description: The revenue console for modern SaaS.
    body:
      - kind: stack
        props:
          gap: 2xl
        children:
          - kind: hero
            props:
              eyebrow: Billing & revenue
              title: See your revenue the moment it moves.
              subtitle: Meridian gives SaaS teams one calm place to manage customers, subscriptions, and invoices, with the metrics that matter, live.
              cta_text: Start free
              cta_href: /signup
              secondary_text: See pricing
              secondary_href: /pricing
          - kind: section
            props:
              heading: Everything you need to run revenue
              eyebrow: Why Meridian
            children:
              - kind: card
                props:
                  heading: Live MRR & churn
                  text: Watch monthly recurring revenue, growth, and churn update as customers sign up and pay.
              - kind: card
                props:
                  heading: Subscriptions that flow
                  text: "Trialing, active, past-due, canceled: drive the whole lifecycle from one screen."
              - kind: card
                props:
                  heading: Invoices, handled
                  text: "Open, paid, void: track every invoice and mark them paid in a click."
      # The app's closing CTA (an ink-band ui.Themed card in screens.go) is
      # hand-authored past the app spec vocabulary: generate once, then own
      # the Go. pack skips it, so it has no block here.

  - name: pricing
    route: /pricing
    layout: marketing
    title: Pricing
    description: Plans for teams of every size.
    body:
      - kind: stack
        props:
          gap: 2xl
        children:
          - kind: page_header
            props:
              title: Pricing
              subtitle: Start free. Upgrade when revenue does.
          - kind: pricing
            props:
              plans:
                - name: Starter
                  price: "$29"
                  period: /mo
                  description: For solo founders finding their first customers.
                  features: ["Up to 100 customers", "Core billing & invoices", "Email support"]
                  cta_text: Start free
                  cta_href: /signup
                - name: Pro
                  price: "$99"
                  period: /mo
                  description: For growing teams that live in their revenue.
                  featured: true
                  features: ["Unlimited customers", "MRR & churn analytics", "Subscription workflows", "Priority support"]
                  cta_text: Start free
                  cta_href: /signup
                - name: Scale
                  price: "$299"
                  period: /mo
                  description: For high-volume revenue and finance teams.
                  features: ["Everything in Pro", "SSO & audit log", "Dedicated success manager", "99.9% uptime SLA"]
                  cta_text: Contact sales
                  cta_href: /signup

  # ---- About: the plain heading/paragraph content path (readable measure) ----
  - name: about
    route: /about
    layout: marketing
    title: About Meridian
    description: Why we built a calmer billing console.
    body:
      - kind: stack
        props:
          gap: 2xl
        children:
          - kind: page_header
            props:
              title: We think billing should feel calm.
          - kind: markdown
            text: "Meridian is a demonstration product built entirely from a GoFastr app spec, a single declarative file that generates this marketing site, the authenticated console, auth, roles, and an admin back-office, all server-rendered.\n\nIt exists to show that a framework can generate a real, polished web application, not a CRUD scaffold."

  # ---- Terms + Privacy: the markdown content path (rich long-form prose) ------
  - name: terms
    route: /terms
    layout: marketing
    title: Terms of Service
    body:
      - kind: markdown
        text: "# Terms of Service\n\nThis is a demonstration application. The text below is placeholder content that shows long-form, **readable** typography rendered from Markdown in the marketing layout.\n\n## Acceptance\n\nBy using Meridian you agree these terms are illustrative only: there is no real service, billing, or obligation.\n\n## Use of the service\n\n- Evaluate Meridian freely for any purpose.\n- Sample data is reset periodically without notice.\n- Don't rely on it for anything that matters.\n\n## Liability\n\nMeridian is provided *as-is*, without warranty of any kind."

  - name: privacy
    route: /privacy
    layout: marketing
    title: Privacy Policy
    body:
      - kind: markdown
        text: "# Privacy Policy\n\nMeridian is a demo and stores only the sample data you create while exploring it. The content below is placeholder Markdown.\n\n## What we collect\n\nNothing personal. A demo account and any records you add in the console, all reset periodically.\n\n## What we share\n\nNothing. There are no third parties, trackers, or analytics in this demonstration app."

  # ---- Auth ------------------------------------------------------------------
  - name: login
    route: /login
    layout: marketing
    title: Sign in
    body:
      - kind: login_form
        text: Sign in to Meridian
        props:
          action: /auth/login
          next: /app
          register_href: /signup

  - name: signup
    route: /signup
    layout: marketing
    title: Create your account
    body:
      - kind: signup_form
        text: Create your Meridian account
        props:
          action: /auth/register
          next: /app
          login_href: /login

  # ---- App (authenticated, RBAC-gated) --------------------------------------
  - name: dashboard
    route: /app
    layout: app
    access:
      auth: true
    title: Overview
    description: Your revenue at a glance.
    body:
      - kind: page_header
        props:
          title: Overview
          subtitle: Revenue at a glance
      - kind: stat_row
        children:
          - kind: stat_card
            props:
              label: MRR
              # Real MRR: summed from ACTIVE subscriptions, not a stored column.
              source:
                entity: subscriptions
                agg: sum
                field: mrr
                filter: status=active
              format: money
          - kind: stat_card
            props:
              label: Active customers
              source:
                entity: customers
                agg: count
                filter: status=active
          - kind: stat_card
            props:
              label: Past-due invoices
              source:
                entity: invoices
                agg: count
                filter: status=past_due
          - kind: stat_card
            props:
              label: Plans
              source:
                entity: plans
                agg: count
      - kind: bar_chart
        props:
          title: Customers by status
          source:
            entity: customers
            group_by: status
      - kind: entity_list
        entity: invoices
        text: Recent invoices
        fields: [number, customer_id, amount, status, due_on]
        limit: 8
        empty_text: No invoices yet.

  - name: customers
    route: /app/customers
    layout: app
    access:
      auth: true
    title: Customers
    body:
      - kind: entity_list
        entity: customers
        text: Customers
        fields: [name, email, company, status, mrr]
        limit: 8
        create: true
        empty_text: No customers yet. Add your first to get started.

  - name: customer_detail
    route: /app/customers/{id}
    layout: app
    access:
      auth: true
    title: Customer
    body:
      - kind: entity_detail
        entity: customers

  - name: invoices
    route: /app/invoices
    layout: app
    access:
      auth: true
    title: Invoices
    body:
      - kind: entity_list
        entity: invoices
        text: Invoices
        fields: [number, customer_id, amount, status, issued_on, due_on]
        limit: 25
        create: true
        bulk: true
        empty_text: No invoices yet.

  - name: invoice_detail
    route: /app/invoices/{id}
    layout: app
    access:
      auth: true
    title: Invoice
    body:
      - kind: entity_detail
        entity: invoices

  - name: subscriptions
    route: /app/subscriptions
    layout: app
    access:
      auth: true
    title: Subscriptions
    body:
      - kind: entity_list
        entity: subscriptions
        text: Subscriptions
        fields: [customer_id, plan_id, status, mrr, renews_on]
        limit: 25
        create: true
        empty_text: No subscriptions yet.

  - name: subscription_detail
    route: /app/subscriptions/{id}
    layout: app
    access:
      auth: true
    title: Subscription
    body:
      - kind: entity_detail
        entity: subscriptions

# =============================================================================
# NAV: marketing header/footer + app sidebar
# =============================================================================

nav:
  - label: Overview
    href: /app
  - label: Customers
    href: /app/customers
  - label: Subscriptions
    href: /app/subscriptions
  - label: Invoices
    href: /app/invoices
  - label: Admin
    href: /admin
    role: admin          # only signed-in admins see this link

# =============================================================================
# SEED: realistic sample data so a fresh boot shows a populated console.
# =============================================================================

seed:
  - entity: plans
    rows:
      - name: Starter
        slug: starter
        price: "29"
        interval: month
        active: true
      - name: Pro
        slug: pro
        price: "99"
        interval: month
        active: true
      - name: Scale
        slug: scale
        price: "299"
        interval: month
        active: true
  - entity: customers
    rows:
      - name: Ada Lovelace
        email: ada@acme.io
        company: Acme Inc
        status: active
        mrr: "99"
      - name: Grace Hopper
        email: grace@globex.com
        company: Globex
        status: active
        mrr: "299"
      - name: Alan Turing
        email: alan@initech.io
        company: Initech
        status: past_due
        mrr: "29"
      - name: Katherine Johnson
        email: kj@umbrella.co
        company: Umbrella
        status: active
        mrr: "99"
      - name: Linus Torvalds
        email: linus@hooli.com
        company: Hooli
        status: canceled
        mrr: "0"
      - name: Margaret Hamilton
        email: mh@apollo.io
        company: Apollo
        status: active
        mrr: "299"
      - name: Dennis Ritchie
        email: dmr@belllabs.com
        company: Bell Labs
        status: trialing
        mrr: "0"
      - name: Barbara Liskov
        email: liskov@mit.edu
        company: MIT
        status: active
        mrr: "99"
      - name: Tim Berners-Lee
        email: tbl@w3.org
        company: W3C
        status: past_due
        mrr: "29"
      - name: Donald Knuth
        email: knuth@stanford.edu
        company: Stanford
        status: active
        mrr: "299"

  # Relational seed: customer_id / plan_id reference rows seeded above by a
  # natural key ("@entity.field=value"); the seed runner resolves them to the
  # generated ids before insert. Order matters: plans + customers first.
  - entity: subscriptions
    rows:
      - customer_id: "@customers.email=ada@acme.io"
        plan_id: "@plans.slug=pro"
        status: active
        mrr: "99"
        started_on: "2025-09-04"
        renews_on: "2026-07-04"
      - customer_id: "@customers.email=grace@globex.com"
        plan_id: "@plans.slug=scale"
        status: active
        mrr: "299"
        started_on: "2025-06-12"
        renews_on: "2026-06-12"
      - customer_id: "@customers.email=alan@initech.io"
        plan_id: "@plans.slug=starter"
        status: past_due
        mrr: "29"
        started_on: "2025-11-20"
        renews_on: "2026-05-20"
      - customer_id: "@customers.email=kj@umbrella.co"
        plan_id: "@plans.slug=pro"
        status: active
        mrr: "99"
        started_on: "2025-10-01"
        renews_on: "2026-07-01"
      - customer_id: "@customers.email=linus@hooli.com"
        plan_id: "@plans.slug=scale"
        status: canceled
        mrr: "0"
        started_on: "2024-12-15"
        renews_on: "2025-12-15"
      - customer_id: "@customers.email=mh@apollo.io"
        plan_id: "@plans.slug=scale"
        status: active
        mrr: "299"
        started_on: "2025-03-22"
        renews_on: "2026-06-22"
      - customer_id: "@customers.email=dmr@belllabs.com"
        plan_id: "@plans.slug=pro"
        status: trialing
        mrr: "0"
        started_on: "2026-06-01"
        renews_on: "2026-06-15"
      - customer_id: "@customers.email=liskov@mit.edu"
        plan_id: "@plans.slug=pro"
        status: active
        mrr: "99"
        started_on: "2025-08-09"
        renews_on: "2026-07-09"
      - customer_id: "@customers.email=tbl@w3.org"
        plan_id: "@plans.slug=starter"
        status: past_due
        mrr: "29"
        started_on: "2025-12-02"
        renews_on: "2026-06-02"
      - customer_id: "@customers.email=knuth@stanford.edu"
        plan_id: "@plans.slug=scale"
        status: active
        mrr: "299"
        started_on: "2025-05-18"
        renews_on: "2026-06-18"

  - entity: invoices
    rows:
      - customer_id: "@customers.email=ada@acme.io"
        number: INV-1001
        amount: "99"
        status: paid
        issued_on: "2026-05-04"
        due_on: "2026-05-18"
        paid_on: "2026-05-06"
      - customer_id: "@customers.email=grace@globex.com"
        number: INV-1002
        amount: "299"
        status: paid
        issued_on: "2026-05-12"
        due_on: "2026-05-26"
        paid_on: "2026-05-13"
      - customer_id: "@customers.email=alan@initech.io"
        number: INV-1003
        amount: "29"
        status: past_due
        issued_on: "2026-04-20"
        due_on: "2026-05-04"
      - customer_id: "@customers.email=kj@umbrella.co"
        number: INV-1004
        amount: "99"
        status: paid
        issued_on: "2026-05-01"
        due_on: "2026-05-15"
        paid_on: "2026-05-02"
      - customer_id: "@customers.email=mh@apollo.io"
        number: INV-1005
        amount: "299"
        status: open
        issued_on: "2026-06-01"
        due_on: "2026-06-15"
      - customer_id: "@customers.email=liskov@mit.edu"
        number: INV-1006
        amount: "99"
        status: paid
        issued_on: "2026-05-09"
        due_on: "2026-05-23"
        paid_on: "2026-05-11"
      - customer_id: "@customers.email=tbl@w3.org"
        number: INV-1007
        amount: "29"
        status: past_due
        issued_on: "2026-04-02"
        due_on: "2026-04-16"
      - customer_id: "@customers.email=knuth@stanford.edu"
        number: INV-1008
        amount: "299"
        status: paid
        issued_on: "2026-05-18"
        due_on: "2026-06-01"
        paid_on: "2026-05-19"
      - customer_id: "@customers.email=grace@globex.com"
        number: INV-1009
        amount: "299"
        status: open
        issued_on: "2026-06-12"
        due_on: "2026-06-26"
      - customer_id: "@customers.email=ada@acme.io"
        number: INV-1010
        amount: "99"
        status: open
        issued_on: "2026-06-04"
        due_on: "2026-06-18"
      - customer_id: "@customers.email=knuth@stanford.edu"
        number: INV-1011
        amount: "299"
        status: paid
        issued_on: "2026-04-18"
        due_on: "2026-05-02"
        paid_on: "2026-04-20"
      - customer_id: "@customers.email=kj@umbrella.co"
        number: INV-1012
        amount: "99"
        status: past_due
        issued_on: "2026-04-15"
        due_on: "2026-04-29"

  - entity: payments
    rows:
      - invoice_id: "@invoices.number=INV-1001"
        customer_id: "@customers.email=ada@acme.io"
        amount: "99"
        method: card
        status: succeeded
      - invoice_id: "@invoices.number=INV-1002"
        customer_id: "@customers.email=grace@globex.com"
        amount: "299"
        method: card
        status: succeeded
      - invoice_id: "@invoices.number=INV-1004"
        customer_id: "@customers.email=kj@umbrella.co"
        amount: "99"
        method: ach
        status: succeeded
      - invoice_id: "@invoices.number=INV-1006"
        customer_id: "@customers.email=liskov@mit.edu"
        amount: "99"
        method: card
        status: succeeded
      - invoice_id: "@invoices.number=INV-1008"
        customer_id: "@customers.email=knuth@stanford.edu"
        amount: "299"
        method: wire
        status: succeeded
      - invoice_id: "@invoices.number=INV-1011"
        customer_id: "@customers.email=knuth@stanford.edu"
        amount: "299"
        method: wire
        status: succeeded
      - invoice_id: "@invoices.number=INV-1003"
        customer_id: "@customers.email=alan@initech.io"
        amount: "29"
        method: card
        status: failed
      - invoice_id: "@invoices.number=INV-1012"
        customer_id: "@customers.email=kj@umbrella.co"
        amount: "99"
        method: card
        status: failed

ShopFront: storefront

app generator100% generated

A complete storefront from one gofastr.yml: five related entities (categories, products, orders, order_items, reviews), a themed eight-screen UI, custom endpoints, and seed data. Nothing under app/ is hand-written.

  • Second app generator beside Meridian
  • Owner-scoped orders and reviews
  • Exercised by its own end-to-end test
run
cd examples/ecommerce && gofastr dev --dir app
View source ↗
examples/ecommerce/gofastr.yml
3 lines
# 5 entities, 8 screens, auth, seeds: one YAML
$ gofastr generate   // emits ./app: plain Go
$ go run ./app

Go-declared blog

smallest~270 LoC

Users, posts, comments. Three entities. Start here: it's the end-to-end story in one file.

  • Three entities declared in Go
  • Auto-CRUD + Swagger UI + MCP
  • SQLite by default; swap for Postgres in main.go
run
cd examples/blog && gofastr dev
View source ↗
examples/blog/main.go
4 lines
app.Entity("users", …)
app.Entity("posts", …)
app.Entity("comments", …)
app.Start(":8080")

This site (UI showcase)

largest~12000 LoC

Every core-ui pattern + framework/ui component, one page each, plus the docs, SEO, multi-step wizard, and print-battery demos. The site you're reading right now.

  • Every core-ui pattern + framework/ui component
  • Docs, philosophy, examples, Kiln pages
  • SEO interfaces, sitemap/robots, wizard, print
run
cd examples/site && gofastr dev
View source ↗
examples/site/main.go
3 lines
host := uihost.New(site, …)
app := framework.NewUIHostApp(host)
app.Start(":8083")

API tour

annotated source~160 LoC

Every v2 API feature in one annotated main.go, with the curl commands to exercise each one in the file header.

  • Cursor + offset pagination
  • Eager loading (?include=…)
  • Batch endpoints, SSE entity events, uploads
run
cd examples/api-tour && gofastr dev
View source ↗
examples/api-tour/main.go
3 lines
app.Entity("posts", …)
// cursor + offset paging, ?include=, batch, SSE
app.Start(":8080")

Local semantic search

no API key~120 LoC

A markdown corpus indexed locally via battery/semantic. No external API key; works offline.

  • Brute-force cosine, hybrid keyword fusion
  • Snapshot + WAL persistence
  • Poll-watch for file changes
run
cd examples/semantic-demo && gofastr dev
View source ↗
examples/semantic-demo/main.go
3 lines
idx, _ := semantic.Open(semantic.Options{…})
idx.Add(ctx, docs…)   // local vectors
hits, _ := idx.Query(ctx, semantic.Query{Text: "hooks", Limit: 5})

Vue + GoFastr API

BYO client~140 LoC server

For teams who already have a client app. Shows the framework is happy to just be your typed API.

  • Same auto-CRUD entities
  • Vue 3 + Vue Router from a CDN, no npm, no build step
  • No SSR: the Go app serves JSON and the static files
run
cd examples/spa && gofastr dev
View source ↗
examples/spa/main.go
3 lines
app.Entity("articles", …)
// JSON API under /api: your Vue app is the client
app.Start(":3090")

Embeddable surfaces

cross-origin~310 LoC

Two servers on different ports: a GoFastr app, and a customer's website that pastes one script tag and gets a live, themed, authenticated panel from it.

  • Single-use handshake nonce, exact origin allowlist
  • Frame runtime with no SPA navigation
  • Customer brand token applied to the app's own components
run
cd examples/embed-demo && gofastr dev
View source ↗
examples/embed-demo/main.go
3 lines
embeds, _ := embed.New(embed.Config{…})
nonce, _ := embeds.MintNonce("reports", user.ID, origin, nil)
// the customer pastes: <script src=".../__gofastr/embed.js" data-token=…>

Static file server

no screens~60 LoC

A plain file server on the framework router: static.Mount serves the pages/ directory, with HTML and CSS straight from disk, no screens, no runtime JS.

  • static.Mount with SPA mode off: only real files serve
  • index.html answers /
  • API routes can mount beside it on the same router
run
cd examples/static-site && gofastr dev
View source ↗
examples/static-site/main.go
4 lines
static.Mount(app.Router(), static.Config{
  FS: os.DirFS(pagesDir),
})
app.Start(":3070")

Entity admin

battery/admin~310 LoC

Three entities and one admin.New call: the whole back-office (list, create, edit, delete) is generated with defaults, behind a demo login. No bespoke JavaScript anywhere in the app.

  • admin.New with AllEntities: true generates every screen
  • DataTable island paginates without a reload
  • Delete is a data-cui-confirm button; forms are server-rendered
run
cd examples/backoffice && gofastr dev
View source ↗
examples/backoffice/main.go
4 lines
app.Entity("products", …)
app.RegisterBattery(admin.New(admin.Config{
  Title: "Backoffice", AllEntities: true,
}))

Process-isolated module

moduleproto~620 LoC

A third-party module as its own process, plus the host app that runs it. The module speaks moduleproto over stdio, serves four routes and one tool, and answers a reverse entity query. The host registers it as an untrusted module, so it runs only where the sandbox passes every probe.

  • The module depends only on moduleproto + the standard library
  • Handshake, ready, health, http, drain, tool.list, tool.call
  • The host refuses the module, with the probe report, where the sandbox does not conform
run
go run ./examples/processmodule-demo/host -module dist/examples/processmodule-demo
View source ↗
examples/processmodule-demo/main.go
4 lines
codec, _ := moduleproto.NewCodec(os.Stdin, os.Stdout, …)
peer := moduleproto.NewPeer(codec, moduleproto.RoleChild)
peer.Handle(moduleproto.MethodHTTP, serveRoute)
peer.Start(); <-peer.Done()

WebMCP remote assist

WebMCP + WebRTC~2000 LoC

One binary, one origin, two roles: a support console whose in-browser agent guides an operator through a camera session. Support-only WebMCP tool discovery, one typed command behind the button and the tools, and peer-to-peer video with server-side signaling only.

  • Tools scoped to /support and authorization-wrapped
  • Role cookies authorize; the WebMCP header only attributes
  • Sequenced realtime state over the ws runtime module
run
cd examples/webmcp-remote-assist && gofastr dev
View source ↗
examples/webmcp-remote-assist/main.go
4 lines
tools := webmcp.New(webmcp.WithInstructions(…))
group.Handle(rt, sendInstruction, handler,
  webmcp.WithHTTPMiddleware(requireSupport))
webmcp.WithDocumentScope(supportScope)

WebRTC call rooms

battery/rtc~700 LoC

Anonymous users, rooms by name: the dogfood app for the rtc signaling battery. The lobby trades a display name for a cookie; the room page runs one peer connection per peer with chat on a negotiated data channel, and the Go process relays handshakes only, never media.

  • rtc.New + RegisterPlugin: zero hand-rolled signaling
  • Identity server-derived through Config.Authorize
  • Peer-to-peer camera and chat; the server sees neither
run
cd examples/rtc-call && gofastr dev
View source ↗
examples/rtc-call/main.go
2 lines
sig := rtc.New(rtc.Config{ … }
app.RegisterPlugin(sig)

Four more app specs

gofastr.yml only0 LoC until you generate

Each is one gofastr.yml with no Go beside it. Generate in the directory to get a runnable app you own; every one is validated by the CLI's app spec test suite.

  • lms: courses, lessons, enrollments
  • portfolio: projects and case studies
  • project-manager: projects, tasks, teams
  • real-estate: listings, agents, inquiries
run
cd examples/lms && gofastr generate app gofastr.yml
examples/lms/gofastr.yml
3 lines
# entities, screens, nav, endpoints, seed: one YAML
$ gofastr generate app gofastr.yml
$ go run .