Skip to content

Commit 87b6c84

Browse files
Shootegerlarbish
andauthored
feat(dx): improve error handling and add fetch retry backoff (#534)
Co-authored-by: Baptiste Leproux <[email protected]>
1 parent b6495ff commit 87b6c84

File tree

5 files changed

+60
-22
lines changed

5 files changed

+60
-22
lines changed
File renamed without changes.

src/module.ts

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface ModuleOptions {
4747
* @docs https://supabase.com/blog/jwt-signing-keys
4848
*/
4949
secretKey: string
50+
5051
/**
5152
* Redirect automatically to login page if user is not authenticated
5253
* @default `true`
@@ -181,11 +182,27 @@ export default defineNuxtModule<ModuleOptions>({
181182
logger.warn('Missing supabase url, set it either in `nuxt.config.ts` or via env variable')
182183
}
183184
else {
184-
// Use the default storage key as defined by the supabase-js client if no cookiePrefix is set.
185-
// Source: https://github.com/supabase/supabase-js/blob/3316f2426d7c2e5babaab7ddc17c30bfa189f500/src/SupabaseClient.ts#L86
186-
const defaultStorageKey = `sb-${new URL(finalUrl).hostname.split('.')[0]}-auth-token`
187-
const currentPrefix = nuxt.options.runtimeConfig.public.supabase.cookiePrefix
188-
nuxt.options.runtimeConfig.public.supabase.cookiePrefix = currentPrefix || defaultStorageKey
185+
try {
186+
// Use the default storage key as defined by the supabase-js client if no cookiePrefix is set.
187+
// Source: https://github.com/supabase/supabase-js/blob/3316f2426d7c2e5babaab7ddc17c30bfa189f500/src/SupabaseClient.ts#L86
188+
const defaultStorageKey = `sb-${new URL(finalUrl).hostname.split('.')[0]}-auth-token`
189+
const currentPrefix = nuxt.options.runtimeConfig.public.supabase.cookiePrefix
190+
nuxt.options.runtimeConfig.public.supabase.cookiePrefix = currentPrefix || defaultStorageKey
191+
}
192+
catch (error) {
193+
logger.error(
194+
`Invalid Supabase URL: "${finalUrl}". `
195+
+ `Please provide a valid URL (e.g., https://example.supabase.co or http://localhost:5432)`, error)
196+
197+
// Use fallback prefix
198+
const currentPrefix = nuxt.options.runtimeConfig.public.supabase.cookiePrefix
199+
nuxt.options.runtimeConfig.public.supabase.cookiePrefix = currentPrefix || 'sb-auth-token'
200+
201+
// Fail build in production
202+
if (!nuxt.options.dev) {
203+
throw new Error('Invalid Supabase URL configuration')
204+
}
205+
}
189206
}
190207

191208
// Warn if the key isn't set.
@@ -266,13 +283,24 @@ export default defineNuxtModule<ModuleOptions>({
266283
filename: 'types/supabase-database.d.ts',
267284
getContents: async () => {
268285
if (options.types) {
269-
// resolvePath is used to minify user input error.
270-
const path = await resolvePath(options.types)
271-
const typesPath = await resolvePath('~~/.nuxt/types/') // this is the default path for nuxt types
272-
273-
if (fs.existsSync(path)) {
274-
// Make the path relative to the "types" directory.
275-
return `export * from '${relative(typesPath, path)}'`
286+
try {
287+
// resolvePath is used to minify user input error.
288+
const path = await resolvePath(options.types)
289+
const typesPath = await resolvePath('~~/.nuxt/types/') // this is the default path for nuxt types
290+
291+
if (fs.existsSync(path)) {
292+
// Make the path relative to the "types" directory.
293+
return `export * from '${relative(typesPath, path)}'`
294+
}
295+
else {
296+
logger.warn(
297+
`Database types configured at "${options.types}" but file not found at "${path}". `
298+
+ `Using "Database = unknown".`,
299+
)
300+
}
301+
}
302+
catch (error) {
303+
logger.error(`Failed to load Supabase database types from "${options.types}":`, error)
276304
}
277305
}
278306

src/runtime/plugins/auth-redirect.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ import type { Plugin } from '#app'
44
import { defineNuxtPlugin, addRouteMiddleware, defineNuxtRouteMiddleware, useRuntimeConfig, navigateTo } from '#imports'
55
import type { RouteLocationNormalized } from '#vue-router'
66

7+
function matchesAnyPattern(path: string, patterns: (string | undefined)[]): boolean {
8+
return patterns.some((pattern) => {
9+
if (!pattern) return false
10+
const regex = new RegExp(`^${pattern.replace(/\*/g, '.*')}$`)
11+
return regex.test(path)
12+
})
13+
}
14+
715
export default defineNuxtPlugin({
816
name: 'auth-redirect',
917
setup() {
@@ -15,21 +23,16 @@ export default defineNuxtPlugin({
1523

1624
// Redirect only on included routes (if defined)
1725
if (include && include.length > 0) {
18-
const isIncluded = include.some((path: string) => {
19-
const regex = new RegExp(`^${path.replace(/\*/g, '.*')}$`)
20-
return regex.test(to.path)
21-
})
22-
if (!isIncluded) {
26+
if (!matchesAnyPattern(to.path, include)) {
2327
return
2428
}
2529
}
2630

2731
// Do not redirect on login route, callback route and excluded routes
28-
const isExcluded = [...exclude ?? [], login, callback]?.some((path) => {
29-
const regex = new RegExp(`^${path.replace(/\*/g, '.*')}$`)
30-
return regex.test(to.path)
31-
})
32-
if (isExcluded) return
32+
const excludePatterns = [login, callback, ...(exclude ?? [])]
33+
if (matchesAnyPattern(to.path, excludePatterns)) {
34+
return
35+
}
3336

3437
const session = useSupabaseSession()
3538
if (!session.value) {

src/runtime/server/services/serverSupabaseServiceRole.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { SupabaseClient } from '@supabase/supabase-js'
22
import { createClient } from '@supabase/supabase-js'
3+
import { fetchWithRetry } from '../../utils/fetch-retry'
34
import type { H3Event } from 'h3'
45
import { useRuntimeConfig } from '#imports'
56
// @ts-expect-error - `#supabase/database` is a runtime alias
@@ -27,6 +28,9 @@ export const serverSupabaseServiceRole: <T = Database>(event: H3Event) => Supaba
2728
persistSession: false,
2829
autoRefreshToken: false,
2930
},
31+
global: {
32+
fetch: fetchWithRetry,
33+
},
3034
})
3135
}
3236

src/runtime/utils/fetch-retry.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ export async function fetchWithRetry(req: RequestInfo | URL, init?: RequestInit)
1414
throw error
1515
}
1616
console.warn(`Retrying fetch attempt ${attempt + 1} for request: ${req}`)
17+
18+
// Small incremental delay before retry
19+
await new Promise(resolve => setTimeout(resolve, 100 * attempt))
1720
}
1821
}
1922
throw new Error('Unreachable code')

0 commit comments

Comments
 (0)