Sergey Sovgut
Sergey Sovgut

Reputation: 389

Sequelize `findOne` working was not as expected

I ran into a problem that I have not been able to solve for a week ...

When I pass the user's email to a query to the database, sequelize seems to do everything right and executes this query, it returns data from the table with the found result, but the result of executing the findOne function is not a model, why did this happen and how can I fix it?

const email = '[email protected]'
const user = await User.findOne({ where: { email }})

console.log(user.password) // undefined

Here is the model, service and database connection

Model

import { DataTypes, Model } from "sequelize"
import { connection } from "./database"

class User extends Model {
  readonly id: number
  readonly email: string
  password: string
  username: string
  readonly createdAt: Date
  readonly updatedAt: Date
}

User.init(
  {
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
    },
    email: {
      type: DataTypes.STRING,
      unique: true,
    },
    password: {
      type: DataTypes.STRING,
    },
    username: {
      type: DataTypes.STRING,
    },
  },
  {
    sequelize: connection,
    modelName: "users",
    createdAt: "created_at",
    updatedAt: "updated_at",
  }
)

export default User

Service

export async function loginUser(email: string, password: string):  Promise<ServiceResponse<string>> {
  try {
    const user = await User.findOne({ where: { email } })
    if (!user) {
      throw Error("User not found")
    }

    if (!bcrypt.compareSync(password, user.password)) {
      throw Error("User not found or password did not match")
    }

    const { password: _, ...payload } = user.toJSON()
    const token = jwt.sign(payload, environment.jwt)

    return { success: true, content: token }
  } catch (error) {
    return { success: false, error: error.message }
  }
}

Database connection

import { Sequelize } from "sequelize"
import { environment } from "../environment"

export const connection = new Sequelize(environment.database, {
  dialect: "postgres",
  logging: console.log,
  dialectOptions: {
    ssl: {
      rejectUnauthorized: false,
    },
  },
})

Here is log

Executing (default): SELECT "id", "email", "password", "username", "created_at", "updated_at" FROM "users" AS "users" WHERE "users"."email" = '[email protected]';
users {
  dataValues: {
    id: 9,
    email: '[email protected]',
    password: '$2b$10$s0HFL9eBbcp3GcGkB9cDZuSiCjOEgfQB5lZLxhxXraRfvRF6voDfW',
    username: null,
    created_at: 2020-11-16T13:09:30.631Z,
    updated_at: 2020-11-16T13:09:30.631Z
  },
  _previousDataValues: {
    id: 9,
    email: '[email protected]',
    password: '$2b$10$s0HFL9eBbcp3GcGkB9cDZuSiCjOEgfQB5lZLxhxXraRfvRF6voDfW',
    username: null,
    created_at: 2020-11-16T13:09:30.631Z,
    updated_at: 2020-11-16T13:09:30.631Z
  },
  _changed: Set(0) {},
  _options: {
    isNewRecord: false,
    _schema: null,
    _schemaDelimiter: '',
    raw: true,
    attributes: [
      'id',
      'email',
      'password',
      'username',
      'created_at',
      'updated_at'
    ]
  },
  isNewRecord: false,
  id: undefined,
  email: undefined,
  password: undefined,
  username: undefined,
  createdAt: undefined,
  updatedAt: undefined
}

Used stack NextJS, Sequelize, PostgreSQL

What I'm doing wrong?

Upvotes: 1

Views: 8076

Answers (4)

Asad Abbas
Asad Abbas

Reputation: 1

By default, Sequelize returns an instance of the model, which may contain some extra properties and methods specific to Sequelize models. To restrict it by doing so and in order to get only the specific model data you need to add raw:true , then you will be able to directly manipulate the model object. I'm attaching an example that how it would be worked.

enter image description here

Upvotes: 0

Sergey Sovgut
Sergey Sovgut

Reputation: 389

Thanks, the answer is Model.get({ plain: true }) before manipulating with received data.

Upvotes: 0

Andrew
Andrew

Reputation: 88

Model instances operate with the concept of a dataValues property, which stores the actual values represented by the instance. As you can see in your printout under dataValues you have your model:

Executing (default): SELECT "id", "email", "password", "username", "created_at", "updated_at" FROM "users" AS "users" WHERE "users"."email" = '[email protected]';
users {
  dataValues: {
    id: 9,
    email: '[email protected]',
    password: '$2b$10$s0HFL9eBbcp3GcGkB9cDZuSiCjOEgfQB5lZLxhxXraRfvRF6voDfW',
    username: null,
    created_at: 2020-11-16T13:09:30.631Z,
    updated_at: 2020-11-16T13:09:30.631Z
  },
  _previousDataValues: {
      (...)

To get data from this dataValues field you can use get method. Also findOne return a promise.

So taking it together, in your case I think this should work

const email = '[email protected]'
User.findOne({ where: { email }}).then(data => {
    console.log(data.get('password')
});

Take a look at Sequelize Model documentation

Upvotes: 1

Did you check your user object is it also undefined?

Try to do user.dataValues.password to get password. Or you can add raw: true like this const user = await User.findOne({ raw:true, where: { email }}).Sequelize will return only data not the model instance. So you can use user.password

Upvotes: 1

Related Questions