I'm trying got write a plugin for objection that intercepts the sql queries outputed by the query builder and logs them to a .sql file. Is there any way to achieve this?
For ex:
await Person.query();
log:
select "Person".* from "Person"
I think there are hooks in knex that can be used for this, but I cannot find any documentation. Only the single query events are documented. @elhigu Is there a global on('query', (data) => ...) hook in knex and should it be documented?
Yup knex.on('query', data => { console.dir(data) }) is part of public API, but looks like it is not mentioned in documentation http://knexjs.org/#Interfaces-query
Also parameters that event handler takes in should be documented better.
Ok but is this event accessible from the query() method of the objection model?
Something like:
Person.query().on('query', (data) => console.dir(data));
Is not possible...
Unfortunately there's no reliable way to do that in the model. There is the toString method in objection's query builder that returns the sql, but it doesn't work in all cases and should only be used for debugging.
Internally there is a query build phase called onBuildKnex in addition to the onBuild phase (to which you can attach hooks using the onBuild method). onBuild is given an objection query builder, and onBuildKnex is called for the knex query builder that is created based on the objection one. Well anyway, we could add a new onBuildKnex hook in which you could attach the on('query') to the knex query builder. I have to think about this some more though.
If that hook was added, you could do this:
class LoggingModel extends Model {
static query(...args) {
return super.query(...args).onBuildKnex(knexQueryBuilder => {
knexQueryBuilder.on('query', queryData => {
log(queryData);
});
});
}
}
Yes if possible that would be great! I'm currently exploring the option to add that functionality as a plugin to be used as a mixin to the models using the $beforeInsert/update etc
Unfortunately there's no reliable way to do that in the model. There is the
toStringmethod in objection's query builder that returns the sql, but it doesn't work in all cases and should only be used for debugging.Internally there is a query build phase called
onBuildKnexin addition to theonBuildphase (to which you can attach hooks using theonBuildmethod).onBuildis given an objection query builder, andonBuildKnexis called for the knex query builder that is created based on the objection one. Well anyway, we could add a newonBuildKnexhook in which you could attach theon('query')to the knex query builder. I have to think about this some more though.If that hook was added, you could do this:
class LoggingModel extends Model { static query(...args) { return super.query(...args).onBuildKnex(knexQueryBuilder => { knexQueryBuilder.on('query', queryData => { log(queryData); }); }); } }
@koskimas this looks like exactly what I need but I'm having a lot of trouble getting the Typescript types to play nicely. Here's what I have:
class LoggingModel extends Model {
static query(...args: Parameters<ModelClass<Model>['query']>) {
const queryBuilder = Model.query(...args);
queryBuilder.onBuildKnex(knexQueryBuilder => {
knexQueryBuilder.on('query', () => {
Logger.debug('Knex SQL', {
sql: knexQueryBuilder.toSQL().toNative()
});
});
});
return queryBuilder;
}
}
I get the following error:
The types returned by 'query(...)' are incompatible between these types.
Type 'QueryBuilder<Model, Model[]>' is not assignable to type 'QueryBuilderType<M>'.
Type 'QueryBuilder<Model, Model[]>' is not assignable to type 'QueryBuilder<M, M[]>'.
The types returned by 'findById(...).execute()' are incompatible between these types.
Type 'Promise<Model>' is not assignable to type 'Promise<M>'.
Type 'Model' is not assignable to type 'M'.
'Model' is assignable to the constraint of type 'M', but 'M' could be instantiated with a different subtype of constraint 'Model'.ts(2417)
Do you know why both Model and M are incompatible? Thank you!
Most helpful comment
Unfortunately there's no reliable way to do that in the model. There is the
toStringmethod in objection's query builder that returns the sql, but it doesn't work in all cases and should only be used for debugging.Internally there is a query build phase called
onBuildKnexin addition to theonBuildphase (to which you can attach hooks using theonBuildmethod).onBuildis given an objection query builder, andonBuildKnexis called for the knex query builder that is created based on the objection one. Well anyway, we could add a newonBuildKnexhook in which you could attach theon('query')to the knex query builder. I have to think about this some more though.If that hook was added, you could do this: