elysiajs / elysia

Ergonomic Framework for Humans
https://elysiajs.com
MIT License
9.23k stars 198 forks source link

problem with derives #613

Closed yadsbah closed 3 months ago

yadsbah commented 3 months ago

What version of Elysia.JS is running?

1.0.13

What platform is your computer?

Mac Air M2

What steps can reproduce the bug?

I have derive named accountAuther

const accountAuther = (x0: number) =>
  new Elysia({}).use(bearer()).derive(
    {
      as: "scoped",
    },
    async ({ bearer, error }) => {
      console.log(x0);
      if (!bearer) return error(401, { error: "bearer is required!" });
      const data = jwtVerify(bearer);
      if (!data) return error(401, { error: "Token is invalid!" });
      var userFromDatabase = await db.account.findFirst({
        where: {
          id: data.id,
        },
      });
      if (!userFromDatabase) return error(401, { error: "User Doesnt Exist!" });
      return {
        user: userFromDatabase,
      };
    }
  );

and the product router is

const productOwner = new Elysia().use(accountAuther(1)).derive(
  {
    as: "scoped",
  },
  ({ user, body }) => {
    return {
     prducts: []
    };
  }
);

const productRouter = new Elysia({
  prefix: "/products",
  tags: ["Products"],
})
  .use(accountAuther(2))
  .get("/", async ({ query, user }) => {})
  .use(productOwner)
  .get("/dd", ({ prducts }) => "dd");

What is the expected behavior?

When I send a get request to /dd it only print number 2 which indicate that only accountAuther in the productRouter has triggered. while I do have another accountAuther in productOwner derive which should print 1 but it doesnt get triggered.

What do you see instead?

I should have seen 1 and 2 printed since I have called accountAuther twice.

Additional information

I dont know if this behaviour is normal since the accountAuther is scoped. I have used accountAuther again in productOwner derive because I need user parameter. I dont want to use ts-ignore for that. so the only solution is to reuse the derive use again.

yadsbah commented 3 months ago

When I add use the same derive such as accountAuther in same router it does work like:

const productRouter = new Elysia({
  prefix: "/products",
  tags: ["Products"],
})
 .use(accountAuther(2))
 .use(accountAuther(3))
  .get("/", async ({ query, user }) => {})
  .use(productOwner)
  .get("/dd", ({ prducts }) => "dd");

Here both 2 and 3 prints, which mean the accountAuther has been called twice

dashedstripes commented 3 months ago

Switching the Hook type to "global" on your plugins should fix the issue you're seeing. The current hook type "scoped", does not apply to all instances that are applying your plugin (according to the docs):

https://elysiajs.com/essential/scope.html#hook-type

I reproduced a minimal example here:

const accountAuther = (x0: number) =>
new Elysia({}).derive(
    {
        as: "global",
    },
    async ({ error }) => {
        console.log(x0)
        return {
            user: x0,
        };
    }
);

const productOwner = new Elysia().use(accountAuther(1)).derive(
  {
    as: "global",
  },
  ({ user, body }) => {
    return {
     prducts: []
    };
  }
);

const productRouter = new Elysia({
    prefix: "/products",
    tags: ["Products"],
})
    .use(accountAuther(2))
    .get("/", () => "/products")
    .use(productOwner)
    .get("/dd", ({ products }) => "dd");
yadsbah commented 3 months ago

Switching the Hook type to "global" on your plugins should fix the issue you're seeing. The current hook type "scoped", does not apply to all instances that are applying your plugin (according to the docs):

https://elysiajs.com/essential/scope.html#hook-type

I reproduced a minimal example here:

const accountAuther = (x0: number) =>
new Elysia({}).derive(
  {
      as: "global",
  },
  async ({ error }) => {
      console.log(x0)
      return {
          user: x0,
      };
  }
);

const productOwner = new Elysia().use(accountAuther(1)).derive(
  {
    as: "global",
  },
  ({ user, body }) => {
    return {
     prducts: []
    };
  }
);

const productRouter = new Elysia({
  prefix: "/products",
  tags: ["Products"],
})
  .use(accountAuther(2))
  .get("/", () => "/products")
  .use(productOwner)
  .get("/dd", ({ products }) => "dd");

Thanks for your replay.

lets say we have 2 scoped derive A and B. B is using A. in any router if I used B, A wont be triggered. I have to use both A and B in the router even if A was used in B? is that what you are saying?

SaltyAom commented 3 months ago

Switching the Hook type to "global" on your plugins should fix the issue you're seeing. The current hook type "scoped", does not apply to all instances that are applying your plugin (according to the docs): https://elysiajs.com/essential/scope.html#hook-type I reproduced a minimal example here:

const accountAuther = (x0: number) =>
new Elysia({}).derive(
    {
        as: "global",
    },
    async ({ error }) => {
        console.log(x0)
        return {
            user: x0,
        };
    }
);

const productOwner = new Elysia().use(accountAuther(1)).derive(
  {
    as: "global",
  },
  ({ user, body }) => {
    return {
     prducts: []
    };
  }
);

const productRouter = new Elysia({
    prefix: "/products",
    tags: ["Products"],
})
    .use(accountAuther(2))
    .get("/", () => "/products")
    .use(productOwner)
    .get("/dd", ({ products }) => "dd");

Thanks for your replay.

lets say we have 2 scoped derive A and B. B is using A. in any router if I used B, A wont be triggered. I have to use both A and B in the router even if A was used in B? is that what you are saying?

You should use propagate experimental API in this case. propagate allows you to promote the all current local scope to scoped thus persisting the use nested plugin.

const productOwner = new Elysia()
    .use(accountAuther(1))
    .propagate()
    .derive(
        {
            as: "scoped",
        },
        ({ user, body }) => {
            return {
                prducts: []
            }
        }
    )