How to prevent Knex.js from running a query object when returning it from an async function?

Related searches

I have a node.js backend that constructs DB queries dynamically from various inputs using Knex.js. Some of the inputs need to be processed asynchronously. My problem is, that I can't return a knex query object from an async function (or of course in a Promise resolve function) because this triggers the execution of the query. Currently I need to process all my async inputs before handing them to the query building functions but that really limits their composability. Is there a way to prevent Knex from executing a query object in an async context?

You need to wrap builder to a function or to an object:

async function returnsQueryBuilder() {
  return { builder : knex('mytable').where('foo', 'bar') };
}

const query = (await returnsQueryBuilder()).builder;

Because async functions actually wraps and resolves returned values/promises/thenables and knex query builder is a thenable (https://promisesaplus.com/ chapter 1.2) it gets reasolved automatically.

For the same reason you can also directly await query builder to get result from the built query. Without knex query builder being thenable this would not work either:

// returns all the rows of the table
const result = await knex('table'); 

So as I said the only option is to not return query builder instance directly, but wrap it to something that is not thenable.

How to prevent Knex.js from running a query object when returning it , Cannot access the query object returned from an async function #2940 that will prevent the knex query builder from executing in a context like this. I don't think that is a good idea, since all JS developers should know how� I have a test suite that queries against a postgres running in a docker container. When I run tests using mock-knex before I run my integration tests, all those tests start failing with the following stack: 20) lib/models/Foo "before all

Thanks to Mikael Lepistö's answer I got an idea how to work around this. As he pointed out Knex queries are thenables by virtue of having a then function. The JavaScript await keyword actually calls the then function of any object you present to it, regardless if promise or not. So in order to prevent query execution on await (or .then()) you can remove/rename the queries then function. E.g.

const getQuery = async () => {
  const qb = knex("users")
    .select("id")
    .limit(100);
  qb.promise = qb.then;
  qb.then = undefined;
  return qb;
};

const query = await getQuery();
console.log(query.toString());
console.log(await query.promise());

UPDATE, WARNING: Don't try this at home kids :)

I feel obliged to point to Mikael's valid criticism in the comments. This a hacky and potential dangerous shortcut to writing your own wrapper class and might make your code harder to understand. But I also stand by my assessment that with proper TypeScript typing in my specific use case it's a valid and efficient solution.

UPDATE2: Now without messing up the prototype :). Setting .then to undefined on the instance works just fine.

Cannot access the query object returned from an async function , Here are some things I've learned using ES2017 async/await only work under the hood with functions which return actual Promise objects. With the Knex.js library, you can create SQL queries using JavaScript chaining syntax. is no automatic execution of the query by running this code — instead, the� While developing web applications, I keep a close eye on performance issues, particularly in database queries. In my latest project, I’ve been using Knex.js, a SQL query builder for Node.js. I developed a method of logging the queries executed by Knex.js as well as the execution times for each query.

For my needs, the most important is to extract the where clause, because this is often the complicated part and it's shared between several queries. Happily, this is easy to do with Knex.

For example, if you have this query, and the where function is the stuff to share with other queries.

const data = await knex("mytable")
   .where(builder => {
      builder.whereNull("mytable.deleted_at")

      if (something) {
         builder.where("something", 42)
      }
   })
   .select("*")

You can refactor as:

const makeWhereClause = ({ something }) => builder => {
   builder.whereNull("mytable.deleted_at")
   if (something) {
      builder.where("something", 42)
   }
}

const data = await knex("mytable")
   .where(makeWhereClause({something})
   .select("*")

ES2017 async function tips. Including using async functions with , The knex module is itself a function which takes a configuration object for Knex, var knex = require('knex')({ client: 'postgres', connection: async () => { const you should use the instance returned from the initialize call throughout your library. pool for transaction connections and then attempting to run queries outside of� Knex. Knex is a SQL query builder, mainly used for Node.js applications with built in model schema creation, table migrations, connection pooling and seeding.

Knex.js, Writing asynchronous applications in Node.js can be hard to learn, but Node.js, MySQL and promises, I gave an example of executing a few queries, one to return a promise, and call that function using the async/await syntax. of a class, and it uses the promisify() utility function to keep things simple. Creates a select query, taking an optional array of columns for the query, eventually defaulting to * if none are specified when the query is built. The response of a select call will resolve with an array of objects selected from the database. knex.select('title', 'author', 'year').from('books') Outputs: select `title`, `author`, `year` from

Node.js, MySQL and async/await. Learn how to use async/await to , Node.js Async Function Best Practices Since Node.js version 7.6, Node.js ships with a new V8 version that features async functions. As Node.js 8 becomes the active LTS version on October 31 , there is no reason for not starting to adopt async functions in your codebase.

The new function loadScript will not require a callback. Instead, it will create and return a Promise object that resolves when the loading is complete. The outer code can add handlers (subscribing functions) to it using .then:

Comments
  • Hey Mikael. Thank you for your answer. Wrapping the query object would definitely work. But it also would make the API less elegant. I wonder if it would be possible to rename the "then" function on the Knex QueryBuilder and add an execute function that triggers "then" and returns the promise.
  • If API uses certain practice, like using objects with certain key having builder to pass query builders around any less elegant than having to call promise() to be able to trigger that query afterwards... Anyhow if you like to try to make it more elegant, you can write your own class which wraps query builder instance inside and implement some helpers to make it have nicer API than just plain object and magic attribute.
  • Cross linking this answer with Github issue: github.com/tgriesser/knex/issues/2940#issuecomment-444401481
  • renaming .then function from query builder is a really bad Idea and will probably break if you use other libraries which expect knex querybuilder intances to be passed to them. Also nothing guarantees that query builders keep on working correctly without then method. Monkeypatching / breaking library internals is generally bad thing and in this case it is completely unnecessary and even make your code harder to read to anyone who has used the library. This might work in your particular usecase, but should not be promoted as a good method to do it.
  • ...and monkey patching the whole library would be also possible, but even worse.
  • In general I agree with your assessment, specially regarding the monkey patching. Changing the "then" method of the query instance seems to be a reasonable risk to take though. Also I'm in the process of switching the project from and old version of Flow (with terribly broken Knex typing) to TypeScript and will properly type the derived Knex variant. That should make it save enough to work with, without a wrapper class. But in general I agree it's a convenient and efficient hack for a specific use case but not a general solution. There is a definitive yuck factor to this approach :)
  • How do you feel about it without messing with the prototype, Mikael? :)
  • Not much better, its still invalid (monkey patched) knex query builder, which may work or may not. Probably still works in your project just fine :)