Migrations with Separate Entity Definition - nestjs

I am trying to update my database using the entities with separate entity definition.
The database isn't updated either with the option synchronize = true or with the command:
ts-node ./node_modules/typeorm/cli.js migration:generate -n CreateDatabase
which generates an empty migration file.
What am I forgetting?
Steps to reproduce or a small repository showing the problem:
ormconfig.json
{
"type": "postgres",
"host": "localhost",
"port": 5432,
"username": "postgres",
"password": "secret",
"database": "app",
"entities": ["src/**/entities/*.entity.ts"],
"migrations": ["src/database/migrations/*.ts"],
"cli": {
"migrationsDir": "src/database/migrations"
},
"synchronize": true
}
topic.entity.ts
export class Topic {
title: string
}
topic.schema.ts
import { EntitySchema, EntitySchemaColumnOptions } from 'typeorm'
import { Topic} from '../topic.entity'
export const TopicSchema = new EntitySchema<Topic>({
name: 'topics',
target: Topic,
columns: {
title: {
name: 'title',
type: 'text',
nullable: true,
} as EntitySchemaColumnOptions,
},
})

Try split the topic.entity.ts to topic.entity.ts and topic.schema.ts and use only the topic.schema.ts on your ormconfig.json.
Change the entities section from your ormconfig.json to:
"entities": ["src/**/entities/*.schema.ts"]

I was struggling with this. What I did was to split into
Model and Schema. Model is a common Class.
Session.ts
export class Session {
token: string;
userId: string;
}
Session.entity.ts
And Schema. If you forget target it unifies every entity into one table. Don't forget that. And use the same name as model so it matches properly.
import {EntitySchema} from "typeorm";
import { Session } from "../models/Session";
export const SessionEntity = new EntitySchema<Session>({
name: "Session", // BE CAREFUL: must be the same name as Model, STRING
target: Session, // This must be the Class itself, NOT STRING
columns: {
token: {
type: String,
primary: true,
generated: "uuid"
},
userId: {
type: String
}
}
});
And to use it
import { SessionEntity } from "../entities/Session.entity";
const connection = this.typeORMService.get("default");
const repository = this.ormService.connection.getRepository(SessionEntity);
const session = await repository.save({userId: "123"});
Another issue that you may have is that if you add your settings in the code and in ormconfig.json it will use the code settings. So be careful with that as well.
I prefer to setup settings in code like this:
const rootDir = __dirname;
#Configuration({
rootDir,
typeorm: [
{
name: "default",
synchronize: true,
type: "postgres",
url: process.env.DATABASE_URL || config.DATABASE_URL,
ssl: process.env.DATABASE_URL ? true : false, // If env var is not set then it is dev
"entities": [
`${rootDir}/**/*.entity.js`,
`${rootDir}/**/*.entity.{ts,js}`
],
"migrations": [`${rootDir}/migrations/**/*.js`],
subscribers: [
`${rootDir}/subscriber/*.js}`
]
}
]
})
I hope you find this useful. If you have any doubts please let me know.

Related

Sequelize - trying to make models dynamic

I've been trying to automate the creation of my sequelize models by creating them with a generic model that I can pass definitions into, rather than creating a model file specifically for each one.
I have an array of model definitions which looks something like this:
const modelDefinitions = [
{
name: "User",
fieldDefinitions: [
{
name: "first_name",
label: "First Name",
column_type: Sequelize.DataTypes.STRING,
},
{
name: "last_name",
label: "Last Name",
column_type: Sequelize.DataTypes.STRING,
},
{
name: "email",
label: "Email",
column_type: Sequelize.DataTypes.STRING,
},
{
name: "password",
label: "Password",
restricted: true,
column_type: Sequelize.DataTypes.STRING,
},
],
},
{
name: "Audit",
fieldDefinitions: [
{
name: "ref",
column_type: Sequelize.DataTypes.STRING,
label: "Audit Ref",
},
{
name: "result",
column_type: Sequelize.DataTypes.STRING,
label: "Result",
},
{
name: "auditor_id",
column_type: Sequelize.DataTypes.INTEGER,
label: "Auditor",
},
],
},
];
When my array of models contains just one model it works perfectly fine, but when I have multiple, the GenericModel of the previously defined models is then "changed" to ne the last one in the list that was initialised.
I'm new to node so I think I'm either missing something or there's some sort of model caching happening, meaning that all instances of GenericModel become what it is initialised as last.
Please see my example below (the commented out code is what I used to use to define the models and the reduce is my new way of defining these)
// {
// User: User.init(sequelize, modelDef),
// Article: Article.init(sequelize, modelDef),
// Audit: Audit.init(sequelize, modelDef),
// Form: Form.init(sequelize, modelDef),
// };
const models = modelDefinitions.reduce((acc, modelDef) => {
return { ...acc, [modelDef.name]: GenericModel.init(sequelize, modelDef) };
}, {});
console.log({ models });
My console.log() returns the following - notice both are Group :
{
models: {
User: Group,
Group: Group
}
}
As you can see, what ever the last model is defined as, the previous ones inherit that instead of keeping what I defined them as originally.
But what I actually want is :
{
models: {
User: User,
Group: Group
}
}
If my list only had User in it, it works fine.
I managed to get this working in the end.
I think my issue was that my GenericModel was treated as a Singleton, so to get around this I changed GenericModel from extending the Sequelize.Model and instead made a new class with a contructor to consume my arguments and then created a method on the new class to return the sequelize model.
The main change there was instead of defining the models with GenericModel.init(), I defined them by calling sequelize.define(modelName, attributes, options)
so my map now looks like this :
const models = modelDefinitions.reduce((acc, modelDef) => {
return { ...acc, [modelDef.name]: new GenericModel(sequelize, modelDef).getDBModel() };
}, {});
and my class:
class TestModel {
constructor(sequelize, modelDef) {
this.sequelize = sequelize;
this.modelDef = modelDef;
this.modelName = modelDef?.name;
this.definitions = modelDef?.fieldDefinitions;
this.restrictedFieldList = this.definitions.filter((field) => field?.restricted).map((definition) => definition.name);
}
getDBModel() {
const model = this.sequelize.define(
this.modelName,
this.definitions.reduce((acc, definition) => {
return { ...acc, [definition.name]: definition.column_type };
}, {}),
{
defaultScope: {
attributes: {
exclude: this.restrictedFieldList,
},
},
sequelize: this.sequelize,
modelName: this.modelName,
}
);
return model;
}
}```

Typeorm is not saving data in table. How to do Migrations in typeorm?

I have this mutation
#Mutation(() => AuthResponse, { nullable: true })
async signUp(
#Ctx() { req }: MyContext,
#Arg("details") details: UsernamePasswordTypes
): Promise<AuthResponse> {
const { password, username } = details;
const user = await User.create({
username,
password: "hasedPassword"
});
try {
await user.save();
req.session.userId = user.id;
console.log("USER CREATED",user)
return {user}
} catch (e) {
return {
errors: [
{
field: "username",
message: "username already taken",
},
],
};
}
}
In this i can see USER created
I have 2 problems
It is not printing password field in console
It is not saving data in postgres db but it have created user table
Note : I am running this code for first time so i think i need to create table and fields so dont know how to. How to create migrations in typeorm ??
You have two ways to create migrations using TypeOrm.
There are two methods you must fill with your migration code: up and down. up has to contain the code you need to perform the migration. down has to revert whatever up changed. down method is used to revert the last migration.
You can manually create them using typeorm migration:create -n migrationName adding your up and down instructions:
import { MigrationInterface, QueryRunner } from 'typeorm';
export class PostRefactoringTIMESTAMP implements MigrationInterface {
async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TABLE "users" ("create_date" TIMESTAMP NOT NULL DEFAULT now(), "update_date" TIMESTAMP NOT NULL DEFAULT now(), "delete_date" TIMESTAMP, "id" SERIAL NOT NULL, "firstName" character varying NOT NULL, "lastName" character varying NOT NULL, "email" character varying NOT NULL, "phone" character varying, "password" character varying NOT NULL)`);
}
async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "users"`);
}
}
Else, you can generate them from your entities using typeorm migration:generate -n generatedMigrationName.
When your migrations are done, you can run them with typeorm migration:run and typeorm migration:revert to use the down method.
Don't forget to setup your connection options properly:
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "test",
"password": "test",
"database": "test",
"entities": ["entity/*.js"],
"migrationsTableName": "custom_migration_table",
"migrations": ["migration/*.js"],
"cli": {
"migrationsDir": "migration"
}
}
If you have more questions, mind reading the TypeOrm documentation about migrations

Sequelize - Postgres - How to create new object from junction table

I'm trying to use a junction table model to create a new object in Sequelize. I have read through the docs pretty thoroughly and do not believe that this use case is covered, unless I am misunderstanding (which is always a possibility).
Docs:
https://sequelize.org/master/manual/assocs.html
https://sequelize.org/master/class/lib/associations/belongs-to-many.js~BelongsToMany.html
Example code:
import { Model as SQLModel } from 'sequelize';
class CarCompany extends SQLModel {};
class BodyStyle extends SQLModel {};
class Model extends SQLModel {};
// A car company can make multiple types of cars
CarCompany.hasMany(BodyStyle);
// A type of car can be made by multiple companies.
// We have a junction table to represent this relationship
BodyStyle.belongsToMany(CarCompany, { through: CarCompanyBodyStyles });
// Each model has only one type that it fits into and only one company that makes it
Model.belongsTo(CarCompanyBodyStyles);
// Now let's create some example car types
const convertible = new BodyStyle({ name: 'Convertible' });
const suv = new BodyStyle({ name: 'SUV' });
// Now let's create some example car companies
const toyota = new CarCompany({ name: 'Toyota' });
const ferrari = new CarCompany({ name: 'Ferrari' });
// Now let's specify which types of cars are made by each company
const toyotaConvertibles = toyota.addBodyStyle(convertible);
const toyotaSUVs = toyota.addBodyStyle(suv);
const ferrariConvertibles = ferrari.addBodyStyle(convertible);
// Now let's define some specific models
const solara = toyotaConvertibles.createModel({ name: 'Solara' });
const rav4 = toyotaSUVs.createModel({ name: 'RAV-4' });
const spider = ferrariConvertibles.createModel({ name: '488 Spider' });
// Now lets see some nested relational objects that we have created:
const toyotaModels = CarCompany.findByPk(
toyota.id,
{
include: [
{
all: true,
nested: true,
},
],
},
);
const ferrariModels = CarCompany.findByPk(
ferrari.id,
{
include: [
{
all: true,
nested: true,
},
],
},
);
console.log({ ferrariModels, toyotaModels });
What I was hoping to see is something like:
{
ferrariModels: {
bodyStyles: [
{
name: 'Convertible',
models: [
{ name: '488 Spider' },
],
},
],
},
toyotaModels: {
bodyStyles: [
{
name: 'SUV',
models: [
{ name: 'RAV-4' },
],
},
{
name: 'Convertible',
models: [
{ name: 'Solara' },
],
},
],
},
}
But instead I get an error:
TypeError: toyotaConvertibles.createModel is not a function
What am I doing wrong? How am I supposed to go about creating this type of relationship?

Change column name Sequilize

I want to change default column name in my final output which comes from Mysql database when i am accessing database using NodeJS and Sequelize connection.
I am connecting using following code:-
import Sequelize from "sequelize";
let dbConnection = new Sequelize('DB', 'abc', 'password', {
host: '127.0.0.1',
port: 4242,
dialect: 'mysql',
dialectOptions: {
requestTimeout: 0,
},
});
const Listing = dbConnection.define('TableName', {
tour_title: {
type: Sequelize.STRING,
}
}, { freezeTableName: true, timestamps: false });
For example, I want to change tour_title by tour_name in the above column only in the final sequelize output. I do not want any change in database column names.
To change column name only in final output
TableName.findAll({
attributes: ['id', ['tour_title', 'tour_name']] /
})
.then((data) => {
console.log(data);
});
To change column name in database
This may help you, follow this steps
Step 1: npm install -g sequelize-cli
To install sequelize command line tool
Step 2: sequelize migration:create --name rename-column-tour_title-to-tour_name-in-tabelname
Step 3: Open newly created migration file and add below code
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.renameColumn('tableName', 'tour_title', 'tour_name');
},
down: (queryInterface, Sequelize) => {
return queryInterface.renameColumn('tableName', 'tour_name', 'tour_title');
}
};
Step 4: in command line run this below command
sequelize db:migrate
Now your column name changed successfully.
If you want an alias for a column name in the ouput , you can do something like this:
TableName.findAll({
attributes: ['id', ['tour_title', 'tour_name']] //id, tour_title AS tour_name
})
.then(function(data) {
res.json(data);
});
Whenever you fetch any records you can write the column name in a cascaded list and make an alias.
Check for the documentation here : http://docs.sequelizejs.com/manual/tutorial/querying.html
The above answers only works for the query results. If you want to keep your underscore name convention on the database and use for example camelCase for your javascript environment you need to do this:
const ChildTable = dbConnection.define('ChildTable', { //this table name is how you call it on sequelize
tourTitle: { //this is the name that you will use on javascript
field: 'tour_title', // this will be the name that you fetch on your db
type: Sequelize.STRING,
},
parentTableId: { // You need to define the field on the model, otherwise on sequelize you'll have to use the db name of the field
field: 'parent_table_id', //this is defined above on the association part
type: Sequelize.INTEGER,
}
}, { freezeTableName: true, timestamps: false, tableName: 'TableName', //this is the real name of the table on the db
underscored: true, });
ChildTable.associate = (models) => {
ChildTable.belongsTo(models.ParentTable, { as: 'ParentTable', foreignKey: 'parent_table_id', onDelete: 'cascade' });
};
This way you have an "alias" for your fields on all the Sequelize / javascript environment and you keep your names on the db. See also that you have to do the same with the foreign keys on associations, otherwise you'll have to use the original name.
I'd add something to #Deepak's answer. When I tried it the Step 4: (I am a newbie), it prompted that
"Cannot find config.json Have you run sequelize init? "
Then I executed that init command then it made a config.json file inside the config folder. So the file contained below objects.
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
Then I executed the sequelize db:migrate. It prompted another error as
Sequelize CLI [Node: 18.7.0, CLI: 6.4.1, ORM: 6.21.4]
Loaded configuration file "config\config.json".
Using environment "development".
ERROR: Access denied for user 'root'#'localhost' (using password: NO)
Then what I did was I changed the config.json file to something like this -->
{
"development": {
"username": "root",
"password": "<this should include your root password>",
"database": "<this should include your db>",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
And then migration was done successfully!

Sequelize .create is not generating content for my model

I looked for this question everywhere but, I haven't found it.
I'm using Sequelize for Postgres, and I'm trying to add content to a model. It's not throwing any errors, except just a warning that says that string based operations have been deprecated, but this isn't an operation.
sequelize deprecated String based operators are now deprecated. Please use Symbol based operators for better security, read more at http://docs.sequelizejs.com/manual/tutorial/querying.html#operators
Here's my model and .create code
const Sequelize = require('sequelize');
const sequelizeInstance = new Sequelize('journal_entries', 'KupidoExportLLC',
{
dialect: 'postgres'
});
module.exports = function(sequelizeInstance, DataTypes){
const Entries = sequelizeInstance.define('entries', {
user: Sequelize.STRING,
title: Sequelize.STRING,
content: Sequelize.TEXT
})
Entries.sync().then(function(){
Entries.create({
user: 'I need to get this to work',
title: 'Ill be really happy when this does work',
content: 'It will work this time'
})
});
return Entries;
}
Here's what my commandline is showing
sequelize deprecated String based operators are now deprecated. Please use Symbol based operators for better security, read more at http://docs.sequelizejs.com/manual/tutorial/querying.html#operators node_modules/sequelize/lib/sequelize.js:242:13
When I go look at my table, it's empty
journal_entries=# SELECT * FROM entries;
id | user | title | content | createdAt | updatedAt
----+------+-------+---------+-----------+-----------
(0 rows)
I have no idea what else it could be
You need to turn off the warnings.
In your config.JSON file or (If you have initialize the connection in the same file)
Turn the warning off, as given:
IN Config.JSON
{
"development": {
"username": "root",
"password": "12345",
"database": "fasttrack",
"host": "localhost",
"dialect": "mysql",
"define": {
"timestamps": false
},
"operatorsAliases": false
},
"test": {
"username": "root",
"password": "12345",
"database": "fasttrack",
"host": "localhost",
"dialect": "mysql",
"define": {
"timestamps": false
},
"operatorsAliases": false
},
"production": {
"username": "root",
"password": "12345",
"database": "fasttrack",
"host": "localhost",
"dialect": "mysql",
"define": {
"timestamps": false
},
"operatorsAliases": false
}
}
Inline declaration of connection
const sequelize = new Sequelize(config.database, config.username, config.password, {
host: config.host,
dialect: config.dialect,
pool: {
max: 5,
min: 0,
idle: 10000
},
logging: false,
freezeTableName: true,
operatorsAliases: false
});
you are working with Promises here, you have either to await or return them.
if you have async await available in your node version you could just do:
module.exports = async function(sequelizeInstance, DataTypes){
const Entries = sequelizeInstance.define('entries', {
user: Sequelize.STRING,
title: Sequelize.STRING,
content: Sequelize.TEXT
});
await sequelizeInstance.sync();
await Entries.create({
user: 'I need to get this to work',
title: 'Ill be really happy when this does work',
content: 'It will work this time'
});
const entries = await Entries.findAll();
console.log(entries.map(e => e.get({ plain: true })));
return Entries;
};
or if not
module.exports = async function(sequelizeInstance, DataTypes){
const Entries = sequelizeInstance.define('entries', {
user: Sequelize.STRING,
title: Sequelize.STRING,
content: Sequelize.TEXT
});
return sequelizeInstance.sync().then(() => {
return Entries.create({
user: 'I need to get this to work',
title: 'Ill be really happy when this does work',
content: 'It will work this time'
});
})
.then(() => {
return Entries.findAll().then(entries => {
console.log(entries.map(e => e.get({ plain: true })));
return entries;
})
});
};

Resources