Objection.js: Query promise resolve before commit end.

Created on 3 Oct 2017  路  5Comments  路  Source: Vincit/objection.js

Hi,

When i use transaction to wrap some queries. The promise is resolve before queries in the transaction are executed. This is the code that make the transaction.

objection.transaction(Model.knex(), trx =>
        Model.query(trx)
          .upsertGraph(req.body.model, {
            relate: true,
            unrelate: false
          })
          .then(model => {
            res.status(httpCode.HTTP_CREATED).json(model);
          }).catch(err => {
            res.statusMessage = "There is an error";
            res.status(httpCode.HTTP_UNPROCESSABLE_ENTITY).end();
          })
      );

Now this code is the unit test that call this endpoint :

chai.request(server)
        .post(`${basePath}/models`)
        .set("Authorization", `JWT ${tokenAdmin}`)
        .set("Content-Type", "application/json")
        .send({
          model: newModel
        })
        .then(res => {
          Model.query().findOne({name: "modelName"}).then(model => {
            console.log(model); // model is undefined
            res.should.have.status(httpCode.HTTP_CREATED);
            res.body.should.be.a("object");
            res.body.should.not.be.empty;
            res.body.should.have.property("description", "modelDescription");
            Model.query().delete().where("id", model.id).then(() => {
              done();
            }).catch(err => {
              done(err);
            });
          }).catch(err => {
            done(err);
          });
        }).catch(err => {
          done(err);
        });

If I pause the test with breakpoints. This test works well. So what's the problem? am I missing something?

thx

Most helpful comment

This is off topic, but it always bugs me when people don't use promises correctly 馃槃 . Your unit test can be written like this using promise chaining:

chai.request(server)
        .post(`${basePath}/models`)
        .set("Authorization", `JWT ${tokenAdmin}`)
        .set("Content-Type", "application/json")
        .send({
          model: newModel
        })
        .then(res => {
          return Model.query().findOne({name: "modelName"}).then(model => {
            console.log(model); // model is undefined

            res.should.have.status(httpCode.HTTP_CREATED);
            res.body.should.be.a("object");
            res.body.should.not.be.empty;
            res.body.should.have.property("description", "modelDescription");

            return Model.query().delete().where("id", model.id);
          });
        })
        .then(() => {
          done();
        })
        .catch(err => {
          done(err);
        });

Now there is only one catch block but the result is exactly same.

All 5 comments

You need to return a promise from the transaction callback:

objection.transaction(Model.knex(), trx =>
        // HERE: notice the return in the next line.
        return Model.query(trx)
          .upsertGraph(req.body.model, {
            relate: true,
            unrelate: false
          })
          .then(model => {
            res.status(httpCode.HTTP_CREATED).json(model);
          }).catch(err => {
            res.statusMessage = "There is an error";
            res.status(httpCode.HTTP_UNPROCESSABLE_ENTITY).end();
          })
      );

Also you shouldn't respond inside the transaction or you can lose atomicity. This is better:

objection.transaction(Model.knex(), trx =>
  // HERE: notice the return in the next line.
  return Model.query(trx)
    .upsertGraph(req.body.model, {
      relate: true,
      unrelate: false
    });
}).then(model => {
  // Here the transaction has been committed.
  res.status(httpCode.HTTP_CREATED).json(model);
}).catch(err => {
  // Here the transaction has been roleld back.
  res.statusMessage = "There is an error";
  res.status(httpCode.HTTP_UNPROCESSABLE_ENTITY).end();
});

This is off topic, but it always bugs me when people don't use promises correctly 馃槃 . Your unit test can be written like this using promise chaining:

chai.request(server)
        .post(`${basePath}/models`)
        .set("Authorization", `JWT ${tokenAdmin}`)
        .set("Content-Type", "application/json")
        .send({
          model: newModel
        })
        .then(res => {
          return Model.query().findOne({name: "modelName"}).then(model => {
            console.log(model); // model is undefined

            res.should.have.status(httpCode.HTTP_CREATED);
            res.body.should.be.a("object");
            res.body.should.not.be.empty;
            res.body.should.have.property("description", "modelDescription");

            return Model.query().delete().where("id", model.id);
          });
        })
        .then(() => {
          done();
        })
        .catch(err => {
          done(err);
        });

Now there is only one catch block but the result is exactly same.

Nice, this works like a charme. And i will update my test code.
Just for information, write
val => { return foo(); }

is exactly the same as writing :
val => foo()
馃槅

Yes I know 馃槃 I just copied your style of writing the arrow functions from your example.

Was this page helpful?
0 / 5 - 0 ratings