kurierjs / kurier

TypeScript framework to create JSON:API compliant APIs
https://kurier.readthedocs.io/en/latest/
MIT License
61 stars 9 forks source link

`add` operations are forced to receive a client-side ID due to Knex + MySQL limitations #260

Open joelalejandro opened 3 years ago

joelalejandro commented 3 years ago

Due to knex/knex#2682, add operations in MySQL require an id to be passed, otherwise Kurier won't return the resource ID on the result of the operation.

https://github.com/kurierjs/kurier/blob/28c25e3435a9a841d8fd234c9e3a9ba632fecdb6/src/processors/knex-processor.ts#L163

This insert + RETURNING syntax works only on engines such as PostgreSQL or Oracle.

spersico commented 3 years ago

I took a look into this issue (mostly because I hate the warnings in the console every time I run the tests). I see two approaches here:

The problem here is that... this is tricky grounds: So... I know that this works for auto incremental ids in MySQL: SELECT MAX(LAST_INSERT_ID()) FROM SOME_TABLE . But The 2 queries approach it's not reliable (it's not atomic, so it can't be trusted), and it doesn't support bulk insertions.

joelalejandro commented 3 years ago

@isBatak do you happen to know if using Prisma would help us to fix this problem?

isBatak commented 3 years ago

@joelalejandro I'm not an expert but createMany is the similar method to insert https://www.prisma.io/docs/reference/api-reference/prisma-client-reference#createmany but it is also not supported by SQLite.

createMany is not supported by SQLite. Is it possible to detect SQLite and MySQL DB and do a slower approach with multiple inserts inside transaction.

spersico commented 3 years ago

So... checking back into this, I found an update that relates to the issue: Seems like SQLite has recently added support for the returning syntax in march: Docs And the SQLite node driver has a PR waiting to be merged, that bumps the SQLite version used in it to the latest version.

So... soon, the only engine that doesn't support returning will be MySQL.

The main issue that we face regarding this, is that any implementation would probably involve creating a transaction, that effectively creates an object, and then pulls the latest data up (something like this). Doing this outside a transaction is a no-go, because AFAIK that's the only way to ensure the atomicity of the operation.

joelalejandro commented 3 years ago

I like the transaction-based approach. I wonder if it would affect the ability to rollback bulk operations.

joelalejandro commented 3 years ago

So, SQLite 3.36 is out, and the PR mentioned by @spersico is now merged. We're waiting on a driver upgrade, but we'll also need to upgrade Knex to remove the .returning() is not supported warning for SQLite.

I've opened an issue here: https://github.com/knex/knex/issues/4766

joelalejandro commented 1 year ago

This continues to happen with MySQL, unfortunately. I've implemented #354 to avoid having a failing response pipeline. At least client-side ID'ed insertions will work. But we'll need a last_inserted_id strategy for this.