An Async Node.js Sqlite Helper
When I first built my media repository API in Node.js, I ran into callback hell when running queries or commands against the database.
As an example:
static readonly GetAllVideoGameSystems = (callback: (error: string | null, systems: VideoGameSystem[]) => void) => {
const db = this.GetDatabase();
const systems: VideoGameSystem[] = [];
db.all(getAllVideoGameSystems, (err: any, rows: VideoGameSystemQueryReturn[]) => {
db.close();
if (err) {
return callback(cleanSqliteError(err), []);
}
rows.forEach((row) => {
systems.push({
videoGameSystemId: row.VideoGameSystemId,
name: row.Name,
colorCode: row.ColorCode,
videoGameCount: row.VideoGameCount,
});
});
return callback(null, systems);
});
};
I recently started work on another tool to manage my fitness data and I decided that this would not be sustainable. So after doing some searching, and thanks to some help from devs on Stack Overflow, I ended up building a new class to do my sqlite database calls. I figured I'd share the code in hopes that maybe it will help you. I loosely modeled it off of Dapper in .NET.
This does require the sqlite3 npm package.
db.ts
import sqlite3 from 'sqlite3';
const cleanSqliteError = (error: Error): string => error.message.replace('SQLITE_ERROR: ', '');
class db {
private static GetDatabase = () => new sqlite3.Database('./path/to/db.db');
static async Query<T>(sql: string, params: any = []): Promise<[error: string | null, data: T[]]> {
return new Promise(function (resolve, reject) {
try {
const database = db.GetDatabase();
database.all(sql, params, (err: Error, rows: T[]) => {
database.close();
if (err) {
return reject([cleanSqliteError(err), []]);
}
resolve([null, rows]);
});
} catch (e) {
return reject(e);
}
});
}
static async QuerySingle<T>(sql: string, params: any = []): Promise<[error: string | null, data: T | null]> {
return new Promise(function (resolve, reject) {
try {
const database = db.GetDatabase();
database.get(sql, params, (err: Error, row: T) => {
database.close();
if (err) {
return reject([cleanSqliteError(err), []]);
}
if (!row) {
return resolve([null, null]);
}
resolve([null, row]);
});
} catch (e) {
return reject(e);
}
});
}
static async Execute(sql: string, params: any = []): Promise<string | null> {
return new Promise(function (resolve, reject) {
try {
const database = db.GetDatabase();
database.run(sql, params, (err: Error) => {
database.close();
if (err) {
return reject(cleanSqliteError(err));
}
resolve(null);
});
} catch (e) {
return reject(e);
}
});
}
}
export { db };
Or, alternatively, if you prefer a JS version,
db.js
import sqlite3 from 'sqlite3';
const cleanSqliteError = (error) => error.message.replace('SQLITE_ERROR: ', '');
class db {
private static GetDatabase = () => new sqlite3.Database('./path/to/db.db');
static async Query(sql, params = []) {
return new Promise(function (resolve, reject) {
try {
const database = db.GetDatabase();
database.all(sql, params, (err, rows) => {
database.close();
if (err) {
return reject([cleanSqliteError(err), []]);
}
resolve([null, rows]);
});
} catch (e) {
return reject(e);
}
});
}
static async QuerySingle(sql, params = []) {
return new Promise(function (resolve, reject) {
try {
const database = db.GetDatabase();
database.get(sql, params, (err, row) => {
database.close();
if (err) {
return reject([cleanSqliteError(err), []]);
}
if (!row) {
return resolve([null, null]);
}
resolve([null, row]);
});
} catch (e) {
return reject(e);
}
});
}
static async Execute(sql, params = []) {
return new Promise(function (resolve, reject) {
try {
const database = db.GetDatabase();
database.run(sql, params, (err) => {
database.close();
if (err) {
return reject(cleanSqliteError(err));
}
resolve(null);
});
} catch (e) {
return reject(e);
}
});
}
}
export { db };
Hopefully this helps. Here are links to Gists of them.