Keyborg/server/src/lib/db/dbWrapper.ts
2025-01-27 15:53:20 +03:00

83 lines
2.7 KiB
TypeScript

import { Database, RestBindParameters } from "@db/sqlite";
import { err, getMessageFromError, ok, Result } from "@shared/utils/result.ts";
import { QueryExecutionError } from "@lib/errors.ts";
import { fromNullableVal, none, Option, some } from "@shared/utils/option.ts";
import log from "@shared/utils/logger.ts";
export class DatabaseClient {
constructor(private readonly db: Database) {}
private safeExecute<T>(fn: () => T): Result<T, QueryExecutionError> {
try {
return ok(fn());
} catch (e) {
const message = getMessageFromError(e);
log.error(`Failed to execute sql! Error: ${e}`);
return err(new QueryExecutionError(message));
}
}
exec(
sql: string,
...params: RestBindParameters
): Result<number, QueryExecutionError> {
return this.safeExecute(() => this.db.exec(sql, params));
}
first<T extends object>(
sql: string,
...params: RestBindParameters
): Result<Option<T>, QueryExecutionError> {
return this.safeExecute(() =>
fromNullableVal(this.db.prepare(sql).get<T>(params))
);
}
all<T extends object>(
sql: string,
...params: RestBindParameters
): Result<Option<T[]>, QueryExecutionError> {
return this.safeExecute(() => this.db.prepare(sql).all<T>(params)).map(
(results) => (results.length > 0 ? some(results) : none),
);
}
prepareFetch<
T extends object,
P extends RestBindParameters = RestBindParameters,
>(sql: string): PreparedStatement<T> {
const stmt = this.db.prepare(sql);
const get = (
...params: P
): Result<Option<NonNullable<T>>, QueryExecutionError> =>
this.safeExecute(() => fromNullableVal(stmt.get<T>(params)));
const all = (
...params: P
): Result<Option<NonNullable<T[]>>, QueryExecutionError> =>
this.safeExecute(() => stmt.all<T>(params)).map((result) =>
result.length > 0 ? some(result) : none
);
return { get, all };
}
prepareExec<P extends RestBindParameters = RestBindParameters>(
sql: string,
): (...params: P) => Result<number, QueryExecutionError> {
const stmt = this.db.prepare(sql);
return (...params: P): Result<number, QueryExecutionError> => {
return this.safeExecute(() => stmt.run(params));
};
}
}
interface PreparedStatement<T extends object> {
get(...params: RestBindParameters): Result<Option<T>, QueryExecutionError>;
all(
...params: RestBindParameters
): Result<Option<T[]>, QueryExecutionError>;
}