How do I skip the primary key auto increment in sequelize node.js when unique constraint error occurs
When I enter same username twice that was defined as unique into mysql by using of Postman my program is running correct way but the problem is the incremental primary key is still continuing.
For example
when I insert another different username value the program is jumping at one of the sequential primary key as expected.
So that, How can I stop the auto increment id as I restricted not to insert duplicate username values in my database
/* DATABASE CONFIGURATION FILE */
const { Sequelize, QueryTypes, DataTypes, Op, UniqueConstraintError, ValidationErrorItem } = require(`sequelize`);
const sequelize = new Sequelize(`tutorialdb`, `root`, ``, {
host: `localhost`,
dialect: `mysql`,
logging: true,
pool: {
max: 5,
min: 0,
acquire: 30000,
idle: 10000,
},
});
sequelize
.authenticate()
.then(() => {
console.log(`Connection has been established successfully...`);
})
.catch((err) => {
console.log(`Unable to connect to the database: `, err);
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.QueryTypes = QueryTypes;
db.DataTypes = DataTypes;
db.Op = Op;
db.ValidationErrorItem = ValidationErrorItem;
db.UniqueConstraintError = UniqueConstraintError;
db.postModel = require(`../models/post.model.jsx`)(sequelize, DataTypes);
db.sequelize.sync({ force: false, alter: false, match: /tutorialdb$/ }).then(() => {
console.log(`Tables were synced successfully`);
});
module.exports = db;
/* Model definition File */
module.exports = (sequelize, DataTypes) => {
const Post = sequelize.define(
`post`,
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
primaryKey: true,
autoIncrement: true,
},
title: {
type: DataTypes.STRING(30),
allowNull: false,
validate: {
notEmpty: {
args: true,
msg: `Title is required`,
},
len: {
args: [3, 50],
msg: `Title must between 3 and 30 characters`,
},
},
},
text: {
type: DataTypes.STRING(100),
allowNull: false,
validate: {
notEmpty: {
args: true,
msg: `Text is required`,
},
len: {
args: [5, 100],
msg: `Text must between 5 and 100 characters`,
},
},
},
username: {
type: DataTypes.STRING(20),
allowNull: false,
unique: true,
validate: {
notEmpty: {
args: true,
msg: `Username is required`,
},
len: {
args: [3, 20],
msg: `Username must between 3 and 20 characters`,
},
},
},
},
{
timestamps: true,
paranoid: true,
}
);
Post.beforeCreate(async (post, options) => {
post.username = post.username.toLowerCase();
});
Post.beforeUpdate(async (post, options) => {
post.username = post.username.toLowerCase();
});
return Post;
};
/* Controller File */
const db = require(`../config/db.config.jsx`);
const postModel = db.postModel;
const Sequelize = db.Sequelize;
const sequelize = db.sequelize;
const QueryTypes = db.QueryTypes;
const DataTypes = db.DataTypes;
const Op = db.Op;
const ValidationErrorItem = db.ValidationErrorItem;
const UniqueConstraintError = db.UniqueConstraintError;
/* Create new Post */
exports.create = async (req, res) => {
const transactions = await sequelize.transaction();
try {
const trim = (noSpace) => {
return noSpace.replace(/s/g, ``);
};
const post = await postModel.create(
{
title: req.body.title,
text: req.body.text,
username: trim(req.body.username),
},
{ transaction: transactions }
);
await transactions.commit();
res.status(200).json(post);
} catch (err) {
await transactions.rollback();
const messages = {};
let message;
err.errors.forEach((error) => {
messages[error.path] = error.message;
message = messages[error.path];
});
res.status(500).json(message);
}
};
/* Find All posts */
exports.findAll = async (req, res) => {
const transactions = await sequelize.transaction();
try {
const title = req.query.title;
const text = req.query.text;
const username = req.query.username;
let finder = title ? { title: { [Op.like]: `%${title}%` } } : text ? { text: { [Op.like]: `%${text}%` } } : username ? { username: { [Op.like]: `%${username}%` } } : null;
const posts = await postModel.findAll({
as: `posts`,
attributes: [`id`, `title`, `text`, `username`, `createdAt`, `updatedAt`, `deletedAt`],
transaction: transactions,
lock: false,
paranoid: false,
order: [[`id`, `DESC`]],
where: finder,
});
await transactions.commit();
res.status(200).json(posts);
} catch (err) {
await transactions.rollback();
res.status(500).json(err.message);
}
};
/* Router File */
module.exports = (app) => {
const router = require(`express`).Router();
const postCtrl = require(`../controllers/post.controller.jsx`);
router.route(`/post`).post(postCtrl.create).get(postCtrl.findAll);
app.use(`/api/v1`, router);
};
/* MiddleWare Logger File */
const moment = require(`moment`);
/* Create Logger */
const logger = (req, res, next) => {
console.log(`${req.protocol}://${req.get(`host`)}${req.originalUrl} : ${moment().format()}`);
next();
};
module.exports = logger;
/* Server File */
const express = require(`express`);
const cors = require(`cors`);
const logger = require(`./src/middleware/logger.jsx`);
const app = express();
const corsOptions = {
origin: `http://localhost:4001`,
optionsSuccessStatus: 200,
};
app
.use(cors(corsOptions))
.use(logger)
.use(express.json())
.use(express.urlencoded({ extended: false }))
.get(`/`, (req, res) => res.status(200).send(`Welcome to fullstack tutorial application`));
require(`./src/routes/routers.jsx`)(app);
const PORT = process.env.PORT || 4000;
app.listen(PORT, () => console.log(`Server is running on port ${PORT}...`));
The output result is working well. But the primary Key auto-increment is still continuing
http://localhost:4000/api/v1/post : 2022-08-28T11:02:47+03:00
Executing (ac12d76f-d7dc-4040-9692-3d6b853feac9): START TRANSACTION;
Executing (ac12d76f-d7dc-4040-9692-3d6b853feac9): INSERT INTOposts
(id
,title
,text
,username
,createdAt
,updatedAt
) VALUES
(DEFAULT,?,?,?,?,?); Executing (ac12d76f-d7dc-4040-9692-3d6b853feac9):
ROLLBACK;
3
Answers
I had attempted the following solution and works me perfectly.
I am not sure about issue’s existence in previous versions of sequelize. But this issue does not exist if using Object.findOrCreate() with following mentioned versions.
However this issue does appear if using Object.create() method with unique constraint set for field value and not checking field value existence prior to using Object.create() e.g in following code email unique property is set and if user.create() is used for an existing email in db an error is thrown but userid is incremented thus for next successful creation userid is not as expected.
An alternate solution is using user.findOne() prior to use user.create() but out of the scope of this answer and issue can be avoided using object.findOrCreate() as following
Versions: "mysql2": "^2.3.3", "sequelize": "^6.28.0"
To avoid the issue try using following approach