Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: deprecate assigning function to schema in favor of api-party:extend hook #88

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ function nav(): DefaultTheme.NavItem[] {
{ text: 'Cookies', link: '/guide/cookies' },
{ text: 'Retries', link: '/guide/retries' },
{ text: 'Dynamic Backend URL', link: '/guide/dynamic-backend-url' },
{ text: 'Hooks', link: '/guide/hooks' },
],
},
],
Expand Down Expand Up @@ -150,6 +151,7 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] {
{ text: 'Cookies', link: '/guide/cookies' },
{ text: 'Retries', link: '/guide/retries' },
{ text: 'Dynamic Backend URL', link: '/guide/dynamic-backend-url' },
{ text: 'Hooks', link: '/guide/hooks' },
],
},
{ text: 'Migration', link: '/guide/migration' },
Expand Down
4 changes: 2 additions & 2 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Main module configuration for your API endpoints. Each key represents an endpoin
- `headers`: Headers to send with each request (optional)
- `cookies`: Whether to send cookies with each request (optional)
- `allowedUrls`: A list of allowed URLs to change the [backend URL at runtime](/guide/dynamic-backend-url) (optional)
- `schema`: A URL, file path, object, or async function pointing to an [OpenAPI Schema](https://swagger.io/resources/open-api) used to [generate types](/guide/openapi-types) (optional)
- `schema`: A URL, file path, or object representing an [OpenAPI Schema](https://swagger.io/resources/open-api) used to [generate types](/guide/openapi-types) (optional)
- `openAPITS`: [Configuration options](https://openapi-ts.pages.dev/node/#options) for `openapi-typescript`. Options defined here will override the global `openAPITS`

::: info
Expand All @@ -44,7 +44,7 @@ interface ApiEndpoint {
headers?: Record<string, string>
cookies?: boolean
allowedUrls?: string[]
schema?: string | URL | OpenAPI3 | (() => Promise<OpenAPI3>)
schema?: string | URL | OpenAPI3
openAPITS?: OpenAPITSOptions
}

Expand Down
28 changes: 28 additions & 0 deletions docs/guide/hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Hooks

Nuxt API Party provides a number of hooks that can be used to customize the module's behavior. Hooks are functions that are called at specific points in the module's lifecycle. You can use hooks to modify the module's configuration.

For more information on how to work with hooks, see the [Nuxt documentation](https://nuxt.com/docs/guide/going-further/hooks).

## Available Hooks

| Hook name | Arguments | Description |
| ---------- | --------- | ----------- |
| `api-party:extend` | `options` | Called during module initialization after the options have been resolved. Can be used to modify the endpoint configuration. |

## Usage

To use hooks, define them in the `hooks` property of your `nuxt.config.ts` file. The following example demonstrates how to use the `api-party:extend` hook:

```ts
// `nuxt.config.ts`
export default defineNuxtConfig({
modules: ['nuxt-api-party'],

hooks: {
'api-party:extend': async (options) => {
console.log(`Resolved server endpoints:`, options.endpoints)
},
},
})
```
6 changes: 6 additions & 0 deletions playground/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ export default defineNuxtConfig({

compatibilityDate: '2024-04-03',

hooks: {
'api-party:extend': async (options) => {
console.log(`[Build] Resolved endpoints:`, options.endpoints)
},
},

apiParty: {
endpoints: {
jsonPlaceholder: {
Expand Down
9 changes: 8 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { joinURL } from 'ufo'
import { camelCase, pascalCase } from 'scule'
import { createJiti } from 'jiti'
import { addImportsSources, addServerHandler, addTemplate, createResolver, defineNuxtModule, useLogger } from '@nuxt/kit'
import type { HookResult } from '@nuxt/schema'
import type { OpenAPI3, OpenAPITSOptions } from 'openapi-typescript'
import type { QueryObject } from 'ufo'
import { name } from '../package.json'
Expand All @@ -16,7 +17,7 @@ export interface ApiEndpoint {
headers?: Record<string, string>
cookies?: boolean
allowedUrls?: string[]
schema?: string | URL | OpenAPI3 | (() => Promise<OpenAPI3>)
schema?: string | OpenAPI3
openAPITS?: OpenAPITSOptions
}

Expand Down Expand Up @@ -86,6 +87,10 @@ declare module '@nuxt/schema' {
interface RuntimeConfig {
apiParty: ModuleOptions
}

interface NuxtHooks {
'api-party:extend': (options: ModuleOptions) => HookResult
}
}

export default defineNuxtModule<ModuleOptions>({
Expand Down Expand Up @@ -129,6 +134,8 @@ export default defineNuxtModule<ModuleOptions>({

const resolvedOptions = nuxt.options.runtimeConfig.apiParty as Required<ModuleOptions>

nuxt.callHook('api-party:extend', resolvedOptions)

// Write options to public runtime config if client requests are enabled
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore: `client` types are not compatible
Expand Down
20 changes: 14 additions & 6 deletions src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import { useNuxt } from '@nuxt/kit'
import type { OpenAPI3, OpenAPITSOptions } from 'openapi-typescript'
import type { ApiEndpoint } from './module'

/** @deprecated Hooks should be used instead */
type SchemaFn = () => Promise<NonNullable<ApiEndpoint['schema']>>

type SchemaEndpoint = ApiEndpoint & {
schema: NonNullable<ApiEndpoint['schema']> | SchemaFn
}

export async function generateDeclarationTypes(
endpoints: Record<string, ApiEndpoint>,
globalOpenAPIOptions: OpenAPITSOptions,
) {
const resolvedSchemaEntries = await Promise.all(
Object.entries(endpoints)
.filter(([, endpoint]) => Boolean(endpoint.schema))
.filter((entry): entry is [string, SchemaEndpoint] => Boolean(entry[1].schema))
.map(async ([id, endpoint]) => {
const types = await generateSchemaTypes({ id, endpoint, openAPITSOptions: globalOpenAPIOptions })
return [id, types] as const
Expand Down Expand Up @@ -38,14 +45,14 @@ ${normalizeIndentation(types).trimEnd()}

async function generateSchemaTypes(options: {
id: string
endpoint: ApiEndpoint
endpoint: SchemaEndpoint
openAPITSOptions?: OpenAPITSOptions
},
) {
// openapi-typescript < 7 does not have named exports
const openAPITS = await interopDefault(import('openapi-typescript'))
const { astToString } = await import('openapi-typescript')
const schema = await resolveSchema(options.endpoint)
const schema = await resolveSchema(options.id, options.endpoint)

try {
const ast = await openAPITS(schema, {
Expand Down Expand Up @@ -80,12 +87,13 @@ export type operations = Record<string, never>
}
}

async function resolveSchema({ schema }: ApiEndpoint): Promise<string | URL | OpenAPI3> {
async function resolveSchema(id: string, { schema }: SchemaEndpoint): Promise<string | URL | OpenAPI3> {
const nuxt = useNuxt()

if (typeof schema === 'function')
if (typeof schema === 'function') {
console.warn(`[nuxt-api-party] Passing a function to "apiParty.endpoints.${id}.schema" is deprecated. Use a hook instead.`)
return await schema()

}
if (typeof schema === 'string' && !isValidUrl(schema))
return new URL(resolve(nuxt.options.rootDir, schema), import.meta.url)

Expand Down