Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Body already used issue with certain libraries #26

Open
danieljvdm opened this issue Jan 9, 2025 · 4 comments
Open

Body already used issue with certain libraries #26

danieljvdm opened this issue Jan 9, 2025 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@danieljvdm
Copy link

What versions are running?

...
"@bogeychan/elysia-logger": "^0.1.7",
"elysia": "^1.2.10",
"better-auth": "^1.1.10",
...

What steps can reproduce the bug?

My server looks like this:

new Elysia()
  .use(logger())
  .use(cors())
  .all('/api/auth/*', betterAuthView)
  .listen(getEnv().PORT);

betterAuthView is this:

export const betterAuthView = async (context: Context) => {
  const BETTER_AUTH_ACCEPT_METHODS = ['POST', 'GET'];
  if (BETTER_AUTH_ACCEPT_METHODS.includes(context.request.method)) {
    console.log(context.request);
    try {
      return auth.handler(context.request);
    } catch (error) {
      console.error('Error in auth.handler:', error);
      return context.error(500, 'Error in authentication handler');
    }
  } else {
    context.error(405);
  }
};

When a request goes to an auth route, I get a ERR_BODY_ALREADY_USED error. If I comment out this logger, it works fine. I'm not exactly sure what's going on, but the stacktrace of the error is on this line (in the better-auth libary):
return await request.json();

So it seems like that library is trying to consume the request body but by that time this logger already has...

What is the expected behavior?

No response

What do you see instead?

No response

Additional information

No response

@danieljvdm danieljvdm added the bug Something isn't working label Jan 9, 2025
@bogeychan bogeychan self-assigned this Jan 10, 2025
@bogeychan
Copy link
Owner

bogeychan commented Jan 10, 2025

Thanks for reporting this finding.

Elysia has built-in support for parsing application/json, ..., which is why your body is already used. However, due to (required) access of ctx in hooks in this plugin:

log.error(ctx);

Elysia cannot determine whether the body property is being accessed or not. As a result, it pre-parses the body (context.body). You will encounter the same issue with new Elysia({ aot: false }) if you remove the plugin.


Here's a workaround to skip parsing:

.all("/api/auth/*", betterAuthView, { parse: () => 1 })

Kinda blocked by elysiajs/elysia#1009

@danieljvdm
Copy link
Author

Thanks for the response. I don't quite follow what you're saying - this seems to be more localized to this library than other things. I'm getting the same issue on my GraphQL server - body is already used unless I disable this logger (there isn't an obvious way to pass { parse: () => 1} to a gql yoga instance either.)

Sorry if i'm not understanding things correctly - but is there no way this library can use response.clone() to avoid accessing the same response object?

@danieljvdm
Copy link
Author

For now I've just moved my graphql yoga server above the logger call:

  .use(graphqlServer)
  .use(logger())

@bogeychan
Copy link
Owner

bogeychan commented Jan 17, 2025

We need to wait until the mentioned Elysia issue is resolved.

If other plugins cause similar issues, you can work around it by adding a global parse-hook and manually handling parsing on the cloned request:

import { Elysia, ParseError } from "elysia";

const plugin = new Elysia({ name: "plugin" })
  .derive((ctx) => ({ plugin: ctx.body }))
  .as("global");

new Elysia()
  .onParse(({ request, contentType }) => {
    const cloned = request.clone();

    switch (contentType) {
      case "application/json":
        return cloned.json();
      case "text/plain":
        return cloned.text();
    }

    throw new ParseError();
  })
  .use(plugin)
  .post("/", ({ request }) => request.bodyUsed)
  .listen(3000);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants