Let's say I have a products collection, and there is a relation to the users collection, where a user has many products. How can I hide products that aren't related to that specific user? I couldn't find a "filter" property. Access control is not what I want. If there are 10 rows of data, and 3 belongs to X user, I want the products page to only list those 3 items. I'm looking for the relevant config, I can handle the logic myself. Thanks. 👍
Access controls probably is what you want. You can create incredibly complex nested queries with the access control to filter on certain nested elements. What do your collections look like?
My collections are:
- Business
- User
- Product
User is default Payload user. Each user belongs to a business, and each product belongs to a business. When the user visits the products page, I want them to only be able to see their own related business' products.
I figured AC is only for allowing/rejecting operations, not filtering data to show?
Ok sure, so in your access control view function, you can get the user from the request object. Req.user
It’s used for both, if you think about from a multi-tenant perspective, we may want to hide information from different orgs, so with Access Control, we filter to only get information relevant to that tenant
Which AC operation?
admin?: (args?: any) => Promise<boolean> | boolean;
create?: Access;
delete?: Access;
read?: Access;
readVersions?: Access;
unlock?: Access;
update?: Access;Read
Is there a context or similar argument that I can use to get the user's related business ID?
And the result of Access Control can either be a Boolean, or it can be a query -
https://payloadcms.com/docs/queries/overviewOh, a query would do.
You have access to the entire User object, so if you have the org Id saved to the user, you can query on it
Let me try that, thank you very much
@191776538205618177. 👍
Or you can use the payload.find() method to do a full DB query. It’s very powerful 😄
Which do you recommend?
Returning a query object is better as it is less code. But it is doing pretty much the same thing as payload.find()
This is interesting, this query only shows the user their own related business in the businesses page. It seems to work, but why? User's ID isn't the business' ID, I'm assuming?
// collections/Business.ts
read: ({ req }) => {
return {
id: {
equals: req.user.id,
},
};
},Hmm, it probably works because there's currently only 1 user and 1 business, so both have the same ID of 1.
The data and id arguments are undefined. 🤔
That is strange, that doesn’t look like it should work
Can you breakpoint debug? Or better yet, throw it in a payload.find() and see what the result is
It's because I only have 1 user and 1 business and IDs start from 1, so technically it will return 1 result, it's a malformed query. 😄
Sure.
I was trying to find what column the businesses table references the users table by.
Hmm, I guess Payload creates dedicated tables for relations.
(await req.payload.find({ collection: "businesses" })).docs;
(await req.payload.find({ collection: "users" })).docs;
[
{
id: 1,
Brand: 'foobar',
User: [ [Object] ],
updatedAt: '2024-03-21T21:55:19.623Z',
createdAt: '2024-03-21T21:55:19.623Z',
email: 'admin@app.com',
password: undefined,
loginAttempts: 0
}
]
[
{
id: 1,
Type: 'admin',
updatedAt: '2024-03-23T09:01:03.515Z',
createdAt: '2024-03-17T18:04:08.155Z',
email: 'admin@app.com',
password: undefined,
loginAttempts: 0
}
]They
arerelated, I just can't see how from the returned data except the ID but that's just because primary keys start from 1.

Oh you’re using Postgres’s
Oh, wait, so "email" is the relation?
The businesses page shows that the User field is the user's email.
No I’d is the relation. Thing is though I don’t know of Postgres treats any of this differently
Yeah.
Hmm this is a bit of a strange one. I’ve only ever used MongoDB, so I don’t know if some kind of joining table is being created on the Postgres side. Let me check
I was going to try:
return {
User: {
id: {
equals: req.user.id
},
},
}or users instead of User, but the return type is not expected to be like that, it expects propert -> equals/some other filter -> property -> and so on 🤔
Since a business row has a User array like:
{
id: 1,
Brand: 'foobar',
User: [ [Object] ],
updatedAt: '2024-03-21T21:55:19.623Z',
createdAt: '2024-03-21T21:55:19.623Z',
email: 'admin@app.com',
password: undefined,
loginAttempts: 0
}Can you drop your collection files here
Sure.
import { CollectionConfig } from "payload/types";
import { config, permissions } from "./config";
import payload from "payload";
const Business: CollectionConfig = {
slug: "businesses",
auth: true,
fields: [
{
name: "Brand",
type: "text",
required: true,
},
{
name: "User",
type: "relationship",
hasMany: true,
relationTo: "users",
},
{
name: "Product",
type: "relationship",
hasMany: true,
relationTo: "products",
hidden: true,
},
],
hooks: {},
access: {
create: permissions.allow.admin,
read: ({ req: { user } }) => {
return {
"users.id": {
equals: user.id,
},
};
},
update: permissions.allow.admin,
delete: permissions.allow.admin,
},
admin: {
...config.admin,
description: undefined,
},
};
export { Business };import { CollectionConfig } from "payload/types";
import { config } from "./config";
const User: CollectionConfig = {
slug: "users",
auth: true,
admin: {
...config.admin,
description: undefined,
useAsTitle: "email",
},
fields: [
// Email added by default
// Add more fields as needed
{
name: "Type",
type: "select",
options: [
{
label: "Admin",
value: "admin",
},
{
label: "User",
value: "user",
},
],
},
{
name: "Business",
type: "relationship",
hasMany: false,
relationTo: "businesses",
},
],
hooks: {
beforeOperation: [
// to do: don't let creating a second admin user
async ({ args }) => {
return args;
},
// to do: don't let deleting the last admin user
async ({ operation, args }) => {
if (operation === "delete" && args.type === "admin") {
throw new Error("Not allowed to delete admin.");
}
},
],
},
access: {},
};
export { User };import { CollectionConfig } from "payload/types";
import { config } from "./config";
const Product: CollectionConfig = {
slug: "products",
fields: [
{
name: "Business",
type: "relationship",
hasMany: false,
relationTo: "businesses",
},
],
admin: {
...config.admin,
description: undefined,
},
hooks: {},
access: {},
};
export { Product };I think I found the solution.
read: ({ req: { user } }) => {
return {
"User.id": {
equals: user.id,
},
};
},has many
you need an in: [] query
Hmm, so my query would only show the first related result?
What is this kind of query style called? GraphQL?
I should learn it a bit.
no no, it's because your business collection has a relationship to users, but because that is an array, you can't say users === 'userId'. You'd need to say 'userId' in users
I believe so
So this would work?
"User.id": {
in: req.user.id,
},I think just "User" should be enough
because user is an ID, not an object when querying
unless postgress handles it differently
I tried that just now, it shows results regardless of in vs not_in.
What about User.id
Whereas User.id works only when I use the "in" filter.
ok that'll be it then
Thanks a lot again for the trouble
@191776538205618177I appreciate it 👍
That's ok! glad we got it sorted 🙂
Star
Discord
online
Get dedicated engineering support directly from the Payload team.