Skip to content

Commit 34defd9

Browse files
feat: Allow localhost connection strings (#118)
* feat: Allow localhost connection strings * chore: changeset * fix: lockfile * fix: idk man
1 parent fe934d8 commit 34defd9

File tree

13 files changed

+819
-118
lines changed

13 files changed

+819
-118
lines changed

.changeset/grumpy-owls-accept.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@vercel/postgres': minor
3+
'@vercel/postgres-kysely': minor
4+
---
5+
6+
feat: Allow users to connect to local databases

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
"prettier": "2.8.8",
4343
"publint": "0.1.11",
4444
"ts-jest": "29.0.3",
45-
"turbo": "latest",
46-
"typescript": "^4.8.0"
45+
"turbo": "^1.9.3",
46+
"typescript": "^4.9.5"
4747
},
4848
"packageManager": "pnpm@8.4.0",
4949
"engines": {

packages/postgres-kysely/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
"ts-jest": "29.0.3",
6666
"tsconfig": "workspace:*",
6767
"tsup": "6.3.0",
68-
"typescript": "4.8.4"
68+
"typescript": "4.9.5"
6969
},
7070
"peerDependencies": {
7171
"kysely": "^0.24.2"

packages/postgres/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"ts-jest": "29.0.3",
6868
"tsconfig": "workspace:*",
6969
"tsup": "6.3.0",
70-
"typescript": "4.8.4"
70+
"typescript": "4.9.5"
7171
},
7272
"engines": {
7373
"node": ">=14.6"

packages/postgres/src/create-client.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Client } from './types';
22
import {
33
MOCKED_DIRECT_CONNECTION_STRING,
4+
MOCKED_LOCALHOST_CONNECTION_STRING,
45
MOCKED_POOLED_CONNECTION_STRING,
56
} from './mocks';
67
import { createClient } from './create-client';
@@ -69,4 +70,10 @@ describe('createClient', () => {
6970
expect(bad).toThrow(VercelPostgresError);
7071
expect(bad).toThrow('invalid_connection_string');
7172
});
73+
74+
it('does not throw error if provided with local connection string', () => {
75+
const good = (): Client =>
76+
createClient({ connectionString: MOCKED_LOCALHOST_CONNECTION_STRING });
77+
expect(good).not.toThrow(VercelPostgresError);
78+
});
7279
});

packages/postgres/src/create-client.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Client } from '@neondatabase/serverless';
33
import type { VercelPostgresClientConfig } from './types';
44
import {
55
isDirectConnectionString,
6+
isLocalhostConnectionString,
67
postgresConnectionString,
78
} from './postgres-connection-string';
89
import { VercelPostgresError } from './error';
@@ -44,7 +45,10 @@ export function createClient(
4445
'missing_connection_string',
4546
"You did not supply a 'connectionString' and no 'POSTGRES_URL_NON_POOLING' env var was found.",
4647
);
47-
if (!isDirectConnectionString(connectionString))
48+
if (
49+
!isLocalhostConnectionString(connectionString) &&
50+
!isDirectConnectionString(connectionString)
51+
)
4852
throw new VercelPostgresError(
4953
'invalid_connection_string',
5054
'This connection string is meant to be used with a pooled connection. Try `createPool()` instead.',

packages/postgres/src/create-pool.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Pool } from './types';
22
import {
33
MOCKED_DIRECT_CONNECTION_STRING,
4+
MOCKED_LOCALHOST_CONNECTION_STRING,
45
MOCKED_POOLED_CONNECTION_STRING,
56
} from './mocks';
67
import { createPool } from './create-pool';
@@ -78,4 +79,10 @@ describe('createPool', () => {
7879
expect(bad).toThrow(VercelPostgresError);
7980
expect(bad).toThrow('invalid_connection_string');
8081
});
82+
83+
it('does not throw error if provided with local connection string', () => {
84+
const good = (): Pool =>
85+
createPool({ connectionString: MOCKED_LOCALHOST_CONNECTION_STRING });
86+
expect(good).not.toThrow(VercelPostgresError);
87+
});
8188
});

packages/postgres/src/create-pool.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type {
66
import { Pool } from '@neondatabase/serverless';
77
import type { VercelPoolClient, VercelPostgresPoolConfig } from './types';
88
import {
9+
isLocalhostConnectionString,
910
isPooledConnectionString,
1011
postgresConnectionString,
1112
} from './postgres-connection-string';
@@ -75,7 +76,10 @@ export function createPool(config?: VercelPostgresPoolConfig): VercelPool {
7576
"You did not supply a 'connectionString' and no 'POSTGRES_URL' env var was found.",
7677
);
7778

78-
if (!isPooledConnectionString(connectionString))
79+
if (
80+
!isLocalhostConnectionString(connectionString) &&
81+
!isPooledConnectionString(connectionString)
82+
)
7983
throw new VercelPostgresError(
8084
'invalid_connection_string',
8185
'This connection string is meant to be used with a direct connection. Make sure to use a pooled connection string or try `createClient()` instead.',

packages/postgres/src/mocks.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ export const MOCKED_DIRECT_CONNECTION_STRING =
44
'postgres://default:password@foo123.us-east-2.postgres.vercel-storage.com/verceldb';
55
export const MOCKED_POOLED_CONNECTION_STRING =
66
'postgres://default:password@foo123-pooler.us-east-2.postgres.vercel-storage.com/verceldb';
7+
export const MOCKED_LOCALHOST_CONNECTION_STRING =
8+
'postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp';

packages/postgres/src/postgres-connection-string.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
} from './mocks';
55
import {
66
isDirectConnectionString,
7+
isLocalhostConnectionString,
78
isPooledConnectionString,
89
postgresConnectionString,
910
} from './postgres-connection-string';
@@ -79,3 +80,31 @@ describe('isPooledConnectionString', () => {
7980
);
8081
});
8182
});
83+
84+
describe('isLocalhostConnectionString', () => {
85+
it.each(['localhost', 'http', 'foobar', 'blah'])(
86+
'returns false for invalid connection strings: %s',
87+
(connectionString) => {
88+
expect(isLocalhostConnectionString(connectionString)).toEqual(false);
89+
},
90+
);
91+
it.each([
92+
'postgresql://localhost',
93+
'postgresql://localhost:5432',
94+
'postgresql://localhost/mydb',
95+
'postgresql://user@localhost',
96+
'postgresql://user:secret@localhost',
97+
'postgresql://other@localhost/otherdb?connect_timeout=10&application_name=myapp',
98+
'postgresql://localhost/mydb?user=other&password=secret',
99+
])(
100+
'returns true for a valid localhost connection string',
101+
(connectionString) => {
102+
expect(isLocalhostConnectionString(connectionString)).toEqual(true);
103+
},
104+
);
105+
it('returns false for a valid non-localhost connection string', () => {
106+
expect(
107+
isLocalhostConnectionString(MOCKED_POOLED_CONNECTION_STRING),
108+
).toEqual(false);
109+
});
110+
});

0 commit comments

Comments
 (0)