custom-pages-and-apis — Claude Code skill: EventCatalog pages and APIs
Builds custom Astro pages and TypeScript API routes inside an EventCatalog project, with access to catalog data through a public toolkit.
npx skills add event-catalog/skills --skill custom-pages-and-apis -a claude-codeWhat custom-pages-and-apis does
The skill walks the agent through the job: find the catalog by eventcatalog.config.js, read what is already in pages/, check the prefix in pages.prefix (default custom) and the output mode. Then it writes code against the public toolkit — @catalog/utils getters such as getServices, getDomains and getEvents return catalog entries, while @catalog/layouts/Layout.astro supplies the page shell with header and nav.
Three things get checked before any file is written. You need the Scale plan with a key in EVENTCATALOG_SCALE_LICENSE_KEY; without it the routes are not served at all and you get a 404, with only a warning in the dev server. You need @eventcatalog/core 4.1.0 or later. And API routes require output: "server" in the config — in static mode a production build fails hard, dev merely warns. If there is no Scale key, the skill tells the agent to sort out licensing instead of generating files.
Pages are reachable by URL right away, but most people want them in the menu, so a group goes into navigation.groups. One catch: supplying your own groups replaces the entire default navigation, so built-in items have to be listed again. Avoid importing EventCatalog internals like @components/* or @utils/*. They resolve, but they are not a stable contract and break on any release.
What it helps with
- build a report page listing every service in the catalog
- replace the default catalog homepage
- expose catalog data through a JSON endpoint
- add a dynamic route for a single team by id
- put custom entries into the vertical navigation
Example prompts for the agent
- add a custom page to my EventCatalog with a table of all services
- create an API route at
pages/api/teams/[id].tsthat returns a team by id - wire my pages into the nav with
navigation.groups
How to install custom-pages-and-apis
1Skills CLI (Vercel)
npx skills add event-catalog/skills --skill custom-pages-and-apis -a claude-code2Manually: put the files in place
~/.claude/skills/custom-pages-and-apis/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/custom-pages-and-apis/
1Skills CLI (Vercel)
npx skills add event-catalog/skills --skill custom-pages-and-apis -a codex2Manually: put the files in place
.agents/skills/custom-pages-and-apis/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add event-catalog/skills --skill custom-pages-and-apis -a cursor2Manually: put the files in place
.cursor/skills/custom-pages-and-apis/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install event-catalog/skills custom-pages-and-apis2Manually: put the files in place
.github/skills/custom-pages-and-apis/SKILL.md1Skills CLI (Vercel)
npx skills add event-catalog/skills --skill custom-pages-and-apis -a gemini-cli2Platform CLI
gemini skills install https://github.com/event-catalog/skills --path skills/custom-pages-and-apisManually: put the files in place
.devin/skills/custom-pages-and-apis/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add event-catalog/skills --skill custom-pages-and-apis -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about custom-pages-and-apis
How do I install custom-pages-and-apis in Claude?
Run npx skills add event-catalog/skills --skill custom-pages-and-apis -a claude-code. The skill lands in ~/.claude/skills/custom-pages-and-apis/SKILL.md.
What does custom-pages-and-apis do?
It guides the agent through creating custom Astro pages and TypeScript API routes in an EventCatalog project, wiring them into navigation and setting up editor support.
What do I need before using it?
An EventCatalog Scale plan with the key in EVENTCATALOG_SCALE_LICENSE_KEY, plus @eventcatalog/core 4.1.0 or later. Routes under pages/api/ also need output: "server" in eventcatalog.config.js.
Source text
SKILL.md · MIT
---
name: custom-pages-and-apis
description: Builds custom pages and API routes inside an EventCatalog project (Scale plan feature). Creates Astro pages and TypeScript endpoints in the catalog's pages/ folder using the stable @catalog/layouts and @catalog/utils toolkit, wires them into the navigation, and sets up editor support. Use when user asks to "add a custom page to my catalog", "build a team page", "add an API endpoint to EventCatalog", "create a custom homepage", "add a custom route", "query catalog data from my own page", or "extend EventCatalog with my own pages".
license: MIT
metadata:
author: eventcatalog
version: "1.0.0"
---
# EventCatalog Custom Pages & APIs
Build user-defined pages and API routes that are served by EventCatalog itself — with full access to catalog data (domains, services, events, teams, ...) through a stable, documented toolkit.
Custom pages live in a `pages/` folder at the root of the catalog and are served under a configurable URL prefix (default `/custom`). API routes live in `pages/api/` and require SSR mode.
## Requirements (check these FIRST)
Before writing any files, verify all three. Generating pages that silently 404 is the most common failure mode of this feature.
1. **Scale plan** — custom pages are an [EventCatalog Scale](https://eventcatalog.dev/pricing) feature. The license key must be configured (usually the `EVENTCATALOG_SCALE_LICENSE_KEY` env var / `.env` file). **Without a Scale license the routes are simply not served (404)** — the dev server prints a warning (`Custom pages require the Scale plan`) but pages fail silently otherwise. If the user doesn't have a Scale key, tell them up front instead of generating files — and let them know they can get a **free 30-day trial key from [eventcatalog.cloud](https://eventcatalog.cloud/)**.
2. **Core version** — requires `@eventcatalog/core` **4.1.0 or later**. Check the catalog's `package.json`.
3. **SSR for API routes only** — `.astro` pages work in any output mode, but anything under `pages/api/` requires `output: "server"` in `eventcatalog.config.js`. A production build fails with a hard error if API routes exist in static mode (dev only warns). See [references/api-routes.md](references/api-routes.md).
## Instructions
### Step 1: Locate the catalog
Find the user's EventCatalog project (look for `eventcatalog.config.js`). Then check:
- Does `pages/` already exist? Read what's there — including `pages/homepage.astro`, which is a special file (it replaces the catalog homepage at `/`, not served under the prefix).
- What is the configured prefix? Read `pages.prefix` from `eventcatalog.config.js` (default `custom`). All generated links must use it.
- What output mode is set (`output: "static"` or `"server"`)?
- Does the catalog have a `components/` folder? Files in it are importable as `@catalog/components/*`.
### Step 2: Understand what the user wants to build
Common asks and where they land:
| User wants | File to create | URL |
|---|---|---|
| A content/dashboard page | `pages/reports.astro` | `/custom/reports` |
| A custom homepage | `pages/homepage.astro` | `/` (replaces default homepage) |
| A section with an index | `pages/tools/index.astro` | `/custom/tools` |
| A dynamic page | `pages/reports/[id].astro` | `/custom/reports/:id` |
| A JSON API endpoint | `pages/api/stats.ts` | `/custom/api/stats` |
| A dynamic API endpoint | `pages/api/teams/[id].ts` | `/custom/api/teams/:id` |
| Shared partials/components | `pages/_components/Card.astro` | never routed (underscore prefix) |
Full routing rules (extensions, index handling, underscore convention, reserved prefixes, prefix validation): [references/routing.md](references/routing.md).
### Step 3: Build pages with the toolkit
Always prefer the **stable public toolkit** over EventCatalog internals:
- `@catalog/layouts/Layout.astro` — the EventCatalog page shell (header, nav, optional resource sidebar). Props: `title` (required), `description?`, `sidebar?` (default `true`), `showHeader?` (default `true`).
- `@catalog/utils` — cached collection getters: `getDomains`, `getServices`, `getSystems`, `getEvents`, `getCommands`, `getQueries`, `getFlows`, `getChannels`, `getEntities`, `getAgents`, `getContainers`, `getDataProducts`, `getAdrs`, `getTeams`, `getUsers`, plus `getItemsFromCollectionByIdAndSemverOrLatest` for semver resolution. Pass `{ getAllVersions: false }` to get only latest versions.
- `@catalog/components/*` — the user's own components from their catalog's `components/` folder.
Minimal page:
```astro
---
import Layout from '@catalog/layouts/Layout.astro';
import { getServices } from '@catalog/utils';
const services = await getServices({ getAllVersions: false });
---
<Layout title="Service Report" description="All services in our architecture" sidebar={false}>
<div class="max-w-4xl mx-auto p-8">
<h1 class="text-2xl font-bold text-[rgb(var(--ec-page-text))]">Services ({services.length})</h1>
<ul>
{services.map((service) => (
<li>
<a href={`/docs/services/${service.data.id}/${service.data.version}`}>{service.data.name}</a>
</li>
))}
</ul>
</div>
</Layout>
```
Key conventions when generating page code:
- **Tailwind works out of the box.** For colors, use EventCatalog theme variables so pages respect light/dark mode: `text-[rgb(var(--ec-page-text))]`, `bg-[rgb(var(--ec-page-bg))]`, `border-[rgb(var(--ec-page-border))]`, `bg-[rgb(var(--ec-card-bg))]`, `text-[rgb(var(--ec-accent))]`. Never use `dark:` variants.
- Collection entries are Astro content entries: data is on `entry.data` (e.g. `entry.data.id`, `entry.data.name`, `entry.data.version`, `entry.data.summary`). Entry ids follow `{id}-{version}`.
- Link to catalog docs pages as `/docs/{collection}/{id}/{version}` (e.g. `/docs/services/OrderService/1.0.0`).
- Put shared partials in `pages/_components/` and import them with relative paths.
- Do NOT import from EventCatalog internals (`@components/*`, `@utils/*`, `@layouts/*`). They resolve, but they are not a stable contract and can break on any release. Only `@catalog/*` is public API.
Full toolkit reference (all getter options, Layout props, entry shape): [references/toolkit.md](references/toolkit.md).
Complete working examples (external data fetch, dynamic routes, nav partials, API endpoints): [references/examples.md](references/examples.md).
### Step 4: Wire pages into the navigation
Pages are reachable by URL immediately, but users usually want them in the vertical navigation. Add a group to `navigation.groups` in `eventcatalog.config.js` — remembering that **providing `groups` replaces the entire default nav**, so include the built-ins the user still wants:
```js
navigation: {
groups: [
{ id: 'main', items: [{ id: 'home' }, { id: 'docs' }] },
{ id: 'browse', label: 'Browse', items: [{ id: 'catalog' }, { id: 'schemas' }] },
{
id: 'my-pages',
label: 'My Company',
items: [
{ id: 'reports', label: 'Reports', href: '/custom/reports', icon: 'ChartBar' },
{ id: 'team', label: 'Our Team', href: '/custom/team', icon: 'UsersRound' },
],
},
{ id: 'settings-group', position: 'bottom', items: [{ id: 'settings' }] },
],
}
```
- Built-in item ids: `home`, `docs`, `catalog`, `schemas`, `schema-insights`, `teams`, `users`, `settings`, `studio`.
- Custom items need `href`; `icon` is any [lucide-react](https://lucide.dev/icons) icon name (e.g. `Workflow`, `Github`, `ChartBar`).
- Use `match` (string or array) to keep an item highlighted on child routes, e.g. `match: ['/custom/reports', '/custom/reports/**']`.
- If the config has no `navigation.groups` yet and the user only wants one link added, ask before replacing the default nav — or skip nav wiring and just share the URL.
### Step 5: Editor support (recommended)
VS Code resolves imports from the nearest `tsconfig.json`, so without one the `@catalog/*` imports show "cannot find module" warnings (the build still works — aliases are resolved by EventCatalog at runtime). Create a `tsconfig.json` at the catalog root pointing at the generated `.eventcatalog-core` directory:
```jsonc
{
// Editor support (IntelliSense, go-to-definition) for custom pages.
// These aliases mirror the ones the EventCatalog build provides at runtime.
"compilerOptions": {
"baseUrl": ".",
"allowJs": true,
"jsx": "preserve",
"paths": {
"@catalog/components/*": ["components/*"],
"@catalog/layouts/*": [".eventcatalog-core/src/toolkit/layouts/*"],
"@catalog/utils": [".eventcatalog-core/src/toolkit/utils/index.ts"],
"@catalog/utils/*": [".eventcatalog-core/src/toolkit/utils/*"]
}
},
"include": ["pages/**/*", "components/**/*"]
}
```
Note: `.eventcatalog-core` is generated the first time the dev server runs (`npm run dev`), so the paths resolve after the first run. Tell the user to reload the TS/Astro language server after adding this file.
### Step 6: Verify
1. Start the dev server (`npm run dev`) — or if it's already running, no restart is needed: EventCatalog watches the `pages/` folder and picks up new/removed files automatically (adding/removing a file triggers a brief automatic server restart; edits hot-reload).
2. Open the page at its prefixed URL (e.g. `http://localhost:3000/custom/reports`) and confirm it renders inside the EventCatalog shell.
3. For API routes, curl the endpoint and check the JSON response.
4. If a page 404s, check in order: Scale license configured? Correct prefix in the URL? File has a routable extension (`.astro`, `.ts`, `.js`, `.mjs`)? Not under an underscore-prefixed folder?
## Troubleshooting
| Symptom | Cause |
|---|---|
| All custom pages 404, warning `Custom pages require the Scale plan` | No/invalid Scale license — routes are not injected without it. A free 30-day trial key is available at [eventcatalog.cloud](https://eventcatalog.cloud/) |
| Build fails: API routes require `output: "server"` | `pages/api/**` exists but config is static — set `output: "server"` or remove the API routes |
| Page 404s but others work | Underscore-prefixed path segment, non-routable extension, or wrong prefix |
| `Cannot find module '@catalog/utils'` in editor only | Missing catalog-level `tsconfig.json` (Step 5); build is unaffected |
| Config error: `pages.prefix ... is reserved` | Prefix collides with a core route — see reserved list in [references/routing.md](references/routing.md) |
| Homepage unchanged after adding `pages/homepage.astro` | Requires Scale license; also note homepage is served at `/`, not under the prefix |
## Reference Files
- [references/routing.md](references/routing.md) — file-to-URL mapping, prefix config and validation, reserved prefixes, underscore convention, homepage.astro
- [references/toolkit.md](references/toolkit.md) — `@catalog/layouts`, `@catalog/utils`, `@catalog/components` in full detail
- [references/api-routes.md](references/api-routes.md) — API endpoints, SSR requirement, response patterns
- [references/examples.md](references/examples.md) — complete working examples to adapt
© eventcatalog · MIT · event-catalog/skills