Alphanumeric ID in sequelize.js - node.js

I'm using sequelizejs with PostgreSQL as ORM in my webapp, I want to use alphanumeric ID instead numeric.How can I do that ?
Is there some way to do it through sequelize?
Or do I want to generate this id separately and then save it into db ?
Thanks for any help.

You can use a UUID as primary key, and set its default value to uuidv1 or v4
http://docs.sequelizejs.com/en/latest/api/datatypes/#uuid
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true
}

Related

Sequelize: how to let the database handle primary key value?

I'm using sequelize 6.5.0. I created a simple model to do two rudimentary things: a) find records, b) create records. I'm having trouble creating records; specifically, ones with primary key. If I designate the column as primaryKey like so:
const Table = sequelize.define('table', {
id: {
type: DataTypes.UUID,
primaryKey: true
},
datum: {...}
...
and try to create a record like so:
Table.create({datum: 'abc'})
then it will try (and fail) to set the primary key with:
INSERT INTO "table" ("id","datum") VALUES ($1,$2) RETURNING ...;
which is 50% what I did not ask it to do. Now, I don't need this to happen since default value for id is already handled at the database level. So, the next natural move was to not designate id as primaryKey:
const Table = sequelize.define('table', {
id: {
type: DataTypes.UUID,
// primaryKey: true
},
datum: {...}
...
But now sequelize attempts to get smart and throws a tantrum:
Uncaught Error: A column called 'id' was added to the attributes of 'table' but not marked with 'primaryKey: true'
Q) How do I get sequelize to NOT handle primary key on create?
I think you can skip the id field in the definition altogether, and PostgreSQL will still have one

use mongoose schema over multiple microservices

my application is split into multiple microservices that are running on heroku dynos (they can't access each others files). Sometimes, there are multiple microservices working with one collection. Therefore, both of the microservices need the according mongoose schema.
However, not both microservices need the full schema. For example, microservice A needs the full schema whereas microservice B only needs a few fields of that schema.
Example schema inside microservice A:
var AccountSchema = mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
phone: { type: String, required: true, unique: true },
forename: { type: String, required: true },
surname: { type: String, required: true },
middleInitals: { type: String, required: false },
failedLoginAttempts: { type: Number, required: true, default: 0 },
lockUntil: { type: Number },
createdAt: { type: Date, default: Date.now }
})
Example Schema inside microservice B:
var AccountSchema = mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
failedLoginAttempts: { type: Number, required: true, default: 0 },
lockUntil: { type: Number },
createdAt: { type: Date, default: Date.now }
})
My approach
I would just go ahead and create a new schema in each microservice, containing only the needed fields. However, I am not sure if there will be any problems when multiple microservices register a new schema to the MongoDB database? For example, both microservices would attempt to create an index for the unique field. Will there be any performance issues?
Does anybody have a different approach I could use? Is this even a valid approach to go with?
Thanks in advance :)
It's a valid approach. you can have 2 schemas pointing to the same collection. i have tested it and it works.
Mongoose is an Object Data Modeling (ODM) library, and you can have 2 objects looking at the same collection /(Table or view in SQL) - no problem with that.
No reason for performance problems, so long you got the right index. no relation to Object Data Modeling.
You might want to add some type key, so you can find only type1/type2 accounts on get request. On find, you can restrict getting the right fields with projection.
I think you should have only 2 keys in the index – email + password. If you have phone index and microservice B: don't include a phone –you will have a violation at the unique index of phone.
But if you really want a unique phone index you can make an override. You can generate temp unique value for phone for mircoservice B (using auto-Generated or duplicate the email value), you will ignore this value on mircoservice B and only find/ update / present phone in microsaervice A, where you have a valid phone. When user change from accountB type to accountA type – you must make sure to replace the wrong phone number with a valid one.
I see no problem in 2 schemas to same collection - you just need to mange your indexes the right way – to avoid collisions, and to insure you can differentiate the different accounts type in the collection.
As far as I can see there is no reason you simply can't use the same schema, maybe use some sort of privilege system and have it only return the right information between these separate micro services. You could have a fetch request tell it from which service its coming from and return a which items using a simple conditional.

Customizing sequelize-cli generated IDs

Created a model using:
sequelize-cli model:create --name User --attributes "dispName:string,email:string,phoneNum1:string,vendorId:integer"
Which resulted in the following migration:
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
dispName: {
type: Sequelize.STRING
},
// plus others...
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
I want to change the automatically defined ID to:
cognitoId: {
allowNull: false,
primaryKey: true,
type: Sequelize.STRING(100)
}
So:
Will sequelize be able to recognize this as the ID?
Where all do I need to make this change? I could only identify the migration file.
The model file doesn't have a definition for the cognitoId (or the original auto-generated id field): how will I be able to get the value of a User instance's cognitoId (in the data returned by queries)?
Will changing the auto-generated id field have repercussions down the line?
Is the field name id "magical"? I.e., does the primary key have to be named id?
Is there a better way to do this?
Will changing the types of the fields from Sequelize.STRING to Sequelize.STRING(100) create any issues down the line?
Why doesn't the models file generated by sequelize-cli have the id field defined?
When generating models+migrations from the command-line I couldn't find any syntax to specify the ID or any other customization for the fields.
Using:
[Node: 12.14.1, CLI: 5.5.1, ORM: 5.21.3]
PS: relatively new to NodeJS & completely new to Sequelize.
Yes
You should declare custom named PK in your model
see p.2. If you don't declare PK in your model then sequelize assumes you have id PK with an integer type, autoincremented. If you wish to assign your PK another name you should declare it in the model.
Depends on what changes you make
It is the default PK name in sequelize (see p.3). You can set different name to your PK manually declaring it in your model (see p.3)
Personally I prefer to declare all PKs in my models even if they have id name and default PK type and value.
No issues if all PK values do not exceed this length
see p.3
You can define names and types only for fields while generating models from the command line.

How to remove unique option using sequelize

I am using sequelize at nodejs.
When I was making, I set the unique option in a column called 'invoice'.
But since I need to remove the unique option, I have to use migration.
queryInterface.removeConstraint('my_some_table', 'my_constraint');
I saw this command, and I think it is not correct method for me.
How can I remove 'unique option' using migration at sequelize?
invoice: {
type: DataTypes.STRING(50),
unique: true, <<-- I want to remove this.
allowNull: false,
},
This is from the Sequelize documentation and allows you to change the meta data on a column:
queryInterface.changeColumn(
'nameOfAnExistingTable',
'nameOfAnExistingAttribute',
{
type: Sequelize.FLOAT,
allowNull: false,
defaultValue: 0.0
}
)

Sequelize Composite Foreign Key

I have a database with the following tables:
CREATE TABLE IF NOT EXISTS `app_user` (
`user_id` INT NOT NULL,
`user_name` VARCHAR(45) NOT NULL,
PRIMARY KEY (`user_id`))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `user_folder` (
`user_id` INT NOT NULL,
`document_id` INT NOT NULL,
PRIMARY KEY (`user_id`, `document_id`),
CONSTRAINT `fk_user_document_user`
FOREIGN KEY (`user_id`)
REFERENCES `zinc`.`app_user` (`user_id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS `folder_content` (
`user_id` INT NOT NULL,
`document_id` INT NOT NULL,
`content_id` INT NOT NULL,
PRIMARY KEY (`user_id`, `document_id`, `content_id`),
CONSTRAINT `fk_folder_content_folder`
FOREIGN KEY (`user_id` , `document_id`)
REFERENCES `zinc`.`user_folder` (`user_id` , `document_id`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB;
I need to create a Sequelize model to represent it. The only problem I have is with the relation folder_content and user_folder because of the composite key.
How can I create this sequelize model?
This is what I have so far:
var AppUser = sequelize.define('app_user',
{userId: {type: Sequelize.INTEGER, primaryKey: true, field: 'user_id'}, ... } );
var UserFolder = sequelize.define('user_folder',
{userId: {type: Sequelize.INTEGER, primaryKey: true, field: 'user_id'},
documentId: {type: Sequelize.INTEGER, primaryKey: true, field: 'document_id'}... });
var FolderContent = sequelize.define('folder_content', {
userId: {type: Sequelize.INTEGER, primaryKey: true, field: 'user_id'},
documentId: {type: Sequelize.INTEGER, primaryKey: true, field: 'document_id'},
contentId: {type: Sequelize.INTEGER, primaryKey: true, field: 'content_id'}... });
UserFolder.hasMany(FolderContent);
FolderContent.belongsTo(UserFolder, {foreingKey: !! });// <- PROBLEM
Now Sequelize doesn't support composite foreign keys. This creates several problems.
When Sequelize creates a table, the table definition does not have a composite FK.
To solve this problem I use the afterSync hook on the model and a function
that adds a FK to the table if it does not exist. Example code.
When I use the findAll method with include such model, I use the include[].on option of the findAll method. Or if you don't use as many joins as I do, you can use scope when creating an association (see).
does sequelize still not support composite foreign keys? i would like to have a table with 2 foreign keys as the composite primary key. i would rather have it this way than have a surrogate key as the primary key with a unique constraint over the 2 foreign key fields.
thanks

Resources