d1-orm
Version:
A simple strictly typed ORM for Cloudflare's D1 product
151 lines • 5.96 kB
JavaScript
/**
* @enum {string} - The type of the query
*/
export var QueryType;
(function (QueryType) {
QueryType["SELECT"] = "SELECT";
QueryType["INSERT"] = "INSERT";
QueryType["INSERT_OR_REPLACE"] = "INSERT or REPLACE";
QueryType["UPDATE"] = "UPDATE";
QueryType["DELETE"] = "DELETE";
QueryType["UPSERT"] = "UPSERT";
})(QueryType || (QueryType = {}));
/**
* @param type - The type of query to generate, see {@link QueryType}
* @param tableName - The table to query
* @param options - The options for the query, see {@link GenerateQueryOptions}
* @typeParam T - The type of the object to query. This is generally not needed to be specified, but can be useful if you're calling this yourself instead of through a {@link Model}.
* @returns The query and bindings to be executed
*/
export function GenerateQuery(type, tableName, options = {}, primaryKeys = "id") {
if (typeof tableName !== "string" || !tableName.length) {
throw new Error("Invalid table name");
}
let query = "";
const bindings = [];
switch (type) {
case QueryType.SELECT: {
query = `SELECT * FROM \`${tableName}\``;
if (options.where) {
const whereStmt = [];
for (const [key, value] of Object.entries(options.where)) {
whereStmt.push(`${key} = ?`);
bindings.push(value);
}
if (whereStmt.length)
query += ` WHERE ${whereStmt.join(" AND ")}`;
}
if (options.orderBy) {
query += " ORDER BY " + transformOrderBy(options.orderBy);
}
if (options.limit) {
query += ` LIMIT ${options.limit}`;
if (options.offset) {
query += ` OFFSET ${options.offset}`;
}
}
break;
}
case QueryType.DELETE: {
query = `DELETE FROM \`${tableName}\``;
if (options.where) {
const whereStmt = [];
for (const [key, value] of Object.entries(options.where)) {
whereStmt.push(`${key} = ?`);
bindings.push(value);
}
if (whereStmt.length)
query += ` WHERE ${whereStmt.join(" AND ")}`;
}
break;
}
case QueryType.INSERT_OR_REPLACE:
case QueryType.INSERT: {
query = `${type} INTO \`${tableName}\``;
if (typeof options.data !== "object" ||
Object.getOwnPropertyNames(options.data).length === 0) {
throw new Error("Must provide data to insert");
}
const keys = [];
for (const [key, value] of Object.entries(options.data)) {
keys.push(key);
bindings.push(value);
}
query += ` (${keys.join(", ")}) VALUES (${"?"
.repeat(keys.length)
.split("")
.join(", ")})`;
break;
}
case QueryType.UPDATE: {
query = `UPDATE \`${tableName}\``;
if (typeof options.data !== "object" ||
Object.getOwnPropertyNames(options.data).length === 0) {
throw new Error("Must provide data to update");
}
const keys = [];
for (const [key, value] of Object.entries(options.data)) {
keys.push(`${key} = ?`);
bindings.push(value);
}
query += ` SET ${keys.join(", ")}`;
if (options.where) {
const whereStmt = [];
for (const [key, value] of Object.entries(options.where)) {
whereStmt.push(`${key} = ?`);
bindings.push(value);
}
if (whereStmt.length)
query += ` WHERE ${whereStmt.join(" AND ")}`;
}
break;
}
case QueryType.UPSERT: {
const insertDataKeys = Object.keys(options.data ?? {});
const updateDataKeys = Object.keys(options.upsertOnlyUpdateData ?? {});
const whereKeys = Object.keys(options.where ?? {});
bindings.push(...Object.values(options.data ?? {}), ...Object.values(options.upsertOnlyUpdateData ?? {}), ...Object.values(options.where ?? {}));
if (insertDataKeys.length === 0 ||
updateDataKeys.length === 0 ||
whereKeys.length === 0) {
throw new Error("Must provide data to insert with, data to update with, and where keys in Upsert");
}
query = `INSERT INTO \`${tableName}\` (${insertDataKeys.join(", ")})`;
query += ` VALUES (${"?"
.repeat(insertDataKeys.length)
.split("")
.join(", ")})`;
const primaryKeyStr = Array.isArray(primaryKeys)
? primaryKeys.join(", ")
: primaryKeys;
query += ` ON CONFLICT (${primaryKeyStr}) DO UPDATE SET`;
query += ` ${updateDataKeys.map((key) => `${key} = ?`).join(", ")}`;
query += ` WHERE ${whereKeys.map((key) => `${key} = ?`).join(" AND ")}`;
break;
}
default:
throw new Error("Invalid QueryType provided");
}
return {
query,
bindings,
};
}
/**
* @private
* @hidden
*/
export function transformOrderBy(orderBy) {
if (Array.isArray(orderBy)) {
return orderBy.map(transformOrderBy).join(", ");
}
if (typeof orderBy === "string" ||
typeof orderBy === "symbol" ||
typeof orderBy === "number") {
return `"${String(orderBy)}"`;
}
return (`"${String(orderBy.column)}"` +
(orderBy.descending ? " DESC" : "") +
(orderBy.nullLast ? " NULLS LAST" : ""));
}
//# sourceMappingURL=queryBuilder.js.map