Integrate with / migrate from plain JavaScript Sequelize to Sequelize-Typescript
Versions
- sequelize: 4.44.4
- sequelize-typescript: 0.6.12-beta.3
- typescript: 3.8.3
I'm submitting a ...
[ ] bug report [ ] feature request [X] Question
Actual behavior: Errors all over the place.
Expected behavior: A working application.
Steps to reproduce: Run testing project below.
Related code: https://github.com/MagicLegend/ts-testing-project
Migrated from #438 for readability
I have an existing codebase written in plain JS using Sequelize v4, and I'm working on switching to TS (using v0.6 of this plugin to prevent the app from breaking). Given the complexity and time constraints I really cannot spend time on refactoring all the existing models and their calls to TS.
sry for the late reply: You either need to migrate the associated models as well or you could keep the corresponding classic model definition together with the new one until you‘ve also migrated the associated one. But unfortunately you cannot use an st model together with a sequelize model if their are referenced in different instances. Hope this helps
(Emphasis added, source)
@RobinBuschmann your comments to this issue sound like it is possible to use both the original .js model and the new .ts models side-by-side. How would this work?
It can be assumed that the new models only interact with the existing ones on two occasions for now; one many-to-many (Table I to table T) and one one-to-many (Table P to table A, oldjs-to-newts respectively). The other existing relationships can be dealt with the 'old' way for now.
So far I've made some attempts at convincing sequelize-typescript to use the old .js models, but with no success. Is this because the old .js models should be handled through sequelize itself, and the new .ts models through st? How would this go about in the startup phase, since the two instances need to reference the same tables on table I and P?
5 hour later update:
After reading #483 , I found the test project that you made for that. There what I want is basically done, but I'm having major struggles actually getting it to work in my own PoC. I already changed the legacy models to be the same as the ones in the test project (mine are in the factory wrapper), and work my way back from the known-working project.
However, I can't get it to work. I'm missing some piece of configuration somewhere, but the errors I'm getting are pretty useless when it comes to determining the cause or fixing it. The broken code can be found in the repo mentioned above. Help would be greatly appreciated.
Took me a little while to get the publishable test-project to the same state as my internal one, but it's there now. It throws with the same error:
TypeError: Cannot read property 'define' of undefined
at Object.<anonymous> (/home/user/Documents/gitrepos/ts-testing-project/bin/lib/models/i.js:4:21)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Module.require (internal/modules/cjs/loader.js:1044:19)
at require (internal/modules/cjs/helpers.js:77:18)
at Object.<anonymous> (/home/user/Documents/gitrepos/ts-testing-project/bin/lib/models/T.js:14:13)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Module.require (internal/modules/cjs/loader.js:1044:19)
at require (internal/modules/cjs/helpers.js:77:18)
at Object.<anonymous> (/home/user/Documents/gitrepos/ts-testing-project/bin/lib/sequelize.js:4:13)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
at Module.load (internal/modules/cjs/loader.js:1002:32)
at Function.Module._load (internal/modules/cjs/loader.js:901:14)
at Module.require (internal/modules/cjs/loader.js:1044:19)
at require (internal/modules/cjs/helpers.js:77:18)
at Object.<anonymous> (/home/user/Documents/gitrepos/ts-testing-project/bin/lib/server.js:14:21)
Attaching a debugger to it (start the watcher and attach, or start from the Start and Debug launch config), the sequelize instance is indeed undefined, and I don't know why. To my eyes both the i model and the product model from your project look the same to me. Not to start that the models are actually in the p format.
My graduation is quite blocked by this now; any help is greatly appreciated :-)
New update:
After changing things around a lot (see https://github.com/MagicLegend/ts-testing-project/commit/6aefd5a414520c79f86508adcee44d87cb730818); I found out that if you ignore the error given by the transpiler, the debugger will happily start and build the correct database. However; I don't know what I changed to fix the aforementioned error; nor how to solve the current type-related error:

Type 'Model<any, any>' is missing the following properties from type 'typeof Model': prototype, primaryKeyAttributes, primaryKeyAttribute, attributes, and 5 more.ts(2740)
ModelClassGetter.d.ts(2, 40): The expected type comes from the return type of this signature.
Also note that on line 34 (https://github.com/MagicLegend/ts-testing-project/blob/6aefd5a414520c79f86508adcee44d87cb730818/src/lib/models/T.ts#L34) I'm passing the sequelize instance and singleton to the model. This of course can't be right. How are you supposed to do this?
I developed this tool that can help you in the migration process. It generates all Models from its database. You can also modify everything you need without problems. https://github.com/leonetosoft/ny-sequelize-auto
Hi @leonetosoft
Nice tool, and certainly useful for other people! However, the legacy models still need to be available to the rest of the codebase; since updating the entire codebase at once is too much of a task. In my case the legacy models need to interact with the new models, which I found the aforementioned workaround for. There still seems a bug in this, which I would like @RobinBuschmann to have a look at when he has time.
Hey @MagicLegend, sry for the late reply. Unfortunately I won't have time in the near future to investigate this any further.
But what I can say from now is, that it is very difficult if not impossible to reference an "old" model from a new one. What I meant with the quote you posted is, that you could have old and new models side by side as long as there is no reference between these models.
No worries @RobinBuschmann , life can be busy :) For our goals, I think I've figured out on my own how to do this; it's working for us now.
But what I can say from now is, that it is very difficult if not impossible to reference an "old" model from a new one.
I have disproved this already, what I did here works: https://github.com/MagicLegend/ts-testing-project/blob/0267d3f87e9bdab8a409a6f04bd0c745d04a3cc8/src/lib/models/T.ts#L34
However, if you try to do more complex queries, Sequelize will freak out. But, for simple get-queries or even with some light filtering, this will totally work:
const ts = await T.findAll({
include: [{
model: models.P,
where: {
id: 1337,
},
}],
});
What I meant with the quote you posted is, that you could have old and new models side by side as long as there is no reference between these models.
Yes, I realized this after a bit of testing. This is the solution we ended up taking for our project. Not ideal, but it does indeed fully work; even with complex queries. It has the added benefit of the typings for the other model too. For reference for others:
See https://github.com/MagicLegend/ts-testing-project/blob/0267d3f87e9bdab8a409a6f04bd0c745d04a3cc8/src/lib/models/p.js
You would create a second model (e.g. p.ts) that has the same properties as p.js, but simply in the Sequelize-Typescript form. Then, you would load the new model into ST, but, keep loading the old model into Sequelize. The trick here is to import the models using the same Sequelize instance from ST (https://github.com/MagicLegend/ts-testing-project/blob/0267d3f87e9bdab8a409a6f04bd0c745d04a3cc8/src/lib/seq.ts#L13); and calling sequelize.import(<model>);. In my setup I add the TypeScript models first, and the 'legacy' models last.
I hope my research will help someone else!
i made the association btw plain sequelize models and sequelize-typescript models work by using the AfterDefine hook:
// oldmodel.ts
class OldModel extends Model { // this model is old sequelize model
...
}
OldModel.init(..., {sequelize})
// new-model.entity.ts
@Table
class NewModel extends Model { // extend model from ST
...
@AfterDefine
static legacyAssocations() {
NewModel.belongsTo(OldModel, {...}) // this is exactly the same way the old models define associations
}
}
// sequelize.ts
export const sequelize = new Sequelize({
...
hooks: {
/**
* we cannot use associations between old and new models with sequelize-typescript
* decorators. instead, we use the `@AfterDefine` hook and register the association the
* old sequelize way.
*
* following snippet enables the after define hook.
*
* @see https://github.com/sequelize/sequelize-typescript/issues/517
*/
afterDefine: (Model) => {
const hooks = getHooks(Model)
if (hooks) {
const hook = hooks.find(({ hookType }) => hookType === 'afterDefine')
if (hook) {
// @ts-ignore
Model[hook.methodName]()
}
}
},
},
})