How to use JWT and useFetch to make authenticated API requests in Nuxt 3

iman malekian
3 min readDec 19, 2023

--

In this tutorial, we will learn how to create a custom Nuxt 3 plugin that uses the useFetch function to make authenticated API requests with error handling, useFetch is a built-in function in Nuxt 3. We will use the defu library to merge the default and custom options for the fetch request, and the useCookie function to store and retrieve the access and refresh tokens.

Creating the plugin

We will create a custom hook called useIFetch that takes a URL and an optional options object as parameters and returns the same result as the useFetch function. The useIFetch hook will:

  • Use the useCookie function to get the access token from the cookie
  • Set the baseURL and the Authorization header for the fetch request
  • Use the onResponse option to handle the 401 (unauthorized) error by refreshing the token and retrying the request
  • Use the defu function to merge the default and custom options

To create the plugin, we will create a file called useIFetch.ts in the plugins folder of our Nuxt project, and add the following code:

import type { UseFetchOptions } from "#app";
import { defu } from "defu";

export async function useIFetch<T>(
url: string,
options: UseFetchOptions<T> = {}
) {
const accessToken = useCookie("accessToken");

const defaults: UseFetchOptions<T> = {
baseURL: "http://127.0.0.1:8000/",
key: url,
headers: accessToken.value
? { Authorization: `Bearer ${accessToken.value}` }
: {},
onResponse: async ({ response, options }) => {
if (response.status === 401) {
try {
const newToken = await refreshToken();
accessToken.value = newToken;

options.headers = { Authorization: `Bearer ${newToken}` };
useFetch(url, options as UseFetchOptions<T>);
} catch (error) {
console.error("Token refresh failed:", error);
}
}
},
};

const params = defu(options, defaults);

return useFetch(url, params);
}

async function refreshToken() {
const refreshToken = useCookie("refreshToken");

const { data, status } = await useFetch<{ access: string }>(
"http://127.0.0.1:8000/api/token/refresh/",
{
method: "POST",
body: { refresh: refreshToken.value },
}
);

if (status.value === "success") {
return data.value?.access;
} else {
throw new Error("Token refresh failed");
}
}

I have used Simple JWT to implement APIs.

Using the plugin

we can use the useIFetch hook in any component or page, just like the useFetch function. For example, we can fetch the user profile from the /api/profile/ endpoint like this:

<template>
<div>
<h1>User Profile</h1>
<div v-if="pending">Loading...</div>
<div v-else-if="error">Error: {{ $fetchState.error.message }}</div>
<div v-else>
<p>Username: {{ data.username }}</p>
<p>Email: {{ data.email }}</p>
</div>
</div>
</template>

<script setup lang="ts">
interface Profile {
username: string;
email: string;
}

const { data, pending, error } = useIFetch<Profile>("/api/profile/");
</script>

Conclusion

In this tutorial, we learned how to create a custom Nuxt 3 plugin that uses the useFetch function to make authenticated API requests with error handling. We also learned how to use the defu library to merge the default and custom options for the fetch request, and the useCookie function to store and retrieve the access and refresh tokens. We hope you found this tutorial useful and informative. Please let me know in the comments below if you have any questions or feedback.

Happy coding!

--

--