Skip to content

Commit

Permalink
fix!: strict dynamic backend urls
Browse files Browse the repository at this point in the history
  • Loading branch information
johannschopplich committed Jun 27, 2023
1 parent d5e7110 commit 481872f
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 2 deletions.
2 changes: 2 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Main module configuration for your API endpoints. Each key represents an endpoin
- `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](/guide/dynamic-backend-url) (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`.
Expand All @@ -42,6 +43,7 @@ type ApiPartyEndpoints = Record<
query?: QueryObject
headers?: Record<string, string>
cookies?: boolean
allowedUrls?: string[]
}
> | undefined
```
Expand Down
18 changes: 18 additions & 0 deletions docs/guide/dynamic-backend-url.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

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.

To prevent leaking sensitive data you have to specify a list of allowed backend URLs for each endpoint in the `allowedUrls` option. If the dynamic backend URL is not in the array, the request will be rejected.

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

apiParty: {
endpoints: {
jsonPlaceholder: {
url: 'https://jsonplaceholder.typicode.com',
allowedUrls: ['https://jsonplaceholder-v2.typicode.com']
}
}
}
})
```

## Example

::: info
Expand Down
2 changes: 2 additions & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface ModuleOptions {
* - `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 (optional)
*
* @example
* export default defineNuxtConfig({
Expand All @@ -39,6 +40,7 @@ export interface ModuleOptions {
query?: QueryObject
headers?: Record<string, string>
cookies?: boolean
allowedUrls?: string[]
}
>

Expand Down
15 changes: 13 additions & 2 deletions src/runtime/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,20 @@ export default defineEventHandler(async (event): Promise<any> => {
// (e.g. `jsonPlaceholder` endpoint becomes `Json-Placeholder-Endpoint-Url`)
const baseURL = new Headers(headers).get(`${endpointId}-endpoint-url`) || endpoint.url

// Check if the base URL is in the allow list
if (
baseURL !== endpoint.url
&& !endpoint.allowedUrls?.includes(baseURL)
) {
throw createError({
statusCode: 400,
statusMessage: `Base URL "${baseURL}" is not allowed`,
})
}

try {
return await $fetch(
path!,
return await globalThis.$fetch(
path,
{
...fetchOptions,
baseURL,
Expand Down

0 comments on commit 481872f

Please sign in to comment.