Skip to content

Commit

Permalink
feat!: always fetch client-side when Nuxt SSR is disabled
Browse files Browse the repository at this point in the history
  • Loading branch information
johannschopplich committed Oct 2, 2023
1 parent 4318bbc commit 46c8d64
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 42 deletions.
33 changes: 21 additions & 12 deletions docs/api/my-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ Returns the raw response of the API endpoint. Intended for actions inside method
interface BaseApiFetchOptions {
/**
* Skip the Nuxt server proxy and fetch directly from the API.
* Requires `allowClient` to be enabled in the module options as well.
* @default false
* Requires `client` set to `true` in the module options.
* @remarks
* If Nuxt SSR is disabled, client-side requests are enabled by default.
*/
client?: boolean
/**
Expand Down Expand Up @@ -113,16 +114,11 @@ const data = await $jsonPlaceholder(
Authorization credentials will be publicly visible. Also, possible CORS issues ahead if the backend is not configured properly.
:::

To fetch data directly from your API, without the Nuxt proxy, set the option `client` to `true`:

```ts{3}
const data = await $jsonPlaceholder(
'posts',
{ client: true }
)
```
::: info
Note: If Nuxt SSR is disabled, all requests are made on the client-side by default.
:::

Requires the `allowClient` option to be `true` in your `apiParty` module configuration:
To fetch data directly from your API and skip the Nuxt server proxy, set the `apiParty` module option `client` to `true`:

```ts{9}
// `nuxt.config.ts`
Expand All @@ -133,7 +129,20 @@ export default defineNuxtConfig({
endpoints: {
// ...
},
allowClient: true
client: true
}
})
```

Now you can make client-side requests by setting the `client` option to `true` in the composable.

```ts{3}
const data = await $jsonPlaceholder(
'posts',
{ client: true }
)
```

::: info
Set the `client` module option to `always` to make all requests on the client-side.
:::
33 changes: 21 additions & 12 deletions docs/api/use-my-api-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ By default, Nuxt waits until a `refresh` is finished before it can be executed a
type BaseUseApiDataOptions<ResT, DataT = ResT> = Omit<AsyncDataOptions<ResT, DataT>, 'watch'> & {
/**
* Skip the Nuxt server proxy and fetch directly from the API.
* Requires `allowClient` to be enabled in the module options as well.
* @default false
* Requires `client` set to `true` in the module options.
* @remarks
* If Nuxt SSR is disabled, client-side requests are enabled by default.
*/
client?: boolean
/**
Expand Down Expand Up @@ -169,16 +170,11 @@ const { data, pending, refresh, error } = await useJsonPlaceholderData('comments
Authorization credentials will be publicly visible. Also, possible CORS issues ahead if the backend is not configured properly.
:::

To fetch data directly from your API, without the Nuxt proxy, set the option `client` to `true`:

```ts{3}
const { data } = await useJsonPlaceholderData(
'comments',
{ client: true }
)
```
::: info
Note: If Nuxt SSR is disabled, all requests are made on the client-side by default.
:::

Requires the `allowClient` option to be `true` in your `apiParty` module configuration:
To fetch data directly from your API and skip the Nuxt server proxy, set the `apiParty` module option `client` to `true`:

```ts{9}
// `nuxt.config.ts`
Expand All @@ -189,7 +185,20 @@ export default defineNuxtConfig({
endpoints: {
// ...
},
allowClient: true
client: true
}
})
```

Now you can make client-side requests by setting the `client` option to `true` in the composable.

```ts{3}
const data = await useJsonPlaceholderData(
'posts',
{ client: true }
)
```

::: info
Set the `client` module option to `always` to make all requests on the client-side.
:::
68 changes: 68 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,71 @@ export default defineNuxtConfig({
## `apiParty.openAPITS`

The global [configuration options](https://openapi-ts.pages.dev/node/#options) for `openapi-typescript`. Options set here will be applied to every endpoint schema, but can be overridden by individual endpoint options.

## Type Declaration

```ts
interface Endpoint {
url: string
token?: string
query?: QueryObject
headers?: Record<string, string>
cookies?: boolean
allowedUrls?: string[]
schema?: string | URL | OpenAPI3 | (() => Promise<OpenAPI3>)
openAPITS?: OpenAPITSOptions
}

interface ModuleOptions {
/**
* API endpoints
*
* @remarks
* Each key represents an endpoint ID, which is used to generate the composables. The value is an object with the following properties:
* - `url`: The URL of the API endpoint
* - `token`: The API token to use for the endpoint (optional)
* - `query`: Query parameters to send with each request (optional)
* - `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](https://nuxt-api-party.byjohann.dev/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)
* - `openAPITS`: [Configuration options](https://openapi-ts.pages.dev/node/#options) for `openapi-typescript`. Options defined here will override the global `openAPITS`
*
* @example
* export default defineNuxtConfig({
* apiParty: {
* endpoints: {
* jsonPlaceholder: {
* url: 'https://jsonplaceholder.typicode.com'
* headers: {
* Authorization: `Basic ${Buffer.from('foo:bar').toString('base64')}`
* }
* }
* }
* }
* })
*
* @default {}
*/
endpoints?: Record<string, Endpoint>

/**
* Allow client-side requests besides server-side ones
*
* @remarks
* By default, API requests are only made on the server-side. This option allows you to make requests on the client-side as well. Keep in mind that this will expose your API credentials to the client.
* Note: If Nuxt SSR is disabled, all requests are made on the client-side by default.
*
* @example
* useJsonPlaceholderData('/posts/1', { client: true })
*
* @default false
*/
client?: boolean | 'allow' | 'always'

/**
* Global options for openapi-typescript
*/
openAPITS?: OpenAPITSOptions
}
```
15 changes: 11 additions & 4 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,14 @@ export interface ModuleOptions {
*
* @remarks
* By default, API requests are only made on the server-side. This option allows you to make requests on the client-side as well. Keep in mind that this will expose your API credentials to the client.
* Note: If Nuxt SSR is disabled, all requests are made on the client-side by default.
*
* @example
* useJsonPlaceholderData('/posts/1', { client: true })
*
* @default false
*/
allowClient?: boolean
client?: boolean | 'allow' | 'always'

/**
* Global options for openapi-typescript
Expand All @@ -79,7 +80,7 @@ export default defineNuxtModule<ModuleOptions>({
},
defaults: {
endpoints: {},
allowClient: false,
client: false,
openAPITS: {},
},
async setup(options, nuxt) {
Expand All @@ -100,12 +101,18 @@ export default defineNuxtModule<ModuleOptions>({
options,
)

if (!nuxt.options.ssr) {
logger.info('SSR is disabled, enabling Nuxt API Party client requests by default.')
options.client = 'always'
}

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

// Write options to public runtime config if client requests are enabled
// @ts-expect-error: `client` types are not compatible
nuxt.options.runtimeConfig.public.apiParty = defu(
nuxt.options.runtimeConfig.public.apiParty,
resolvedOptions.allowClient
resolvedOptions.client
? resolvedOptions
: {
// Only expose cookies endpoint option to the client
Expand All @@ -114,7 +121,7 @@ export default defineNuxtModule<ModuleOptions>({
([endpointId, endpoint]) => [endpointId, { cookies: endpoint.cookies }],
),
),
allowClient: false,
client: false,
},
)

Expand Down
15 changes: 8 additions & 7 deletions src/runtime/composables/$api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { useNuxtApp, useRequestHeaders, useRuntimeConfig } from '#imports'
export interface BaseApiFetchOptions {
/**
* Skip the Nuxt server proxy and fetch directly from the API.
* Requires `allowClient` to be enabled in the module options as well.
* Requires `client` set to `true` in the module options.
* @remarks
* If Nuxt SSR is disabled, client-side requests are enabled by default.
* @default false
*/
client?: boolean
Expand Down Expand Up @@ -59,7 +61,7 @@ export function _$api<T = any>(
opts: ApiFetchOptions & BaseApiFetchOptions = {},
) {
const nuxt = useNuxtApp()
const { apiParty } = useRuntimeConfig().public
const apiParty = useRuntimeConfig().public.apiParty as unknown as Required<ModuleOptions>
const promiseMap = (nuxt._promiseMap = nuxt._promiseMap || new Map()) as Map<string, Promise<T>>

const {
Expand All @@ -68,7 +70,7 @@ export function _$api<T = any>(
headers,
method,
body,
client = false,
client = apiParty.client === 'always',
cache = false,
key,
...fetchOptions
Expand All @@ -85,17 +87,16 @@ export function _$api<T = any>(
])
: CACHE_KEY_PREFIX + key

if (client && !apiParty.allowClient)
throw new Error('Client-side API requests are disabled. Set "allowClient: true" in the module options to enable them.')
if (client && !apiParty.client)
throw new Error('Client-side API requests are disabled. Set "client: true" in the module options to enable them.')

if ((nuxt.isHydrating || cache) && _key in nuxt.payload.data)
return Promise.resolve(nuxt.payload.data[_key])

if (promiseMap.has(_key))
return promiseMap.get(_key)!

const endpoints = (apiParty as unknown as ModuleOptions).endpoints || {}
const endpoint = endpoints[endpointId]
const endpoint = (apiParty.endpoints || {})[endpointId]

const clientFetcher = () => globalThis.$fetch<T>(resolvePath(path, pathParams), {
...fetchOptions,
Expand Down
15 changes: 8 additions & 7 deletions src/runtime/composables/useApiData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ type ComputedOptions<T extends Record<string, any>> = {
export type BaseUseApiDataOptions<ResT, DataT = ResT> = Omit<AsyncDataOptions<ResT, DataT>, 'watch'> & {
/**
* Skip the Nuxt server proxy and fetch directly from the API.
* Requires `allowClient` to be enabled in the module options as well.
* Requires `client` set to `true` in the module options.
* @remarks
* If Nuxt SSR is disabled, client-side requests are enabled by default.
* @default false
*/
client?: boolean
Expand Down Expand Up @@ -95,7 +97,7 @@ export function _useApiData<T = any>(
path: MaybeRefOrGetter<string>,
opts: UseApiDataOptions<T> = {},
) {
const { apiParty } = useRuntimeConfig().public
const apiParty = useRuntimeConfig().public.apiParty as unknown as Required<ModuleOptions>
const {
server,
lazy,
Expand All @@ -109,7 +111,7 @@ export function _useApiData<T = any>(
headers,
method,
body,
client = false,
client = apiParty.client === 'always',
cache = true,
key,
...fetchOptions
Expand All @@ -127,11 +129,10 @@ export function _useApiData<T = any>(
: () => CACHE_KEY_PREFIX + toValue(key),
)

if (client && !apiParty.allowClient)
throw new Error('Client-side API requests are disabled. Set "allowClient: true" in the module options to enable them.')
if (client && !apiParty.client)
throw new Error('Client-side API requests are disabled. Set "client: true" in the module options to enable them.')

const endpoints = (apiParty as unknown as ModuleOptions).endpoints || {}
const endpoint = endpoints[endpointId]
const endpoint = (apiParty.endpoints || {})[endpointId]

const _fetchOptions = reactive(fetchOptions)

Expand Down

0 comments on commit 46c8d64

Please sign in to comment.