K
Kysely•2y 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
7 Replies
Swap
SwapOP•2y 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•2y 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•2y 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•2y 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•2y 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•2y ago
Thanks šŸ™‚
Unknown User
Unknown User•3d ago
Message Not Public
Sign In & Join Server To View

Did you find this page helpful?