Exclude user's password from query with Prisma 2 - node.js

Recently I started working on a new project to learn some new technologies (Prisma 2, REST api with Express, etc.). Tho, I faced a problem.
My app has a user authentication system and the user model has a password column. So, when the client requests a user, the backend selects all the columns from the database including the password (that's hashed by the way).
I tried to not select the password column on the prisma findMany, like this:
await prisma.user.findUnique({
where: {
...
},
select: {
password: false
}
});
But I got an error by prisma saying that the select should contain at least one truly value. Thus, I added id: true to the select. I made an api request and I saw that only the id was returning for the user.
By my understanding, prisma expects me to add all the columns I care to the select object. But, I need a lot of columns from the user and I am making a lot of queries to fetch users and I cannot just write all the field I need everytime.
So, I wanted to ask you if there is a legit way to do that.
PS: I don't take "use rawQuery instead" as a solution.

The only legit way is adding column: true to the columns you want to include. There are requests for excluding columns here so it would be great if you could add a 👍 to the request relevant to you so that we can look at the priority.
https://github.com/prisma/prisma/issues/5042
https://github.com/prisma/prisma/issues/7380
https://github.com/prisma/prisma/issues/3796

I've been wondering about how to implement this as well, and bafflingly the issues linked in #Ryan's post are over two years old, and still unresolved. I came up with a temporary workaround, which is to implement a middleware function for the Prisma client which removes the password field manually after each call.
import { PrismaClient } from '#prisma/client'
async function excludePasswordMiddleware(params, next) {
const result = await next(params)
if (params?.model === 'User' && params?.args?.select?.password !== true) {
delete result.password
}
return result
}
const prisma = new PrismaClient()
prisma.$use(excludePasswordMiddlware)
This will check if the model being queried is a User, and it will not delete the field if you explicitly include the password using a select query. This should allow you to still get the password when needed, like when you need to authenticate a user who is signing in:
async validateUser(email: string, password: string) {
const user = await this.prisma.user.findUnique({
where: { email },
select: {
emailVerified: true,
password: true,
},
})
// Continue to validate user, compare passwords, etc.
return isValid
}

Check out the following code
Exclude keys from user
function exclude(user, ...keys) {
for (let key of keys) {
delete user[key]
}
return user
}
function main() {
const user = await prisma.user.findUnique({ where: 1 })
const userWithoutPassword = exclude(user, 'password')
}
reference
prima official Website

Related

Perform check on record before performing update with Prisma

I'm creating the backend for a simple app which allows users to create, update, and delete products. Using Express as my framework, with Postgres as my DB and Prisma, which I'm new to, as my ORM. Users and products have a one-to-many relationship. Prisma's documentation states that when updating a record, you should use the update method - so to update the name of a product with a given ID, your code would look something like this:
export const updateProduct = async (req, res) => {
const [productID, newProductName, userID] = [req.params.id, req.body.name, res.locals.user.id];
const product = await prisma.product.update({
where: {
id: productID,
},
data: {
name: newProductName
}
});
res.status(200);
res.json(product);
};
However, there's a problem here - I'm not checking to see that the product with the provided ID belongs to the user that has sent the request to update it. I have the ID of the user who has sent the request in the variable userID, and each product in the DB has a field belongsToID which is set to the ID of the user that the product belongs to. I should theoretically therefore be able to modify my query to get the product with the specified ID and a matching belongsToID like so:
export const updateProduct = async (req, res) => {
const [productID, newProductName, userID] = [req.params.id, req.body.name, res.locals.user.id];
const product = await prisma.product.update({
where: {
id: productID,
belongsToID: userID
},
data: {
name: newProductName
}
});
res.status(200);
res.json(product);
};
That, however, does not work - I get the following error: Type '{ id: any; belongsToID: any; }' is not assignable to type 'ProductWhereUniqueInput'. Object literal may only specify known properties, and 'belongsToId' does not exist in type 'ProductWhereUniqueInput'.ts(2322).
It appears that when trying to do a 'findUnique', Prisma doesn't allow non-unique fields to be used in the query (even if the combination of both fields is unique, as is the case here). I do get that logically, my query doesn't make much sense - the ID alone is already enough to find a unique entry without the second field, so in that sense, the second field is totally redundant. Yet how else am I meant to check that the belongsToID is what it should be before updating the record? Is there somewhere else within the object passed to .update where I can provide a check to be performed on the retrieved record before performing the update?
I believe that creating an index would allow me to query for both fields at once - but why should I have to create an index when the ID (which is already indexed) alone is all I need to retrieve the record I need? What I really need is a way to perform a check on a retrieved record before performing the update when using Prisma.table_name.update(), not a way to query for something with a unique combination of fields.

I want send user info except password from the express server along with jwt token

I'm creating a MERN stack ecommerce application where I want send all user info along with jwt token but except password I'm ok with token part & I know how to send user but i don't know how to exclude the password property while sending the user through res.json
enter image description here
Modified Answer -
#prathamesh
You can change the default behavior at the schema definition level using the select attribute of the field:
password: { type: String, select: false }
Then you can pull it in as needed in find and populate calls via field selection as '+password'. For example:
Users.findOne({_id: id}).select('+password').exec(...);
You can use the aggregation or select method in the mongoose.
const users = await User.find({}, {name: 1, email: 1});
or
const users = await User.find({}).select("name email");
or
const users = await User.aggregate([{"$project": {name: 1, email: 1}}]);
I use this way to save all attributes except password in another variable and then I show info.
let {password, ...foundUser} = user.toJSON();
response.setStatus(200).setRes(foundUser);

Retrieve inserted row using pg-promise npm module in Express.js

I am inserting a user row into Postgres from Express.js like as follows:
db.none("INSERT INTO users (email, password) VALUES (${incoming.email}, crypt(${incoming.password}, gen_salt('bf')))",{
incoming: { email: qemail, password: qpwd}
}
).then(function (data) {
// data is null
});
The email and password come from the query parameters on the React.js frontend. I use this query for the signup action on my application and wanted to know if I could get this newly added user back from this same query. I just felt that making another get request right after the signup to get the user info was inefficient, which I do for my login action. Thanks in advance.
Append RETURNING * to your query, and replace none with method one.
const row = await db.one("INSERT INTO users(email, password) VALUES(${incoming.email},
crypt(${incoming.password}, gen_salt('bf'))) RETURNING *",
{
incoming: { email: qemail, password: qpwd}
}
);
// row = the newly inserted row
At the end of the query add RETURNING *. This should return the newly added query. (postgresql docs).
In your query:
"INSERT INTO users (
email, password
) VALUES (
${incoming.email}, crypt(${incoming.password}, gen_salt('bf'))
) RETURNING *"

How to hide a field in the callback of document.save() in mongoose?

I want display the data of a user after its been created, returning the user inside a async function using the following code:
const newUser = await user.save()
return newUser
But I don't want it to be possible for the password to be seen.
I've already encountered this topic which contains a similar issue, and the best approach presented there is by placing select: false inside the password field of the schema definition. It works for find functions, but it doesn't for the document.save() callback.
Currently I'm achieving what I want by using a spread operation and resetting the password field:
const newUser = await user.save()
return { ...newUser.toObject(), password: null }
Is there a better approach to achieve this?
use lodash, it is better library with clean code
return _.omit(newUser, ['password']);
or second thing you can use post save on schema level
schema.post('save', function(doc) {
delete doc.password;
});

meteor-roles not working as expected it returning false

we are using meteor METEOR#1.4.0.1 and added alanning:roles for managing role.
below code that we using for apply role.
Accounts.onCreateUser(function(options, user) {
console.log("on account create");
Roles.addUsersToRoles(user._id,'admin')
console.log(Roles.userIsInRole(Meteor.userId(),'admin'));
return user;
});
Roles.userIsInRole(Meteor.userId(),'admin') return false
help us what wrong.
with alanning-roles, you don't need to include that to Accounts.onCreateUser.
You just need to use Roles.addUserToRoles() after you've inserted the new document. Like so:
const id = Accounts.createUser({
email: user.email,
password: "apple1",
profile: { name: user.name }
});
Roles.addUsersToRoles(id,'whateverRole')
Based on their README.md:
Note that the Roles.addUsersToRoles call needs to come after Accounts.createUser or Accounts.onCreate or else the roles package won't be able to find the user record (since it hasn't been created yet).

Resources