How to return only specific attributes when using Sequelize Create method
Asked Answered
R

6

8

I have been searching through Sequelize documentation and forums for the correct syntax and it seems I am doing it the right way, but for some reason the password field is still being returned in the response payload...

The following link shows the attributes exclude syntax I am using was added in version 3.11 of Sequelize: https://github.com/sequelize/sequelize/issues/4074

Anyone know what I might be missing here? Below is the Create method and the console log from the Insert statement.

Create method

async create(req, res) {
try {
    let user = await User.create({
        firstName: req.body.firstName,
        lastName: req.body.lastName,
        email: req.body.email,
        password: req.body.password
    }, {
        attributes: {
            exclude: ['password']
        }
    });

    console.log("USER: ", user);

    res.status(201).send(user.toJSON());
}
catch (error) {
    res.status(500).send(error)
};

}

Console Log

Executing (default): INSERT INTO "Users" ("id","firstName","lastName","email","password","createdAt","updatedAt") VALUES (DEFAULT,'James','Martineau','[email protected]','$2b$10$7ANyHzs74OXYfXHuhalQ3ewaS4DDem1cHMprKaIa7gO434rlVLKp2','2019-02-28 15:18:15.856 +00:00','2019-02-28 15:18:15.856 +00:00') RETURNING *;

USER: User { dataValues: { id: 6, firstName: 'James', lastName: 'Martineau', email: '[email protected]', password: '$2b$10$7ANyHzs74OXYfXHuhalQ3ewaS4DDem1cHMprKaIa7gO434rlVLKp2', updatedAt: 2019-02-28T15:18:15.856Z, createdAt: 2019-02-28T15:18:15.856Z }...

Rules answered 28/2, 2019 at 15:36 Comment(1)
i'm still looking for this like you needed. You finded one way to do this without property.delete? delete all my properties seems not to be a dry solutionEntryway
P
4

I see in the document, you can't exclude attributes when you create a model. Only exclude when you find a model.

I suggest:

async create(req, res);
{
    try {
        let user = await User.create({
            firstName: req.body.firstName,
            lastName: req.body.lastName,
            email: req.body.email,
            password: req.body.password
        });
        delete user["password"];//delete field password
        console.log("USER: ", user);

        res.status(201).send(user.toJSON());
    }
    catch (error) {
        res.status(500).send(error);
    };
}
Prescott answered 1/3, 2019 at 3:10 Comment(1)
Here was the final result delete user.dataValues.password Querying the database again to get the specific attributes would be more costly.Rules
S
10

The proper way to handle this is to leverage the afterCreate and afterUpdate hooks on the actual data model, that Sequelize exposes. These hooks are fired after the record is persisted, so any mutations to the dataValues will only be reflected in the return.

sequelize.define(
    'User',
    {
        id: { type: DataType.UUID, defaultValue: Sequelize.UUIDV4, primaryKey: true },
        username: { type: DataType.STRING, allowNull: false },
        password: { type: DataType.STRING, allowNull: false }
    },
    {
        hooks: {
            afterCreate: (record) => {
                delete record.dataValues.password;
            },
            afterUpdate: (record) => {
                delete record.dataValues.password;
            },
        }
    }
);

Here is a link to the documentation: https://sequelize.org/master/manual/hooks.html

Seascape answered 19/11, 2020 at 15:20 Comment(1)
Better solution and practical approachSacerdotalism
P
4

I see in the document, you can't exclude attributes when you create a model. Only exclude when you find a model.

I suggest:

async create(req, res);
{
    try {
        let user = await User.create({
            firstName: req.body.firstName,
            lastName: req.body.lastName,
            email: req.body.email,
            password: req.body.password
        });
        delete user["password"];//delete field password
        console.log("USER: ", user);

        res.status(201).send(user.toJSON());
    }
    catch (error) {
        res.status(500).send(error);
    };
}
Prescott answered 1/3, 2019 at 3:10 Comment(1)
Here was the final result delete user.dataValues.password Querying the database again to get the specific attributes would be more costly.Rules
C
4

 User.create(req.body).then(user => {
    delete user.dataValues.password
    res.json(user)
  }).catch(error => {
   // do something with error
  })
Cultch answered 13/3, 2019 at 15:37 Comment(0)
A
2

Try overloading Sequelize Model class with your desired functionality. For example, run following code once during application bootstrap:

import {Model} from 'sequelize';

const toJSON = Model.prototype.toJSON;

Model.prototype.toJSON = function ({attributes = []} = {}) {
    const obj = toJSON.call(this);

    if (!attributes.length) {
      return obj;
    }

    return attributes.reduce((result, attribute) => {
      result[attribute] = obj[attribute];

      return result;
    }, {});
  };

After that, you can use your code as usual, but with an attributes option:

User.toJSON({attributes: ['name', 'etc...']}).

Aprilette answered 23/10, 2019 at 22:55 Comment(0)
C
2

I know it's an old question, but it's a problem i faced recently. The way I solved this, is like this:

try {
    const { firstName, lastName, email } = await User.create({
        firstName: req.body.firstName,
        lastName: req.body.lastName,
        email: req.body.email,
        password: req.body.password
    })
    const user = { firstName, lastName, email }

}

     console.log("USER: ", user);

     res.status(201).send(user.toJSON());
}
catch (error) {
     res.status(500).send(error)
};

You can instantiate the fields you want like this, at least it's what i'm doing everywhere in my code

hope this works for you too :)

Commencement answered 20/5, 2021 at 14:42 Comment(0)
H
1

With a quick read through the docs, it seems attributes is only mentioned within queries like:

Model.findAll({
  attributes: { exclude: ['baz'] }
});

(http://docs.sequelizejs.com/manual/tutorial/querying.html#attributes)

If you want to exclude password with create, you could do something like:

let user = await User.create({
    firstName: req.body.firstName,
    lastName: req.body.lastName,
    email: req.body.email,
    password: req.body.password
}, {
    fields: ['firstName', 'lastName', 'email']
});

(http://docs.sequelizejs.com/manual/tutorial/instances.html#creating-persistent-instances)

Hypotension answered 28/2, 2019 at 15:56 Comment(5)
Thanks Scott, but I don't think your response is correct. The documentation seems to state that the fields parameter dictates what fields will be set. This further makes sense as when i attempted to implement your suggestion by Sequelize User model blew up because the password was not provided, only the 3 fields defined were.Rules
Ah, perhaps I misunderstood your intent. Have you tried: User.create({ //... {include: ['firstName', 'lastName', 'email']} })?Hypotension
The include parameter is used for model associations. Since this is simply a Create function and no associations are being added here, the include parameter would not be appropriate. I am basically trying to add the attributes parameter for the Sequelize.create but apparently there's a different way to do it...?Rules
Hmm...the only thing I can think of now is to first create then query for the same record with findOne and return that. That way you can use attributes to exclude password.Hypotension
Seems like it should also be possible to use findOrCreate; e.g., let [user] = await User.findOrCreate({ firstName: req.body.firstName, lastName: req.body.lastName, email: req.body.email, password: req.body.password }, { attributes: { exclude: ['password'] } }); Let me know if that works and I'll gladly edit the answer.Hypotension

© 2022 - 2024 — McMap. All rights reserved.