K
Kyselyā€¢16mo ago
Swap

Plugin for auto-update "updated_at" field

Hello Team, Greetings! I am trying to write a plugin that will auto-update the "updated_at" column when there will be an update operation. So far, I have implemented the following code:
export class SetUpdatedAtTransformer extends OperationNodeTransformer {

protected transformUpdateQuery(node: UpdateQueryNode): UpdateQueryNode {
return this.#transformQuery(super.transformUpdateQuery(node));
}

#transformQuery<T extends UpdateQueryNode>(node: T): T {
if (!node.updates || node.updates.length === 0) {
return node;
}

return freeze({
...node,
updates: this.#setUpdatedAt(node.updates),
});
}

#setUpdatedAt(updates: ReadonlyArray<ColumnUpdateNode>): ReadonlyArray<ColumnUpdateNode> {
const out: ColumnUpdateNode[] = [];
out.push(...updates);

return freeze(out);
}
}
export class SetUpdatedAtTransformer extends OperationNodeTransformer {

protected transformUpdateQuery(node: UpdateQueryNode): UpdateQueryNode {
return this.#transformQuery(super.transformUpdateQuery(node));
}

#transformQuery<T extends UpdateQueryNode>(node: T): T {
if (!node.updates || node.updates.length === 0) {
return node;
}

return freeze({
...node,
updates: this.#setUpdatedAt(node.updates),
});
}

#setUpdatedAt(updates: ReadonlyArray<ColumnUpdateNode>): ReadonlyArray<ColumnUpdateNode> {
const out: ColumnUpdateNode[] = [];
out.push(...updates);

return freeze(out);
}
}
export class UpdatedAtPlugin implements KyselyPlugin {
readonly #transformer = new SetUpdatedAtTransformer();

transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
return this.#transformer.transformNode(args.node);
}

transformResult(
args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
return Promise.resolve(args.result);
}
}
export class UpdatedAtPlugin implements KyselyPlugin {
readonly #transformer = new SetUpdatedAtTransformer();

transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
return this.#transformer.transformNode(args.node);
}

transformResult(
args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
return Promise.resolve(args.result);
}
}
But not sure how to implement further for auto-updating the column, any help or suggestion will really helpful. Thanks šŸ™‚ Note: I am using Postgres.
Solution:
Something like this: ```ts class UpdatedAtPlugin implements KyselyPlugin { transformQuery(args: PluginTransformQueryArgs): RootOperationNode {...
Jump to solution
6 Replies
Swap
SwapOPā€¢16mo ago
Going further, i have removed SetUpdatedAtTransformer class and my modified plugin is follows:
export class UpdatedAtPlugin implements KyselyPlugin {

transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
if (args.node.kind === "UpdateQueryNode") {
const arr: ColumnUpdateNode[] = [];

arr.push(...args.node.updates);
arr.push(
{
kind: 'ColumnUpdateNode',
column: {
kind: 'ColumnNode',
column: { kind: 'IdentifierNode', name: 'updated_at' }
},
value: { kind: 'ValueNode', value: '${new Date()}' }
}
);

return {
...args.node,
updates: arr,
};
}

return args.node;
}

transformResult(
args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
return Promise.resolve(args.result);
}
}
export class UpdatedAtPlugin implements KyselyPlugin {

transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
if (args.node.kind === "UpdateQueryNode") {
const arr: ColumnUpdateNode[] = [];

arr.push(...args.node.updates);
arr.push(
{
kind: 'ColumnUpdateNode',
column: {
kind: 'ColumnNode',
column: { kind: 'IdentifierNode', name: 'updated_at' }
},
value: { kind: 'ValueNode', value: '${new Date()}' }
}
);

return {
...args.node,
updates: arr,
};
}

return args.node;
}

transformResult(
args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
return Promise.resolve(args.result);
}
}
But, getting type error
TS2322: Type Ā { kind: "ValueNode"; value: string; }Ā  is not assignable to type Ā OperationNodeĀ 
Object literal may only specify known properties, and Ā valueĀ  does not exist in type Ā OperationNodeĀ 
column-update-node.d.ts(6, 14): The expected type comes from property Ā valueĀ  which is declared here on type Ā ColumnUpdateNode
TS2322: Type Ā { kind: "ValueNode"; value: string; }Ā  is not assignable to type Ā OperationNodeĀ 
Object literal may only specify known properties, and Ā valueĀ  does not exist in type Ā OperationNodeĀ 
column-update-node.d.ts(6, 14): The expected type comes from property Ā valueĀ  which is declared here on type Ā ColumnUpdateNode
Solution
koskimas
koskimasā€¢16mo ago
Something like this:
class UpdatedAtPlugin implements KyselyPlugin {
transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
if (args.node.kind === "UpdateQueryNode") {
const arr: ColumnUpdateNode[] = [];

arr.push(...args.node.updates!);
arr.push(ColumnUpdateNode.create(
ColumnNode.create("updated_at"),
ValueNode.create(new Date().toISOString())
));

return {
...args.node,
updates: arr,
};
}

return args.node;
}

transformResult(
args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
return Promise.resolve(args.result);
}
}
class UpdatedAtPlugin implements KyselyPlugin {
transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
if (args.node.kind === "UpdateQueryNode") {
const arr: ColumnUpdateNode[] = [];

arr.push(...args.node.updates!);
arr.push(ColumnUpdateNode.create(
ColumnNode.create("updated_at"),
ValueNode.create(new Date().toISOString())
));

return {
...args.node,
updates: arr,
};
}

return args.node;
}

transformResult(
args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
return Promise.resolve(args.result);
}
}
koskimas
koskimasā€¢16mo ago
But I don't recommend doing things like this inside a query builder. You're trying to turn a query builder into an ORM. You'll end up with a really shitty and unpredictable ORM šŸ˜… Instead consider having another layer on top of Kysely that handles stuff like this.
Swap
SwapOPā€¢16mo ago
Absolutely, I agree šŸ˜„ What will your recommendation be for separating this functionality be? I can go with the DB triggers but they also cause performance issues.
koskimas
koskimasā€¢16mo ago
Depends completely on what you like and how you structure your code but at its simplest, just a updatePerson function:
async function updatePerson(person: Updateable<PersonTable>) {
return await db.updateTable('person').set({
...person,
updated_at: new Date(),
})
}
async function updatePerson(person: Updateable<PersonTable>) {
return await db.updateTable('person').set({
...person,
updated_at: new Date(),
})
}
Swap
SwapOPā€¢16mo ago
Thanks šŸ™‚
Want results from more Discord servers?
Add your server