diff --git a/src/runtime/internal/event.ts b/src/runtime/internal/event.ts new file mode 100644 index 000000000..e11b5bffb --- /dev/null +++ b/src/runtime/internal/event.ts @@ -0,0 +1,18 @@ +import type { H3Event } from 'h3' + +/** + * The event a `nuxt/server` handler receives (Nuxt 4.6+). + * Declared here instead of imported, `nuxt/server` does not resolve on older Nuxt versions. + */ +export interface RequestEvent { + readonly req: Request + url: URL + readonly res: { + status?: number + statusText?: string + readonly headers: Headers + } + readonly context: Record +} + +export type ContentEvent = H3Event | RequestEvent diff --git a/src/runtime/nitro.ts b/src/runtime/nitro.ts index 1ab6b7af4..d335f4f9c 100644 --- a/src/runtime/nitro.ts +++ b/src/runtime/nitro.ts @@ -1,6 +1,6 @@ import * as server from './server' import type { Collections, CollectionQueryBuilder } from '@nuxt/content' -import type { H3Event } from 'h3' +import type { ContentEvent } from './internal/event' /** * `@nuxt/content/nitro` import is deprecated and will be removed in the next major version. @@ -10,7 +10,7 @@ import type { H3Event } from 'h3' /** * @deprecated Import from `@nuxt/content/server` instead */ -export const queryCollection = (event: H3Event, collection: T): CollectionQueryBuilder => { +export const queryCollection = (event: ContentEvent, collection: T): CollectionQueryBuilder => { return server.queryCollection(event, collection) } diff --git a/src/runtime/server.ts b/src/runtime/server.ts index aa94b7029..3acf51e5b 100644 --- a/src/runtime/server.ts +++ b/src/runtime/server.ts @@ -1,4 +1,5 @@ import type { H3Event } from 'h3' +import type { ContentEvent } from './internal/event' import { collectionQueryBuilder } from './internal/query' import { generateNavigationTree } from './internal/navigation' import { generateItemSurround } from './internal/surround' @@ -7,6 +8,7 @@ import { fetchQuery } from './internal/api' import type { Collections, CollectionQueryBuilder, PageCollections, SurroundOptions, SQLOperator, QueryGroupFunction } from '@nuxt/content' export type { GenerateSearchSectionsOptions, Section } from './internal/search' +export type { ContentEvent, RequestEvent } from './internal/event' interface ChainablePromise extends Promise { where(field: keyof PageCollections[T] | string, operator: SQLOperator, value?: unknown): ChainablePromise @@ -15,37 +17,37 @@ interface ChainablePromise extends Promise } -export const queryCollection = (event: H3Event, collection: T): CollectionQueryBuilder => { - return collectionQueryBuilder(collection, (collection, sql) => fetchQuery(event, collection, sql)) +export const queryCollection = (event: ContentEvent, collection: T): CollectionQueryBuilder => { + return collectionQueryBuilder(collection, (collection, sql) => fetchQuery(event as H3Event, collection, sql)) } -export function queryCollectionNavigation(event: H3Event, collection: T, fields?: Array) { +export function queryCollectionNavigation(event: ContentEvent, collection: T, fields?: Array) { return chainablePromise(event, collection, qb => generateNavigationTree(qb, fields)) } -export function queryCollectionItemSurroundings(event: H3Event, collection: T, path: string, opts?: SurroundOptions) { +export function queryCollectionItemSurroundings(event: ContentEvent, collection: T, path: string, opts?: SurroundOptions) { return chainablePromise(event, collection, qb => generateItemSurround(qb, path, opts)) } export function queryCollectionSearchSections( - event: H3Event, + event: ContentEvent, collection: T, opts: Omit & { extraFields: K[] }, ): ChainablePromise>> export function queryCollectionSearchSections( - event: H3Event, + event: ContentEvent, collection: T, opts?: GenerateSearchSectionsOptions, ): ChainablePromise export function queryCollectionSearchSections( - event: H3Event, + event: ContentEvent, collection: T, opts?: GenerateSearchSectionsOptions, ) { return chainablePromise(event, collection, qb => generateSearchSections(qb, opts)) } -function chainablePromise(event: H3Event, collection: T, fn: (qb: CollectionQueryBuilder) => Promise) { +function chainablePromise(event: ContentEvent, collection: T, fn: (qb: CollectionQueryBuilder) => Promise) { const queryBuilder = queryCollection(event, collection) const chainable: ChainablePromise = { diff --git a/test/basic.test.ts b/test/basic.test.ts index 0610dff5e..c70a1883b 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -112,5 +112,13 @@ describe('basic', async () => { expect(doc.arrayField).toContain('item2') expect(doc.numberField).toBe(1) }) + + test('retrieve document from a `nuxt/server` handler', async () => { + const doc = await $fetch('/api/content/get?path=/') + const { content, navigation } = await $fetch<{ content: unknown, navigation: unknown[] }>('/api/content/nuxt-server?path=/') + + expect(content).toEqual(doc) + expect(navigation.length).toBeGreaterThan(0) + }) }) }) diff --git a/test/fixtures/basic/server/api/content/nuxt-server.get.ts b/test/fixtures/basic/server/api/content/nuxt-server.get.ts new file mode 100644 index 000000000..126478ebb --- /dev/null +++ b/test/fixtures/basic/server/api/content/nuxt-server.get.ts @@ -0,0 +1,10 @@ +import { defineEventHandler, getQuery } from 'nuxt/server' + +export default defineEventHandler(async (event) => { + const path = getQuery<{ path?: string }>(event).path || '/' + + const content = await queryCollection(event, 'content').path(path).first() + const navigation = await queryCollectionNavigation(event, 'content') + + return { content, navigation } +})