
nuxt-api-party

This module enables you to securely fetch data from any API by proxying the request in a Nuxt server route. Composable names are dynamic – given jsonPlaceholder
set as the module option name
in your Nuxt config, the composables $jsonPlaceholder
and useJsonPlaceholderData
will be available globally.
Features
Setup
pnpm add -D nuxt-api-party
npm i -D nuxt-api-party
How It Works
The generated composables will initiate a POST request to the Nuxt server route /api/__api_party
, which then fetches the actual data for a given route from your API and passes the response back to the template/client. This proxy behavior has the benefit of omitting CORS issues, since data is sent from server to server.
During server-side rendering, calls to the Nuxt server route will directly call the relevant function (emulating the request), saving an additional API call.
ℹ️ Responses are cached and hydrated to the client. Subsequent calls will return cached responses, saving duplicated requests.
Basic Usage
Add this module nuxt-api-party
to your Nuxt config and prepare your first API connection by setting an endpoint object with the following properties for the apiParty
module option:
export default defineNuxtConfig({
modules: ['nuxt-api-party'],
apiParty: {
endpoints: {
jsonPlaceholder: {
url: process.env.JSON_PLACEHOLDER_API_BASE_URL!,
token: process.env.JSON_PLACEHOLDER_API_TOKEN!,
headers: {
'X-Foo': 'bar'
}
}
}
}
})
If you were to call your API jsonPlaceholder
, the generated composables are:
Use the composables in your templates or components:
<script setup lang="ts">
const { data, pending, refresh, error } = await useJsonPlaceholderData('posts/1')
</script>
<template>
<h1>{{ data?.title }}</h1>
<pre>{{ JSON.stringify(data, undefined, 2) }}</pre>
</template>
ℹ️ You can connect as many APIs as you want, just add them to the endpoints
object.
Runtime Config
Instead of the apiParty
module option, you can also use the runtime config to set your API endpoints:
export default defineNuxtConfig({
modules: ['nuxt-api-party'],
runtimeConfig: {
apiParty: {
endpoints: {
jsonPlaceholder: {
url: '',
token: ''
}
}
}
}
})
Leveraging automatically replaced public runtime config values by matching .env
variables at runtime, set your desired option in your project's .env
file:
NUXT_API_PARTY_ENDPOINTS_JSON_PLACEHOLDER_URL=https://jsonplaceholder.typicode.com
NUXT_API_PARTY_ENDPOINTS_JSON_PLACEHOLDER_TOKEN=
Singular API Endpoint
export default defineNuxtConfig({
modules: ['nuxt-api-party'],
apiParty: {
name: 'jsonPlaceholder'
}
})
Set the following environment variables in your project's .env
file:
API_PARTY_BASE_URL=https://jsonplaceholder.typicode.com
Module Options
Option | Type | Description |
---|
name
|
string | undefined
|
API name used for composables
For example, if you set it to foo , the composables will be called $foo and useFooData .
Default value: undefined
|
url
|
string | undefined
|
API base URL
For example, if you set it to foo , the composables will be called $foo and useFooData .
Default value: process.env.API_PARTY_BASE_URL
|
token
|
string | undefined
|
Optional API token for bearer authentication
You can set a custom header with the headers module option instead.
Default value: process.env.API_PARTY_TOKEN
|
query
|
Record<string, QueryValue | QueryValue[]> | undefined
|
Custom query parameters sent with every request to the API
Default value: undefined
|
headers
|
Record<string, string> | undefined
|
Custom headers sent with every request to the API
Add authorization headers if you want to use a custom authorization method.
Default value: undefined
Example:
const username = 'foo'
const password = 'bar'
export default defineNuxtConfig({
apiParty: {
headers: {
'Custom-Api-Header': 'foo',
'Authorization': `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`
}
}
})
|
endpoints
|
type ApiPartyEndpoints = Record<
string,
{
url: string
token?: string
query?: QueryObject
headers?: Record<string, string>
}
> | undefined
|
Multiple API endpoints
This will create multiple API composables for the given endpoint configurations. You can keep the default endpoint as well.
Default value: {}
Example:
export default defineNuxtConfig({
apiParty: {
endpoints: {
jsonPlaceholder: {
url: process.env.JSON_PLACEHOLDER_API_BASE_URL!,
token: process.env.JSON_PLACEHOLDER_API_TOKEN!
},
cms: {
url: process.env.CMS_API_BASE_URL!,
headers: {
Authorization: process.env.CMS_API_AUTH_HEADER!
}
}
}
}
})
|
Composables
Customize your API's composable names with the name
in your Nuxt config module option. Given it is set to jsonPlaceholder
, the composables $jsonPlaceholder
and useJsonPlaceholderData
will be available globally.
ℹ️ The headings of the following sections aren't available as-is. As an example, the module option name
is set to party
.
$party
(Respectively Camel-Cased API Name)
Returns the raw response of the API endpoint. Intended for actions inside methods, e. g. when sending form data to the API when clicking a submit button.
Responses can be cached between function calls for the same path based on a calculated hash by setting cache
to true
.
🦾 Type Declarations
function $party<T = any>(
path: string,
opts?: ApiFetchOptions,
): Promise<T>
type ApiFetchOptions = Omit<NitroFetchOptions<string>, 'body'> & {
body?: string | Record<string, any> | FormData | null
client?: boolean
cache?: boolean
}
Example
<script setup lang="ts">
const data = await $party(
'posts',
{
method: 'POST',
body: {
foo: 'bar'
},
async onRequest({ request }) {
console.log(request)
},
async onResponse({ response }) {
console.log(response)
},
async onRequestError({ error }) {
console.log(error)
},
async onResponseError({ error }) {
console.log(error)
}
}
)
</script>
<template>
<div>
<h1>{{ data?.title }}</h1>
</div>
</template>
usePartyData
(Respectively Pascal-Cased API Name)
Return values:
- data: the response of the API request
- pending: a boolean indicating whether the data is still being fetched
- refresh: a function that can be used to refresh the data returned by the handler function
- error: an error object if the data fetching failed
By default, Nuxt waits until a refresh
is finished before it can be executed again. Passing true
as parameter skips that wait.
Responses are cached between function calls for the same path based on a calculated hash. You can disable this behavior by setting cache
to false
.
The composable supports every useAsyncData
option.
🦾 Type Declarations
function usePartyData<T = any>(
path: MaybeComputedRef<string>,
opts?: UseApiDataOptions<T>,
): AsyncData<T, FetchError>
type UseApiDataOptions<T> = AsyncDataOptions<T> & Pick<
ComputedOptions<NitroFetchOptions<string>>,
| 'onRequest'
| 'onRequestError'
| 'onResponse'
| 'onResponseError'
| 'query'
| 'headers'
| 'method'
> & {
body?: string | Record<string, any> | FormData | null
client?: boolean
cache?: boolean
}
Basic example
<script setup lang="ts">
const { data, pending, error, refresh } = await usePartyData('posts/1')
</script>
<template>
<div>
<h1>{{ data?.result?.title }}</h1>
<button @click="refresh()">
Refresh
</button>
</div>
</template>
Example including all parameters
<script setup lang="ts">
const postId = ref(1)
const { data, pending, refresh, error } = await usePartyData('comments', {
// Whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`)
lazy: false,
// A factory function to set the default value of the data, before the async function resolves - particularly useful with the `lazy: true` option
default: () => ({
foo: 'bar'
}),
// Whether to fetch the data on the server (defaults to `true`)
server: true,
// A function that can be used to alter handler function result after resolving
transform: res => res,
// When set to `false`, will prevent the request from firing immediately. (defaults to `true`)
immediate: true,
// Watch reactive sources to auto-refresh
watch: [],
// Custom query parameters to be added to the request, can be reactive
query: computed(() => ({
postId: postId.value
})),
// Custom headers to be sent with the request
headers: {
'X-Foo': 'bar'
}
})
</script>
<template>
<div>
<h1>{{ data?.result?.title }}</h1>
<button @click="refresh()">
Refresh
</button>
</div>
</template>
FAQ
How Can I Inspect the Error Thrown by My API?
Although the idea of this module is to mask your real API by creating a Nuxt server proxy, nuxt-api-party
will forward error responses – including statusCode
, statusMessage
as well as the API response body as data
– to the client if your API fails to deliver.
Example usage with useJsonPlaceholderData()
const { data, error } = await useJsonPlaceholderData('not-found')
watchEffect(() => {
console.error('statusCode:', error.value.statusCode)
console.error('statusMessage:', error.value.statusMessage)
console.error('data:', error.value.data)
})
Example usage with $jsonPlaceholder()
function onSubmit() {
try {
const response = await $jsonPlaceholder('not-found', {
method: 'POST',
body: form.value
})
}
catch (e) {
console.error('statusCode:', e.statusCode)
console.error('statusMessage:', e.statusMessage)
console.error('data:', e.data)
}
}
💻 Development
- Clone this repository
- Enable Corepack using
corepack enable
- Install dependencies using
pnpm install
- Run
pnpm run dev:prepare
- Start development server using
pnpm run dev
Special Thanks
License
MIT License © 2022-2023 Johann Schopplich