LioranDB TypeScript Series #3: Connection Strings, Transports and Client Lifecycle
LioranDB TypeScript Series: Build with a developer-first document database powered by Rust and designed for TypeScript.
We connected to LioranDB in Part 2 with one line:
const client = await LioranDBClient.connect(process.env.URI!);
There's quite a bit happening underneath it.
Let's unpack it.
Connection URI
A typical local URI looks like:
liorandb://admin:password@127.0.0.1:27018/default
Conceptually:
liorandb:// USER : PASSWORD @ HOST : PORT / DATABASE
LioranDB understands multiple schemes:
liorandb://
liorandb+http://
liorandb+https://
http://
https://
grpc://
For example:
liorandb://admin:password@127.0.0.1:27018/default
or:
liorandb+https://admin:password@db.example.com/default
Reserved characters matter
Suppose your password is:
hello@database#123
Don't place that directly inside the URI.
Encode it:
const password = encodeURIComponent("hello@database#123");
const uri =
`liorandb://admin:${password}@127.0.0.1:27018/default`;
This avoids the URI parser interpreting password characters as URI syntax.
Parsing connection strings
The driver exposes its parser:
import { parseConnectionString } from "@liorandb/driver";
const config = parseConnectionString(
"liorandb://admin:password@127.0.0.1:27018/default"
);
console.log(config);
This is useful for tooling, debugging and infrastructure layers that need to inspect connection configuration.
Client options
You can also configure the client explicitly.
const client = await LioranDBClient.connect(uri, {
transport: "auto",
autoRefreshTokens: true,
logoutOnClose: true,
requestTimeoutMS: 5000,
slowRequestThresholdMS: 200,
onWarning(warning) {
console.warn(
`[${warning.code}] ${warning.message}`
);
},
});
Important options include:
transporttimeoutMSconnectTimeoutMSrequestTimeoutMSgrpcChannelsmaxRetriesretryDelayMSautoRefreshTokenslogoutOnCloseappNameslowRequestThresholdMSonWarning
For most applications, transport: "auto" is the sensible starting point.
Diagnostics
You can attach diagnostic headers:
client.setDiagnosticHeaders({
"x-trace-id": "request-001",
});
And observe responses:
client.setResponseObserver((event) => {
console.log({
transport: event.transport,
operation: event.operation,
durationMS: event.durationMS,
requestId: event.requestId,
});
});
This becomes especially useful when debugging latency or transport behavior.
Health and discovery
The client also provides helpers for inspecting the server:
await client.live();
await client.ready();
await client.serverInfo();
await client.listDatabases();
Client lifecycle
A good application lifecycle looks like this:
const client = await LioranDBClient.connect(uri);
try {
const db = client.db("default");
// application work
} finally {
await client.close();
}
Closing the client cleans up its resources, transports and cursors.
Attempting to use a closed client results in ClientClosedError.
One client, many collections
Don't create a new database connection for every query.
Create the client at the appropriate application lifecycle boundary and reuse it.
Application
↓
LioranDBClient
├── users
├── products
├── sessions
└── events
That's both cleaner and considerably closer to how a database client is intended to be used.
Resources
Connection & Client Docs:
https://docs.liorandb.com/docs/driver/connection-and-client
Documentation: https://docs.liorandb.com
Website: https://liorandb.com
Creator: https://github.com/UltronTheAI
Previous: Part 2 → Docker & First Query
Next: Part 4 → Databases, Collections & Typed CRUD
Top comments (0)