DSN & JDBC string parser with query params support in a tiny and modern package.
npm install @httpx/dsn-parserDSN & JDBC string parser with query params support in a light (~750B) and modern package.









``bash`
$ npm install @httpx/dsn-parser
$ yarn add @httpx/dsn-parser
$ pnpm add @httpx/dsn-parser
- π Parse individual fields (ie: driver, user, password, host...)/
- π Handle query string parameters and converts to boolean and numeric values.
- π¦ Handle special characters like , :... in the password (some libs won't).
- π Error with indicative message / reasons (discriminated union or throwable).
- π‘οΈ Don't leak passwords in the error message.
- π Assertion and typeguard helpers
- π€ Ecosystem friendly (ie: zod integration).
- βΎοΈοΈ Tested on node 20-25, browser, bun, cloudflare workers and runtime/edge.
π Official website or Github Readme
Usage with exceptions
`typescript
import { parseDsnOrThrow } from "@httpx/dsn-parser";
const dsn = "redis://user:p@/ssword@localhost:6379/0?ssl=true";
try {
const parsedDsn = parseDsnOrThrow(dsn);
assert.deepEqual(parsedDsn, {
driver: "redis",
pass: "p@/ssword",
host: "localhost",
user: "user",
port: 6379,
db: "0",
params: {
ssl: true,
},
});
} catch (e) {
// example:
// e -> Error("Can't parse dsn: Invalid port: 12345678 (INVALID_PORT)")
}
`
Usage with discriminated union.
`typescript
import { parseDsn } from "@httpx/dsn-parser";
const dsn = "redis://user:p@/ssword@localhost:6379/0?ssl=true";
const parsed = parseDsn(dsn);
if (parsed.success) {
assert.deepEqual(parsed.value, {
driver: "redis",
pass: "p@/ssword",
host: "localhost",
user: "user",
port: 6379,
db: "0",
params: {
ssl: true,
},
});
} else {
assert.deepEqual(parsed, {
success: false,
// Reasons might vary
reason: "INVALID_PORT",
message: "Invalid http port: 12345678",
});
}
`
`typescript
import { parseDsn, type ParseDsnOptions } from "@httpx/dsn-parser";
const dsn = "mySql://localhost:6379/db";
const parsed = parseDsn(dsn, {
lowercaseDriver: true,
// Overrides, allows to force some values (ParseDsnOptions)
overrides: {
db: "db3",
port: undefined,
},
});
assert.deepEqual(parsed.value, {
driver: "mysql",
host: "localhost",
db: "db3",
});
`
| Params | Type | Description |
| ----------------- | ---------------------- | ----------------------------------------- |
| lowercaseDriver | boolean | Driver name in lowercase, default false |overrides
| | ParseDsnOptions | Overrides allows to force specific values |
`typescript
import { isParsableDsn, type ParsableDsn } from "@httpx/dsn-parser";
const dsn = "postgresql://localhost:6379/db";
if (isParsableDsn(dsn)) {
// known to be ParsableDsn
}
`
`typescript
import { assertParsableDsn, ParsableDsn } from "@httpx/dsn-parser";
try {
assertParsableDsn("redis:/");
// Type is narrowed to string (ParsableDsn) if it
// didn't throw.
} catch (e) {
assert.equal(e.message, "Cannot parse DSN (PARSE_ERROR)");
}
`
ParsableDsn is a weak opaque type.
`typescript`
declare const tag: unique symbol;
export type WeakOpaqueContainer
readonly [tag]: Token;
};
export type ParsableDsn = string & WeakOpaqueContainer<'ParsableDsn'>;
It can be used to enforce that the isParsableDsn or assertParsableDsn have been
used before usage.
`typescript
import type { ParsableDsn } from "./dsn-parser.type";
import { assertParsableDsn } from "./assert-parsable-dsn";
// to opt-in, just change the dsn param type to ParsableDsn instead of string
const fnWithWeakOpaqueType = (dsn: ParsableDsn) => {
// by explictly requiring a ParsableDsn, we can rely on typescript
// to be sure that a validation has occured before (isParsableDsn or assertParsableDsn)
}
fnWithWeakOpaqueType('redis://localhost'); // β typescript will complain
const dsn = 'redis://localhost';
assertParsableDsn(dsn);
fnWithWeakOpaqueType(dsn); // β
working cause it was checked before
`
> PS: WeakOpaque usage is totally optional, a nice to have if applicable
Utility to convert jdbc dsn.
Useful for prisma using sqlserver.
`typescript
import { convertJdbcToDsn } from "@httpx/dsn-parser";
const jdbcDsn =
"sqlserver://localhost:1433;database=my-db;authentication=default;user=sa;password=pass03$;encrypt=true;trustServerCertificate=true";
const dsn = convertJdbcToDsn(jdbc);
// -> 'sqlserver://localhost:1433?database=my-db&authentication=default&user=sa&password=pass03$&encrypt=true&trustServerCertificate=true'
`
The minimum requirement for dsn parsing is to have a host and
a driver _(/[a-z0-9]+/i)_ defined. All other options are optional.
`typescript`
export type ParsedDsn = {
driver: string;
host: string;
user?: string;
pass?: string;
port?: number;
db?: string;
/* Query params /
params?: Record
};
Things like:
`typescript`
const validExamples = [
"postgresql://postgres:@localhost:5432/prisma-db",
"redis://us_er-name:P@ass-_:?/ssw/rd@www.example.com:6379/0?cache=true",
//...
];
should work.
Simple query parameters are supported (no arrays, no nested). For convenience
it will cast 'true' and 'false' to booleans,true
parse numeric string to numbers if possible. When a query
parameter does not contain a value, it will be returned as .
`typescript`
const dsn = "redis://host?index=1&compress=false&ssl";
const parsed = parseDsn(dsn);
assert.deepEqual(parsed.value.params, {
index: 1,
compress: false,
ssl: true,
});
parseDsn won't make any assumptions on default values _(i.e: default port for mysql...)_.
parseDsn wraps its result in a discriminated uniontry... catch
to allow the retrieval of validation errors. No needed and full typescript support.
Reason codes are guaranteed in semantic versions and messages does not
leak credentials
`typescriptsuccess: false
const parsed = parseDsn("redis://localhost:65636");
assert.deepEqual(parsed, {
success: false,
reason: "INVALID_PORT",
message: "Invalid port: 65636",
});
if (!parsed.success) {
// narrows the type to`
// {
// reason: 'PARSE_ERROR'|'INVALID_ARGUMENT'|...
// message: string
// }
log(parsed.reason);
}
| Reason | Message | Comment |
| -------------------- | ----------------------- | --------------- |
| 'PARSE_ERROR' | Cannot parse DSN | _Regexp failed_ |'INVALID_ARGUMENT'
| | DSN must be a string | |'EMPTY_DSN'
| | DSN cannot be empty | |'INVALID_PORT'
| | Invalid port: ${port} | [1-65535] |
The isParsableDsn can be easily plugged into zod custom validation. Example:
`typescript
import { z } from "zod";
import { isParsableDsn, type ParsableDsn } from "@httpx/dsn-parser";
export const serverEnvSchema = z.object({
PRISMA_DATABASE_URL: z.custom
(dsn) => isParsableDsn(dsn),
"Invalid DSN format."
),
});
serverEnvSchema.parse(process.env);
`
Some libs (ioredis...) still might fail parsing a password containing '/',
unfortunately they're pretty convenient, i.e:
`bash
openssl rand 60 | openssl base64 -A
Bundle size
Bundle size is tracked by a size-limit configuration
| Scenario (esm) | Size (compressed) |
|----------------------------------------------------------|------------------:|
|
import { parseDsn } from '@httpx/dsn-parser` | ~ 750B || Level | CI | Description |
|--------------|----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Node | β
| CI for 20.x, 22.x, 24.x & 25.x. |
| Browser | β
| Tested with latest chrome (vitest/playwright) |
| Browserslist | β
| > 95% on 01/2025. defaults, chrome >= 96, firefox >= 105, edge >= 113, safari >= 15, ios >= 15, opera >= 103, not dead |
| Bun | β
| Tested with latest (at time of writing >= 1.3.3) |
| Edge | β
| Ensured on CI with @vercel/edge-runtime. |
| Cloudflare | β
| Ensured with @cloudflare/vitest-pool-workers (see wrangler.toml |
| Typescript | β
| TS 5.0 + / are-the-type-wrong checks on CI. |
| ES2022 | β
| Dist files checked with es-check |
> For _older_ browsers: most frontend frameworks can transpile the library (ie: nextjs...)
Contributions are warmly appreciated. Have a look to the CONTRIBUTING document.
If my OSS work brightens your day, let's take it to new heights together!
Sponsor>), coffee>),
or star β any gesture of support fuels my passion to improve. Thanks for being awesome! πβ€οΈ
![]() | |
JetBrains | Embie.be |
MIT Β© belgattitude and contributors.