Skip to content

Instantly share code, notes, and snippets.

@susemeee
Created December 21, 2023 06:16
Show Gist options
  • Save susemeee/3ee8c9b3b0ff7a56ec5c739d7b0a99bf to your computer and use it in GitHub Desktop.
Save susemeee/3ee8c9b3b0ff7a56ec5c739d7b0a99bf to your computer and use it in GitHub Desktop.
supabase-fetch-override-workaround
import { Database } from '@/lib/supabase-types'
import { createClient } from '@supabase/supabase-js'
export const SUPABASE_NEXTJS_CACHE_TAG = "supabase"
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
export const client = createClient<Database>(
supabaseUrl,
supabaseKey,
)
export default client
const nextFetchExtras: NextFetchRequestConfig = {
tags: [SUPABASE_NEXTJS_CACHE_TAG],
}
type SupabaseQueryType = ReturnType<ReturnType<typeof client["from"]>["select"]> |
ReturnType<ReturnType<typeof client["rpc"]>["select"]>
export async function supabaseNextCompatFetch(arg: SupabaseQueryType, tags?: string[]) {
/**
// @ts-ignore
arg.fetch = async (a0: any, a1: any) => {
// @ts-ignore
const token = await client._getAccessToken() ?? supabaseKey
// @ts-ignore
const { url, headers, method, body, signal, isMaybeSingle, shouldThrowOnError } = arg
headers['apikey'] = supabaseKey
headers['Authorization'] = `Bearer ${token}`
return fetch(a0, a1)
}
return arg
*/
/**
* "@supabase/postgrest-js": "^1.8.5"
* https://github.com/supabase/postgrest-js/blob/5e7dbdcdc57bdd2f3a57c2aa65eff1c941ab1ec6/src/PostgrestBuilder.ts#L76
*/
// @ts-ignore
const token = await client._getAccessToken() ?? supabaseKey
// @ts-ignore
const { url, headers, method, body, signal, isMaybeSingle, shouldThrowOnError, schema } = arg
headers['apikey'] = supabaseKey
headers['Authorization'] = `Bearer ${token}`
// https://postgrest.org/en/stable/api.html#switching-schemas
if (schema === undefined) {
// skip
} else if (['GET', 'HEAD'].includes(method)) {
headers['Accept-Profile'] = schema
} else {
headers['Content-Profile'] = schema
}
if (method !== 'GET' && method !== 'HEAD') {
headers['Content-Type'] = 'application/json'
}
return fetch(url.toString(), { headers, method, body: JSON.stringify(body), signal, next: { ...nextFetchExtras, tags: tags ?? nextFetchExtras.tags } })
.then(async (res) => {
let error = null
let data = null
let count: number | null = null
let status = res.status
let statusText = res.statusText
if (res.ok) {
if (method !== 'HEAD') {
const body = await res.text()
if (body === '') {
// Prefer: return=minimal
} else if (headers['Accept'] === 'text/csv') {
data = body
} else if (
headers['Accept'] &&
headers['Accept'].includes('application/vnd.pgrst.plan+text')
) {
data = body
} else {
data = JSON.parse(body)
}
}
const countHeader = headers['Prefer']?.match(/count=(exact|planned|estimated)/)
const contentRange = res.headers.get('content-range')?.split('/')
if (countHeader && contentRange && contentRange.length > 1) {
count = parseInt(contentRange[1])
}
// Temporary partial fix for https://github.com/supabase/postgrest-js/issues/361
// Issue persists e.g. for `.insert([...]).select().maybeSingle()`
if (isMaybeSingle && method === 'GET' && Array.isArray(data)) {
if (data.length > 1) {
error = {
// https://github.com/PostgREST/postgrest/blob/a867d79c42419af16c18c3fb019eba8df992626f/src/PostgREST/Error.hs#L553
code: 'PGRST116',
details: `Results contain ${data.length} rows, application/vnd.pgrst.object+json requires 1 row`,
hint: null,
message: 'JSON object requested, multiple (or no) rows returned',
}
data = null
count = null
status = 406
statusText = 'Not Acceptable'
} else if (data.length === 1) {
data = data[0]
} else {
data = null
}
}
} else {
const body = await res.text()
try {
error = JSON.parse(body)
// Workaround for https://github.com/supabase/postgrest-js/issues/295
if (Array.isArray(error) && res.status === 404) {
data = []
error = null
status = 200
statusText = 'OK'
}
} catch {
// Workaround for https://github.com/supabase/postgrest-js/issues/295
if (res.status === 404 && body === '') {
status = 204
statusText = 'No Content'
} else {
error = {
message: body,
}
}
}
if (error && isMaybeSingle && error?.details?.includes('0 rows')) {
error = null
status = 200
statusText = 'OK'
}
if (error && shouldThrowOnError) {
throw error
}
}
const postgrestResponse = {
error,
data,
count,
status,
statusText,
}
return postgrestResponse
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment