Skip to content

Commit

Permalink
feat: deprecate assigning function to schema in favor of `api-party:e…
Browse files Browse the repository at this point in the history
…xtend` hook (#88)

* feat: deprecate assigning function to schema in favor of hooks

* refactor: use `api-party:extend` hook

* docs: update

---------

Co-authored-by: Johann Schopplich <[email protected]>
  • Loading branch information
mattmess1221 and johannschopplich authored Nov 11, 2024
1 parent da9a2bb commit 4a6af04
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 9 deletions.
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

0 comments on commit 4a6af04

Please sign in to comment.