quickstart

Before you start

This walkthrough uses Next.js (Next 16 proxy.ts; the identical code works in a Next ≤15 middleware.ts). You need your product data reachable in-process or via a fast cache: the gateway sits on the edge hot path (p95 < 50ms compute budget), so source resolvers must not do slow origin round-trips.

On a different stack? The config in steps 2–4 is identical everywhere; only the mounting changes. See the Shopify, Node (Express/Fastify), and Cloudflare/edge adapter pages.

Installing with an agent? Start here

Copy this into Claude Code, Cursor, Copilot, or whatever you build with. It covers the serving half of this page, and it front-loads the two things that are easy to get wrong and hard to notice later: passing the fallthrough, and checking every URL you wired rather than the first one that worked.

Install @rebilder/gateway in this repository.

WHAT IT DOES

It is middleware. When an AI agent requests one of our URLs with Accept: text/markdown, it answers with a clean markdown rendering of that page's facts. Every other request falls through to the existing pipeline untouched.

STEPS

  1. Read the README of the installed package before writing anything. Do not work from memory of this prompt; the API is in node_modules/@rebilder/gateway/README.md.
  2. Add the dependency with the package manager this repo already uses.
  3. Create a gateway config exporting a GatewayConfig. Its sources resolvers map a URL to our own data. There are five source kinds and every page on this site is one of them.
  4. Pass the existing fallthrough into the adapter rather than calling it yourself.
  5. Add a match router to sources if you can: one pure, synchronous function from URL to source kind.

IMPLEMENTATION REQUIREMENTS

WHEN YOU ARE DONE

Tell me which URL patterns you wired and which you deliberately skipped. Then run this against one real URL of EACH kind you wired.

1. Install

npm install @rebilder/gateway        # pnpm add / yarn add

2. Wire your source of truth

The gateway renders from your data and never invents content. Give it lookup functions from a URL to whatever that URL is: a product, a policy, a priced listing, a document, or a collection.

// lib/catalog.ts: your source of truth, typed with the gateway's re-exported types
import type { ProductSource, PolicySource } from '@rebilder/gateway'
const PRODUCTS: Record<string, ProductSource> = {
  '/products/alpine-trail-pack-28l': {
    url: 'https://store.example.com/products/alpine-trail-pack-28l',
    title: 'Alpine Trail Pack 28L',
    brand: 'Basecamp Supply Co',
    description: 'A 28-liter technical daypack: suspended-mesh back panel, magnetic sternum buckle, 3L hydration sleeve.',
    price: { amount: 14800, currency: 'USD' },
    availability: 'in_stock',
    variants: [
      { id: 'BSC-ATP28-JUN', title: 'Juniper Green', price: { amount: 14800, currency: 'USD' }, availability: 'in_stock', options: { Color: 'Juniper Green' } },
      { id: 'BSC-ATP28-EMB', title: 'Ember Orange', price: { amount: 14800, currency: 'USD' }, availability: 'backorder', options: { Color: 'Ember Orange' } },
    ],
    shipping: {
      summary: 'Free US shipping on orders over $75; standard shipping $6.95.',
      freeThreshold: { amount: 7500, currency: 'USD' },
      regions: ['US', 'CA'],
      etaDays: [3, 6],
    },
    returns: {
      summary: '60-day returns.',
      windowDays: 60,
      url: 'https://store.example.com/policies/returns',
    },
    images: [
      { url: 'https://store.example.com/cdn/atp28-front.jpg', alt: 'Alpine Trail Pack 28L, front' },
    ],
    attributes: { Capacity: '28 L', Weight: '1.12 kg (2.47 lb)' },
  },
}

export function getProduct(pathname: string): ProductSource | null {
  return PRODUCTS[pathname] ?? null // null = "not a product page";
}

3. Create the gateway config

// lib/gateway-config.ts: wire the gateway to your source of truth
import type { GatewayConfig } from '@rebilder/gateway'
import { getProduct, getPolicies, getCollection } from './catalog'
export const gatewayConfig: GatewayConfig = {
  storeId: 'store_123',
  sources: {
    product:  (url) => getProduct(url.pathname),
    policies: (url) => getPolicies(url.pathname),
    catalog:  (url) => getCollection(url.pathname),
    document: (url) => getDocument(url.pathname),
    collection: (url) => getDocumentIndex(url.pathname),
  }
}

4. Wire the event sink

npm install @rebilder/events        # pnpm add / yarn add

5. Add the proxy

// proxy.ts on Next 16
import { NextResponse } from 'next/server'
import { createGatewayProxy } from '@rebilder/gateway/next'
const gateway = createGatewayProxy(gatewayConfig, () => NextResponse.next())
export default gateway

6. Verify with curl

# 1. What an agent gets: clean markdown
curl -si -H 'Accept: text/markdown' https://trymumm.com/trust/refunds
# 2. What a browser (or Googlebot) gets: the canonical HTML
curl -si https://trymumm.com/trust/refunds

7. Check every URL you wired, not the first one

npx rebilder diff https://example.com/services/consulting
npx rebilder check https://example.com/services/consulting