Skip to content
This repository was archived by the owner on Oct 26, 2025. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { GenerateQueryOptions } from "./queryBuilder.js";
/**
* @typeParam T - The type of the model, which will be returned when using methods such as First() or All()
*/
export class Model<T extends Record<string, ModelColumn>> {
export class Model<T extends ModelColumns> {
/**
* @param options - The options for the model. All parameters except autoIncrement and uniqueKeys are required.
* @param options.tableName - The name of the table to use.
Expand Down Expand Up @@ -195,7 +195,10 @@ export class Model<T extends Record<string, ModelColumn>> {
orReplace = false
): Promise<D1Result<InferFromColumns<T>>> {
const qt = orReplace ? QueryType.INSERT_OR_REPLACE : QueryType.INSERT;
const statement = GenerateQuery(qt, this.tableName, { data });
const statement = GenerateQuery(qt, this.tableName, {
data,
columns: this.columns,
});
return this.D1Orm.prepare(statement.query)
.bind(...statement.bindings)
.run();
Expand Down Expand Up @@ -315,7 +318,9 @@ export interface ModelColumn {
type: DataTypes;
notNull?: boolean;
defaultValue?: unknown;
json?: boolean;
}
export type ModelColumns = Record<string, ModelColumn>;

/**
* @enum {string} Aliases for DataTypes used in a {@link ModelColumn} definition.
Expand Down
18 changes: 13 additions & 5 deletions src/queryBuilder.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ModelColumns } from "./model";

/**
* @enum {string} - The type of the query
*/
Expand Down Expand Up @@ -32,6 +34,7 @@ export type GenerateQueryOptions<T extends object> = {
orderBy?: OrderBy<T> | OrderBy<T>[];
data?: Partial<T>;
upsertOnlyUpdateData?: Partial<T>;
columns?: ModelColumns;
};

/**
Expand Down Expand Up @@ -112,14 +115,19 @@ export function GenerateQuery<T extends object>(
throw new Error("Must provide data to insert");
}
const keys = [];
const values = [];
for (const [key, value] of Object.entries(options.data)) {
const column = options.columns?.[key];
keys.push(key);
bindings.push(value);
if (column?.json) {
bindings.push(JSON.stringify(value));
values.push(`json(?)`);
} else {
bindings.push(value);
values.push("?");
}
}
query += ` (${keys.join(", ")}) VALUES (${"?"
.repeat(keys.length)
.split("")
.join(", ")})`;
query += ` (${keys.join(", ")}) VALUES (${values.join(", ")})`;
break;
}
case QueryType.UPDATE: {
Expand Down
40 changes: 40 additions & 0 deletions test/querybuilder.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,26 @@ describe("Query Builder", () => {
expect(statement.bindings[0]).to.equal(1);
expect(statement.bindings[1]).to.equal("test");
});
it("can write JSON with a column config", () => {
const address = { line1: '1000 N. Main St.', city: 'New York', state: 'NY' }
const statement = GenerateQuery(QueryType.INSERT, "test", {
data: {
id: 1,
name: "test",
address
},
columns: {
address: { type: 'text', json: true }
}
});
expect(statement.query).to.equal(
"INSERT INTO `test` (id, name, address) VALUES (?, ?, json(?))"
);
expect(statement.bindings.length).to.equal(3);
expect(statement.bindings[0]).to.equal(1);
expect(statement.bindings[1]).to.equal("test");
expect(statement.bindings[2]).to.equal(JSON.stringify(address));
});
});
describe(QueryType.INSERT_OR_REPLACE, () => {
it("should throw an error if no data is provided", () => {
Expand Down Expand Up @@ -209,6 +229,26 @@ describe("Query Builder", () => {
expect(statement.bindings[0]).to.equal(1);
expect(statement.bindings[1]).to.equal("test");
});
it("can write JSON with a column config", () => {
const address = { line1: '1000 N. Main St.', city: 'New York', state: 'NY' }
const statement = GenerateQuery(QueryType.INSERT_OR_REPLACE, "test", {
data: {
id: 1,
name: "test",
address
},
columns: {
address: { type: 'text', json: true }
}
});
expect(statement.query).to.equal(
"INSERT or REPLACE INTO `test` (id, name, address) VALUES (?, ?, json(?))"
);
expect(statement.bindings.length).to.equal(3);
expect(statement.bindings[0]).to.equal(1);
expect(statement.bindings[1]).to.equal("test");
expect(statement.bindings[2]).to.equal(JSON.stringify(address));
});
});
describe(QueryType.UPDATE, () => {
it("should throw an error if no data is provided", () => {
Expand Down