Composables
Use these functions in Nitro server code. Enable a database, directly or through runtime sync, before querying stored pages.
Nuxt auto-imports queryPages, searchPages, countPages, streamPages, indexPage, and indexPageByRoute.
Other exports need an explicit #ai-ready/server import.
queryPages
Type: (event?: H3Event, options?: QueryPagesOptions) => Promise<PageEntry[] | PageData[]>
Without route, returns a page array. With route, returns one page or undefined.
Set includeMarkdown: true to include the Markdown body. List queries exclude error pages by default.
import { queryPages } from '#ai-ready/server'
export default defineEventHandler(async (event) => {
return queryPages(event, { limit: 20 })
})Single page lookup:
export default defineEventHandler(async (event) => {
const { path } = getQuery(event)
if (typeof path !== 'string' || !path.startsWith('/'))
throw createError({ statusCode: 400, statusMessage: 'Provide a page path starting with /.' })
const page = await queryPages(event, { route: path, includeMarkdown: true })
if (!page)
throw createError({ statusCode: 404, statusMessage: 'Page not found.' })
return page
})QueryPagesOptions:
| Option | Type | Description |
|---|---|---|
route | string | Get single page by route |
includeMarkdown | boolean | Include markdown content |
where.pending | boolean | Filter by indexing status |
where.hasError | boolean | Filter by error status |
where.source | 'prerender' | 'runtime' | Filter by source |
limit | number | Max pages to return |
offset | number | Skip first N pages |
PageEntry:
| Property | Type | Description |
|---|---|---|
route | string | Page route (e.g., /about) |
title | string | Page title |
description | string | Meta description |
headings | Array<Record<string, string>> | Parsed headings, such as [{ h1: "Title" }] |
keywords | string[] | Extracted keywords |
updatedAt | string | ISO timestamp |
isError | boolean | Whether the stored page is an error page |
locale | string | Stored locale, or an empty string |
PageData: Extends PageEntry with markdown: string.
searchPages
Type: (event: H3Event, query: string, options?: SearchPagesOptions) => Promise<SearchResult[]>
SQLite-compatible databases use FTS5 across title, description, route, headings, keywords, and Markdown. PostgreSQL uses case-insensitive matching across title, description, headings, and Markdown. Search returns an empty array during development and prerendering.
import { searchPages } from '#ai-ready/server'
export default defineEventHandler(async (event) => {
const { q } = getQuery(event)
if (typeof q !== 'string' || !q.trim())
return []
return searchPages(event, q, { limit: 10 })
})SearchResult:
| Property | Type | Description |
|---|---|---|
route | string | Page route |
title | string | Page title |
description | string | Meta description |
score | number | FTS5 BM25 score, lower ranks first; PostgreSQL returns 0 |
countPages
Type: (event?: H3Event, options?: CountPagesOptions) => Promise<number>
Count pages matching criteria.
import { countPages } from '#ai-ready/server'
export default defineEventHandler(async (event) => {
const total = await countPages(event)
const pending = await countPages(event, { where: { pending: true } })
return { total, pending, indexed: total - pending }
})streamPages
Type: (event?: H3Event, options?: StreamPagesOptions) => AsyncGenerator<PageData>
Read pages in batches using cursor-based pagination. This does not stream the HTTP response by itself. The example below returns only a count, so it does not collect every Markdown body in memory.
import { streamPages } from '#ai-ready/server'
export default defineEventHandler(async (event) => {
let withMarkdown = 0
for await (const page of streamPages(event, { batchSize: 50 })) {
if (page.markdown)
withMarkdown++
}
return { withMarkdown }
})indexPage
Type: (route: string, html: string, options?: IndexPageOptions, event?: H3Event) => Promise<IndexPageResult>
Index HTML you already trust. Call this from your authenticated publishing flow:
import type { H3Event } from 'h3'
import { indexPage } from '#ai-ready/server'
export function indexAbout(html: string, event: H3Event) {
return indexPage('/about', html, { force: true }, event)
}Database, conversion, or hook failures can throw. A successful write returns success: true.
The indexPageByRoute wrapper converts fetch and indexing failures into a result with success: false.
Options:
| Option | Type | Default | Description |
|---|---|---|---|
ttl | number | config value | Override TTL check |
force | boolean | false | Re-index even if fresh |
skipHook | boolean | false | Skip ai-ready:page:indexed hook |
Result:
| Property | Type | Description |
|---|---|---|
success | boolean | Whether indexing succeeded |
skipped | boolean | true if page was fresh |
isUpdate | boolean | true if updating existing entry |
contentChanged | boolean | true if content hash differs |
data | object | Page data if successful; its headings field contains serialized JSON |
error | string | Error message if failed |
indexPageByRoute
Type: (route: string, event: H3Event | undefined, options?: IndexPageOptions) => Promise<IndexPageResult>
Fetch HTML and index it in one call. Pass the current request event:
import type { H3Event } from 'h3'
import { indexPageByRoute } from '#ai-ready/server'
export function refreshAbout(event: H3Event) {
return indexPageByRoute('/about', event, { force: true })
}Call this helper from an authenticated publishing flow. For HTTP triggers, use the module's authenticated reindex endpoint.
useDatabase
The current export is useRawDb. useDatabase is not exported.
Use an explicit import for raw SQL access:
Type: (event?: H3Event) => Promise<RawExecutor>
import { useRawDb } from '#ai-ready/server'
export default defineEventHandler(async (event) => {
const db = await useRawDb(event)
return db.all('SELECT route, title FROM ai_ready_pages WHERE indexed = 1 AND is_error = 0 LIMIT 20')
})Prefer queryPages when it covers the query. Raw SQL depends on the database schema.
Data Sources
| Context | Source |
|---|---|
| Development | Query helpers return empty results |
| Prerender | Temporary SQLite database; full-text search is unavailable |
| Runtime | Configured SQLite, Bun, D1, LibSQL, Neon, or PostgreSQL database |
A database-backed feature must enable storage. See database configuration.