AngryDog
AngryDog

Reputation: 25

Node JS API Sequelize PostgreSQL UUID as primary key return error "column Nan does not exist"

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".

Upvotes: 1

Views: 2512

Answers (1)

Anatoly
Anatoly

Reputation: 22758

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 }

Upvotes: 1

Related Questions