Making requests
To make authenticated requests, the approach differs based on whether your client and server are on the same origin or different origins.
Same-origin requests
If your client and server are on the same origin (e.g. making an API call to foo.com/api
from JavaScript running on foo.com
), the session token is automatically passed to the backend in a cookie. This means that all requests to same-origin endpoints are authenticated by default.
Vanilla JavaScript
You can use the native browser Fetch API as you normally would and the request will be authenticated.
fetch('/api/foo').then((res) => res.json())
React-based applications
You can use the native browser Fetch API as you normally would and the request will be authenticated. But when a tab loses focus, and data must be fetched in the background, the session token cookie is not automatically included. You'll need to explicitly pass the session token as a Bearer token in the Authorization header.
Use the useAuth()
hook's getToken()
method to get the session token. Since getToken()
returns a Promise, you'll need to await its resolution before making the request.
export default async function useFetch() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const authenticatedFetch = async (...args) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return authenticatedFetch
}
import useSWR from 'swr'
export default async function useClerkSWR(url: string) {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const fetcher = async (...args: [RequestInfo]) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return useSWR(url, fetcher)
}
When using Tanstack Query (formerly React Query), you'll need a query function that properly handles errors. The native Fetch API doesn't throw errors for non-200 responses, so you'll need to add explicit error handling.
import { useQuery } from '@tanstack/react-query'
import { useAuth } from '@clerk/nextjs'
// Define your query keys as constants to avoid typos
export const queryKeys = {
foo: ['foo'] as const,
// Add other query keys as needed
}
// Define the response type for type safety
interface FooResponse {
// Add your response type here
id: string
name: string
}
export function useFooQuery() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
return useQuery({
queryKey: queryKeys.foo,
queryFn: async (): Promise<FooResponse> => {
// Use `getToken()` to get the current session token
const token = await getToken()
// Make the request
const response = await fetch('/api/foo', {
headers: {
Authorization: `Bearer ${token}`, // Include the session token as a Bearer token in the Authorization header
'Content-Type': 'application/json',
},
})
if (!response.ok) {
// Include status code and status text in error message
throw new Error(`API Error: ${response.status} ${response.statusText}`)
}
const data = await response.json()
return data as FooResponse
},
// Add common configuration options
retry: 2,
staleTime: 5 * 60 * 1000, // 5 minutes
})
}
// Usage in component:
function MyComponent() {
const { data, isLoading, error } = useFooQuery()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
if (!data) return null
return <div>{data.name}</div>
}
Cross-origin requests
If your client and server are on different origins (e.g. making an API call to a server on api.foo.com
from JavaScript running on a client at foo.com
), the session token needs to be passed as a Bearer token in the Authorization header.
You can retrieve the session token using the getToken()
method. Since getToken()
returns a Promise, you'll need to await its resolution before making the request.
Vanilla JavaScript
In JavaScript applications, use the global Clerk.session
object to access the getToken()
method.
(async () => {
fetch('/api/foo', {
headers: {
Authorization: `Bearer ${await Clerk.session.getToken()}`,
},
}).then((res) => res.json())
})()
React-based applications
In React-based applications, use the useAuth()
hook to access the getToken()
method.
export default async function useFetch() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const authenticatedFetch = async (...args) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return authenticatedFetch
}
import useSWR from 'swr'
import { useAuth } from '@clerk/nextjs'
export default async function useClerkSWR(url) {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
// Use `getToken()` to get the current session token
const token = await getToken()
const fetcher = async (...args) => {
return fetch(...args, {
headers: { Authorization: `Bearer ${token}` }, // Include the session token as a Bearer token in the Authorization header
}).then((res) => res.json())
}
return useSWR(url, fetcher)
}
The following example shows how to use Tanstack Query to create an authenticated query.
When using Tanstack Query (formerly React Query), you'll need a query function that properly handles errors. The native Fetch API doesn't throw errors for non-200 responses, so you'll need to add explicit error handling.
import { useQuery } from '@tanstack/react-query'
import { useAuth } from '@clerk/nextjs'
// Define your query keys as constants to avoid typos
export const queryKeys = {
foo: ['foo'] as const,
// Add other query keys as needed
}
// Define the response type for type safety
interface FooResponse {
// Add your response type here
id: string
name: string
}
export function useFooQuery() {
// Use `useAuth()` to access the `getToken()` method
const { getToken } = useAuth()
return useQuery({
queryKey: queryKeys.foo,
queryFn: async (): Promise<FooResponse> => {
// Use `getToken()` to get the current session token
const token = await getToken()
// Make the request
const response = await fetch('/api/foo', {
headers: {
Authorization: `Bearer ${token}`, // Include the session token as a Bearer token in the Authorization header
'Content-Type': 'application/json',
},
})
if (!response.ok) {
// Include status code and status text in error message
throw new Error(`API Error: ${response.status} ${response.statusText}`)
}
const data = await response.json()
return data as FooResponse
},
// Add common configuration options
retry: 2,
staleTime: 5 * 60 * 1000, // 5 minutes
})
}
// Usage in component:
function MyComponent() {
const { data, isLoading, error } = useFooQuery()
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
if (!data) return null
return <div>{data.name}</div>
}
Feedback
Last updated on