From bf02804bb70bd252efdc619ff9efaf0196aeeee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20B=C3=A4rtschi?= Date: Fri, 19 May 2023 17:02:49 +0200 Subject: [PATCH] feat: allow passing on cookies and change backend url by header (#21) * feat(cookies): pass on cookies if configured so * feat(custom_url): allow to change backend url by custom header * docs: added cookies and url header documentation * fix: reactive headers in useApiData * fix(server): pass cookie as header * fix(server): pass cookie as header after endpoint.headers * refactor: pass cookies only for SSR requests --------- Co-authored-by: Johann Schopplich --- docs/.vitepress/config.ts | 4 ++++ docs/config/index.md | 2 ++ docs/guide/cookies.md | 25 +++++++++++++++++++++++++ docs/guide/dynamic-backend-url.md | 20 ++++++++++++++++++++ src/module.ts | 2 ++ src/runtime/composables/$api.ts | 7 +++++-- src/runtime/composables/useApiData.ts | 5 ++++- src/runtime/server.ts | 7 ++++++- 8 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 docs/guide/cookies.md create mode 100644 docs/guide/dynamic-backend-url.md diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 26686d0..a7b3281 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -80,6 +80,8 @@ function nav(): DefaultTheme.NavItem[] { items: [ { text: 'Hydration', link: '/guide/hydration' }, { text: 'Caching', link: '/guide/caching' }, + { text: 'Cookies', link: '/guide/cookies' }, + { text: 'Dynamic Backend URL', link: '/guide/dynamic-backend-url' }, ], }, ], @@ -137,6 +139,8 @@ function sidebarGuide(): DefaultTheme.SidebarItem[] { items: [ { text: 'Hydration', link: '/guide/hydration' }, { text: 'Caching', link: '/guide/caching' }, + { text: 'Cookies', link: '/guide/cookies' }, + { text: 'Dynamic Backend URL', link: '/guide/dynamic-backend-url' }, ], }, { diff --git a/docs/config/index.md b/docs/config/index.md index 3788008..b68b285 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -23,6 +23,7 @@ Main module configuration for your API endpoints. Each key represents an endpoin - `token`: The API token to use for the endpoint (optional) - `query`: Query parameters to send with the each request (optional) - `headers`: Headers to send with each request (optional) +- `cookies`: Whether to send cookies with each request (optional) ::: info The composables are generated based on your API endpoint ID. For example, if you were to call an endpoint `jsonPlaceholder`, the composables will be called `useJsonPlaceholderData` and `$jsonPlaceholder`. @@ -40,6 +41,7 @@ type ApiPartyEndpoints = Record< token?: string query?: QueryObject headers?: Record + cookies?: boolean } > | undefined ``` diff --git a/docs/guide/cookies.md b/docs/guide/cookies.md new file mode 100644 index 0000000..9b69065 --- /dev/null +++ b/docs/guide/cookies.md @@ -0,0 +1,25 @@ +# Cookies + +Sometimes your authorization token is stored in a cookie (e.g. coming from SSO). In this case, you can set `cookies` to `true` in your endpoint configuration to send cookies with each request. + +## Examples + +::: info +The examples below assume that you have set up an API endpoint called `jsonPlaceholder`. The API endpoint is authorized by a cookie which is provided by an external SSO service. To pass on the cookie provided by the external SSO, you can enable `cookies` in your endpoint configuration. +::: + +```ts +// `nuxt.config.ts` +export default defineNuxtConfig({ + modules: ['nuxt-api-party'], + + apiParty: { + endpoints: { + jsonPlaceholder: { + url: 'https://jsonplaceholder.typicode.com', + cookies: true + } + } + } +}) +``` diff --git a/docs/guide/dynamic-backend-url.md b/docs/guide/dynamic-backend-url.md new file mode 100644 index 0000000..4e75d46 --- /dev/null +++ b/docs/guide/dynamic-backend-url.md @@ -0,0 +1,20 @@ +# Dynamic Backend URL + +If you need to change the backend URL at runtime, you can do so by using a custom header based on the endpoint name. This is useful for example when you have a multi-tenant application where each tenant has its own backend URL. + +## Example + +::: info +The examples below assume that you have set up an API endpoint called `jsonPlaceholder`. In this case you can use the `JSON_PLACEHOLDER_ENDPOINT_URL` header to change the backend URL at runtime. +::: + +```ts +const { data } = await useJsonPlaceholderData( + 'comments', + { + headers: { + JSON_PLACEHOLDER_ENDPOINT_URL: 'https://jsonplaceholder-v2.typicode.com' + } + } +) +``` diff --git a/src/module.ts b/src/module.ts index 7473948..da1f89c 100644 --- a/src/module.ts +++ b/src/module.ts @@ -13,6 +13,7 @@ export interface ModuleOptions { * - `token`: The API token to use for the endpoint (optional) * - `query`: Query parameters to send with the each request (optional) * - `headers`: Headers to send with each request (optional) + * - `cookies`: Whether to send cookies with each request (optional) * * @example * export default defineNuxtConfig({ @@ -37,6 +38,7 @@ export interface ModuleOptions { token?: string query?: QueryObject headers?: Record + cookies?: boolean } > diff --git a/src/runtime/composables/$api.ts b/src/runtime/composables/$api.ts index 64c2118..041e0af 100644 --- a/src/runtime/composables/$api.ts +++ b/src/runtime/composables/$api.ts @@ -4,7 +4,7 @@ import { headersToObject, serializeMaybeEncodedBody } from '../utils' import { isFormData } from '../formData' import type { ModuleOptions } from '../../module' import type { EndpointFetchOptions } from '../utils' -import { useNuxtApp, useRuntimeConfig } from '#imports' +import { useNuxtApp, useRequestHeaders, useRuntimeConfig } from '#imports' export type ApiFetchOptions = Omit, 'body' | 'cache'> & { body?: string | Record | FormData | null @@ -78,7 +78,10 @@ export function _$api( body: { path, query, - headers: headersToObject(headers), + headers: { + ...headersToObject(headers), + ...useRequestHeaders(['cookie']), + }, method, body: await serializeMaybeEncodedBody(body), } satisfies EndpointFetchOptions, diff --git a/src/runtime/composables/useApiData.ts b/src/runtime/composables/useApiData.ts index df13490..bdde21a 100644 --- a/src/runtime/composables/useApiData.ts +++ b/src/runtime/composables/useApiData.ts @@ -81,7 +81,10 @@ export function _useApiData( const _endpointFetchOptions: EndpointFetchOptions = reactive({ path: _path, query, - headers: computed(() => headersToObject(toValue(headers))), + headers: computed(() => ({ + ...headersToObject(toValue(headers)), + ...useRequestHeaders(['cookie']), + })), method, body, }) diff --git a/src/runtime/server.ts b/src/runtime/server.ts index 589288d..daabd82 100644 --- a/src/runtime/server.ts +++ b/src/runtime/server.ts @@ -34,18 +34,23 @@ export default defineEventHandler(async (event): Promise => { ...fetchOptions } = _body + // Allows to overwrite the backend url with a custom header + // (e.g. `jsonPlaceholder` endpoint becomes `JSON_PLACEHOLDER_ENDPOINT_URL`) + const baseURL = new Headers(headers).get(`${endpointId}_endpoint_url`) || endpoint.url + try { return await $fetch( path!, { ...fetchOptions, - baseURL: endpoint.url, + baseURL, query: { ...endpoint.query, ...query, }, headers: { ...(endpoint.token && { Authorization: `Bearer ${endpoint.token}` }), + ...(endpoint.cookies && { cookie: getRequestHeader(event, 'cookie') }), ...endpoint.headers, ...headers, },