premieroctet / next-crud

Full-featured CRUD routes for Next.js

Home Page:https://next-crud.js.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

onRequest callback unable to mutate request at all

jacobclarke92 opened this issue · comments

Heya, first of all, thanks for all your hard work on this lib, I've been following it for ages and it just keeps getting better.

Minor issue -- because the handler function destructures req before the onRequest callback fires, any meaningful changes the onRequest callback makes to req are ignored.

Docs state intended use case for this callback could be: "You can use it to add some custom content to your query params".
So I presume this may be an oversight?

Also, I found it odd that the handler looks at req.url instead of potentially looking at req.query, any particular reason for that?

Happy to do a pull request later just wanted to drop a line first!

Cheers!

Totally agreed on the last comment: the library needs some way to mutate incoming request in a middleware or callback, to enforce access-control (adding a parameter or a body-field).

As a workaround for now, you can do:

in your src/pages/api/[...nextcrud].js:

const handler = async (req:NextApiRequest, res) => {

  const nextCrudHandler = await NextCrud({
...
})

if(req.method == 'GET'){
// manipulate req.url
const newurl = req.url + `where={user_id=${current_user.id}`
    req.url = newurl;
  } else if(req.method == 'POST'){
    req.body.user_id = user.id;
  }

  return nextCrudHandler(req, res);
};

A better alternative (until solved) is to create an alternative "prisma adapter" that enforces access-control from the input parameters, because the current getOne, delete, etc won't enforce access control for you, and patching req.query and req.body is such a burden.

This is working for me, but it's such a dirty patch:

class AccessControlPrismaAdapter extends PrismaAdapter< userprofile | Space, Prisma.ModelName > {
  prismaDelegate: any;
  pk: any;

  constructor({ modelName, prismaClient, primaryKey = "id", options, manyRelations = [], }: any) {
    super({ primaryKey, prismaClient, manyRelations });
    const pc = prismaClient;
    this.prismaDelegate = prismaClient[modelName];
    this.pk = primaryKey;
  }

  async getOne( resourceName, resourceId, query?: IPrismaParsedQueryParams,): Promise<userprofile | Space> {
    const delegate = this.getPrismaDelegate2(resourceName);
    const findFn = /*delegate.findUnique || */ delegate.findFirst;

    const resource = await findFn({
      where: { AND: [query.where, { [this.pk]: resourceId }] },
      select: query.select,
      include: query.include,
    });

    return resource;
  }

  getPrismaDelegate2( resourceName: Prisma.ModelName,): Record<PrismaAction, (...args: any[]) => Promise<userprofile | Space>> {
    // @ts-ignore
    return this.prismaClient[
      `${resourceName.charAt(0).toLowerCase()}${resourceName.slice(1)}`
    ];
  }

then in your nextcrud config:

  const nextCrudHandler = await NextCrud({
    adapter: new AccessControlPrismaAdapter({ prismaClient }),
    models: {
      [Prisma.ModelName.userprofile]: {

then you export from [nextcrud]:

  if (["GET", "DELETE", "PATCH"].includes(req.method)) {

    const where = req.query.where ? JSON.parse(req.query.where as string) : {};
    let newhere;

    if(url.pathname.match(/\/api\/usuarios/)){
      newhere = JSON.stringify({
        $and: { id: { $eq: user.id }, ...where },
      });
    }else if(url.pathname.match(/\/api\/spaces/)){
      newhere = JSON.stringify({
        $and: { creatorId: { $eq: user.id }, ...where },
      });
    }
    console.log(req.method, newhere);

    params.set("where", newhere);

    const newurl = `${url.pathname}?${params.toString()}`;
    console.log("new url:", newurl);
    req.url = newurl;
  } else if (req.method == "POST") {
    if(url.pathname.match(/\/api\/usuarios/)){
      req.body.creatorId = user.id;
    } else if(url.pathname.match(/\/api\/spaces/)){
      req.body.id = user.id;
    }
    console.log("POST:", req.body);
  }

  return nextCrudHandler(req, res);
};
export default handler;

but as I was saying... feels such a hack 😝

Hello,

Sorry for the delay. It is now fixed in 2.3.2. Let me now if it works fine now

Hello,

Sorry for the delay. It is now fixed in 2.3.2. Let me now if it works fine now

Hey @foyarash, no worries!
I had a look over the commit and it's practically identical to how I'd patch-packaged it in the end so I'm stoked!
Thanks for getting around to it <3