1

I'm working on a REST API for the backend of a simple e-commerce app using Node JS, PostgreSQL and Sequelize, and I'm facing an issue with Sequelize when I try to add a product to the shopping cart. It returns an error "column Nan does not exist"
Initially I was using Integer for the user Id as the primary key, then I changed for UUID to better suit the purpose.

The code I'm using for the models and migrations is the following:

//User model
export default (sequelize, DataTypes) => {
  const User = sequelize.define(
    'User',
    {
      id: {
        type: DataTypes.UUID,
        defaultValue: DataTypes.UUIDV4,
        allowNull: false,
        primaryKey: true,
        unique: true,
      },
      name: {
        type: DataTypes.STRING,
        allowNull: false
      },
      password: {
        type: DataTypes.STRING,
        allowNull: false
      },
      email: {
        type: DataTypes.STRING,
        allowNull: false,
        unique: {
          name: 'user_email',
          msg: 'A user with this email already exists.'
        }
      },
    },
  User.associate = models => {
    User.hasOne(models.Cart, {
      foreignKey: 'userId',
      as: 'cart',
      onDelete: 'cascade'
    });
  };

  User.associate = models => {
    User.hasMany(models.Order, {
      foreignKey: 'userId',
      as: 'orders',
      onDelete: 'cascade'
    });
  };

  return User;
};

//User migration
export const up = (queryInterface, Sequelize) =>
  queryInterface.createTable('Users', {
    id: {
      type: Sequelize.UUID,
      defaultValue: Sequelize.UUIDV4,
      allowNull: false,
      primaryKey: true,
      unique: true,
    },
    name: {
      allowNull: false,
      type: Sequelize.STRING
    },
    password: Sequelize.STRING,
    email: {
      allowNull: false,
      type: Sequelize.STRING,
      unique: true
    },
    createdAt: {
      allowNull: false,
      type: Sequelize.DATE,
      defaultValue: Sequelize.fn('now')
    },
    updatedAt: {
      allowNull: false,
      type: Sequelize.DATE,
      defaultValue: Sequelize.fn('now')
    },
  });

export const down = queryInterface => queryInterface.dropTable('Users');

Cart model

export default (sequelize, DataTypes) => {
  const Cart = sequelize.define('Cart', {
    id: {
      type: DataTypes.UUID,
      defaultValue: DataTypes.UUIDV4,
      allowNull: false,
      primaryKey: true,
    },
    userId: {
      type: DataTypes.UUID,
      defaultValue: DataTypes.UUIDV4,
      allowNull: false
    },
    cartItem: {
      type: DataTypes.TEXT,
      allowNull: false,
      get(value) {
        return JSON.parse(this.getDataValue(value));
      },
      set(value) {
        this.setDataValue('cartItem', JSON.stringify(value));
      }
    }
  });

  Cart.associate = models => {
    Cart.belongsTo(models.User, {
      foreignKey: 'userId',
      as: 'owner'
    });
  };
  return Cart;
};

Cart migration

export const up = (queryInterface, Sequelize) =>
  queryInterface.createTable('Carts', {
    id: {
      allowNull: false,
      primaryKey: true,
      type: Sequelize.UUID,
      defaultValue: Sequelize.UUIDV4,
    },
    userId: {
      type: Sequelize.UUID,
      defaultValue: Sequelize.UUIDV4,
      allowNull: false
    },
    cartItem: {
      type: Sequelize.TEXT,
      allowNull: false
    },
    createdAt: {
      allowNull: false,
      type: Sequelize.DATE,
      defaultValue: Sequelize.fn('now')
    },
    updatedAt: {
      allowNull: false,
      type: Sequelize.DATE,
      defaultValue: Sequelize.fn('now')
    }
  });

export const down = queryInterface => queryInterface.dropTable('Carts');

Code to handle the add to cart:

addToCart() {
    return this.asyncWrapper(async (req, res) => {
      const { body, user } = req;

      body.userId = user.id;
      const cart = await this.service.addToCart(body);

      this.sendResponse(res, cart, undefined, 201);
    });
  }

Add to cart service

async cart(userId, options = {}) {
    const cart = await super.find({ userId }, options);
    return cart;
  }

  async addToCart(data, options) {
    const { userId, productId, qty } = data;

    const [result] = await this.model.findOrCreate({
      where: { userId: +userId },
      defaults: { cartItem: new CartItem() }
    });

    const cartData = JSON.parse(result.dataValues.cartItem);
    const cartItem = new CartItem(cartData);

    const product = await ProductService.getById(productId, { plain: true });

    ExceptionHandler.throwErrorIfNull(product);

    const cart = cartItem.addToCart(product, qty);

    result.cartItem = cart;
    result.save();

    return result;
  }

The SQL query generated by Sequelize is the following:
SELECT "id","userId","cartItem","createdAt","updatedAt" FROM "Carts" AS "Cart" WHERE "Cart"."userId" = NaN LIMIT 1;

The goal is to use UUID as primary key in the database. This issue started when I changed the Datatype from Integer for UUID and I can't see what is wrong with the code.

Any advice on how to solve this?

Sequelize version: "^5.21.9" with "pg": "^8.2.0" and "pg-hstore": "^2.3.3".
10
  • Can you share your model code, and the code that's passing the userId into the query? Commented Dec 27, 2020 at 4:13
  • @Noah The model code is the Cart mode that is already in the post. The code to handle the query is the following: addToCart() { return this.asyncWrapper(async (req, res) => { const { body, user } = req; body.userId = user.id; const cart = await this.service.addToCart(body); this.sendResponse(res, cart, undefined, 201); }); } Commented Dec 27, 2020 at 4:23
  • show this.service.addToCart code. And please add it to your post and not as a comment here Commented Dec 27, 2020 at 8:41
  • Also you don't need to indicate unique and defaultValue options in PK field in User model because it's already a primary key (that means unique) and its default value is generated by DB. Also you need to indicate autoIncrement: true in PK field in model so Sequelize knows that a default value whould be generated by DB Commented Dec 27, 2020 at 8:44
  • @Anatoly autoIncrement: true doesn't work since I'm using UUID identifier Commented Dec 27, 2020 at 15:39

1 Answer 1

1

If you switched a data type from INTEGER to UUID you shouldn't try to convert UUID-string to a number doing where: { userId: +userId }.

Pass userId as is:

where: { userId }
Sign up to request clarification or add additional context in comments.

1 Comment

It worked ! Thank you! It was a silly mistake from my part, I totally forgot to change this conversion. Now it's working fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.