Skip to content

Commit 288aff4

Browse files
authored
fix: preserve path segments in supabaseUrl when constructing endpoints. (#1425)
* preserve the paths in supabaseUrl when constructing endpoints. * add tests for supabaseUrl paths * add ensureTrailingSlash to normalize base URLs
1 parent f254773 commit 288aff4

File tree

4 files changed

+49
-19
lines changed

4 files changed

+49
-19
lines changed

src/SupabaseClient.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
DEFAULT_REALTIME_OPTIONS,
2020
} from './lib/constants'
2121
import { fetchWithAuth } from './lib/fetch'
22-
import { stripTrailingSlash, applySettingDefaults } from './lib/helpers'
22+
import { ensureTrailingSlash, applySettingDefaults } from './lib/helpers'
2323
import { SupabaseAuthClient } from './lib/SupabaseAuthClient'
2424
import { Fetch, GenericSchema, SupabaseClientOptions, SupabaseAuthClientOptions } from './lib/types'
2525

@@ -75,14 +75,14 @@ export default class SupabaseClient<
7575
if (!supabaseUrl) throw new Error('supabaseUrl is required.')
7676
if (!supabaseKey) throw new Error('supabaseKey is required.')
7777

78-
const _supabaseUrl = stripTrailingSlash(supabaseUrl)
78+
const _supabaseUrl = ensureTrailingSlash(supabaseUrl)
7979
const baseUrl = new URL(_supabaseUrl)
8080

81-
this.realtimeUrl = new URL('/realtime/v1', baseUrl)
81+
this.realtimeUrl = new URL('realtime/v1', baseUrl)
8282
this.realtimeUrl.protocol = this.realtimeUrl.protocol.replace('http', 'ws')
83-
this.authUrl = new URL('/auth/v1', baseUrl)
84-
this.storageUrl = new URL('/storage/v1', baseUrl)
85-
this.functionsUrl = new URL('/functions/v1', baseUrl)
83+
this.authUrl = new URL('auth/v1', baseUrl)
84+
this.storageUrl = new URL('storage/v1', baseUrl)
85+
this.functionsUrl = new URL('functions/v1', baseUrl)
8686

8787
// default storage key uses the supabase project ref as a namespace
8888
const defaultStorageKey = `sb-${baseUrl.hostname.split('.')[0]}-auth-token`
@@ -124,7 +124,7 @@ export default class SupabaseClient<
124124
accessToken: this._getAccessToken.bind(this),
125125
...settings.realtime,
126126
})
127-
this.rest = new PostgrestClient(`${_supabaseUrl}/rest/v1`, {
127+
this.rest = new PostgrestClient(new URL('rest/v1', baseUrl).href, {
128128
headers: this.headers,
129129
schema: settings.db.schema,
130130
fetch: this.fetch,

src/lib/helpers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ export function uuid() {
99
})
1010
}
1111

12-
export function stripTrailingSlash(url: string): string {
13-
return url.replace(/\/$/, '')
12+
export function ensureTrailingSlash(url: string): string {
13+
return url.endsWith('/') ? url : url + '/'
1414
}
1515

1616
export const isBrowser = () => typeof window !== 'undefined'

test/SupabaseClient.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,24 @@ describe('SupabaseClient', () => {
4545
expect(client.rest.url).toEqual('http://localhost:3000/rest/v1')
4646
})
4747

48+
test('should preserve paths in supabaseUrl', () => {
49+
const baseUrlWithPath = 'http://localhost:3000/custom/base'
50+
const client = createClient(baseUrlWithPath, KEY)
51+
52+
// @ts-ignore
53+
expect(client.authUrl.toString()).toEqual('http://localhost:3000/custom/base/auth/v1')
54+
// @ts-ignore
55+
expect(client.realtimeUrl.toString()).toEqual('ws://localhost:3000/custom/base/realtime/v1')
56+
// @ts-ignore
57+
expect(client.storageUrl.toString()).toEqual('http://localhost:3000/custom/base/storage/v1')
58+
// @ts-ignore
59+
expect(client.functionsUrl.toString()).toEqual(
60+
'http://localhost:3000/custom/base/functions/v1'
61+
)
62+
// @ts-ignore
63+
expect(client.rest.url).toEqual('http://localhost:3000/custom/base/rest/v1')
64+
})
65+
4866
test('should handle HTTPS URLs correctly', () => {
4967
const client = createClient('https://localhost:3000', KEY)
5068
// @ts-ignore
@@ -161,4 +179,4 @@ describe('SupabaseClient', () => {
161179
expect(rpcCall).toBeDefined()
162180
})
163181
})
164-
})
182+
})

test/helper.test.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
1-
import { stripTrailingSlash } from '../src/lib/helpers'
1+
import { ensureTrailingSlash } from '../src/lib/helpers'
22

3-
test('Strip trailing slash from URL', () => {
4-
const URL = 'http://localhost:3000/'
5-
const expectedURL = URL.slice(0, -1)
6-
expect(stripTrailingSlash(URL)).toBe(expectedURL)
3+
test('Adds trailing slash to URL if missing', () => {
4+
const input = 'http://localhost:3000'
5+
const expected = 'http://localhost:3000/'
6+
expect(ensureTrailingSlash(input)).toBe(expected)
77
})
88

9-
test('Return the original URL if there is no slash at the end', () => {
10-
const URL = 'http://localhost:3000'
11-
const expectedURL = URL
12-
expect(stripTrailingSlash(URL)).toBe(expectedURL)
9+
test('Keeps trailing slash of URL if already present', () => {
10+
const input = 'http://localhost:3000/'
11+
const expected = 'http://localhost:3000/'
12+
expect(ensureTrailingSlash(input)).toBe(expected)
13+
})
14+
15+
test('Adds trailing slash to URL with path if missing', () => {
16+
const input = 'http://localhost:3000/path/to/supabase'
17+
const expected = 'http://localhost:3000/path/to/supabase/'
18+
expect(ensureTrailingSlash(input)).toBe(expected)
19+
})
20+
21+
test('Keeps trailing slash of URL with path if already present', () => {
22+
const input = 'http://localhost:3000/path/to/supabase/'
23+
const expected = 'http://localhost:3000/path/to/supabase/'
24+
expect(ensureTrailingSlash(input)).toBe(expected)
1325
})

0 commit comments

Comments
 (0)