I'm having trouble with NestJS + GraphQL, I've see...
# support-questions
f
I'm having trouble with NestJS + GraphQL, I've seen there were older messages about this in discord but it didn't really help... Honestly I have no clue of how, according to the supertokens documentation of vanilla graphql on nodejs, this should be done:
Copy code
ts
@Injectable()
export class GQLAuthGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const ctx = GqlExecutionContext.create(context).getContext();

    let err = undefined;
    // You can create an optional version of this by passing {sessionRequired: false} to verifySession
    await verifySession({ sessionRequired: false })(
      ctx.req,
      ctx.res,
      (res) => {
        err = res;
      },
    );

    if (ctx.res.headersSent) {
      throw new STError({
        message: 'RESPONSE_SENT',
        type: 'RESPONSE_SENT',
      });
    }

    if (err) {
      throw err;
    }

    return true;
  }
}
r
What’s the error you are facing?
f
There are many in different scenarios... here's the one I get by calling a graphql endpoint being unauthenticated
Copy code
D:\git\evt-server\node_modules\supertokens-node\lib\build\framework\express\framework.js:36
                    step(generator["throw"](value));
                                           ^
TypeError: next is not a function
    at D:\git\evt-server\node_modules\supertokens-node\lib\build\framework\express\framework.js:179:24
    at Generator.throw (<anonymous>)
    at rejected (D:\git\evt-server\node_modules\supertokens-node\lib\build\framework\express\framework.js:36:44)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
The response looks correct 👇🏼 , but it makes the server crash ☝🏼
r
Do you know which function in your code causes this?
f
I had to wait the session to expire because it's the only scenario that causes the error:
The error happens into the code I sent above
ctx.res.headersSent seems to be true
r
You can delete the sAccessToken in the cookie and then it would be like as if it’s expired
I see. So which error handler is catching that error?
The one that’s thrown in that if block
f
Alright I'm starting to understand: The error is handled by this
Copy code
ts
@Catch(STError)
export class SupertokensExceptionFilter implements ExceptionFilter {
  handler: ErrorRequestHandler;

  constructor() {
    this.handler = errorHandler();
  }

  catch(exception: Error, host: ArgumentsHost) {
    const ctx = host.switchToHttp();

    const resp = ctx.getResponse<Response>();
    if (resp.headersSent) {
      return;
    }

    this.handler(
      exception,
      ctx.getRequest<Request>(),
      resp,
      ctx.getNext<NextFunction>(),
    );
  }
}
which I just figured out it's not meant to handle graphql context
correct?
r
Yea. Correct
f
So I'm trying to do something like this:
Copy code
ts
if (ctx.res.headersSent) {
      throw new GQLSTError({
        message: 'RESPONSE_SENT',
        type: 'RESPONSE_SENT',
      });
    }
I'm throwing a different exception
GQLSTError extends STError
r
You can. As long as In that handler, you check that the type is response sent and just return
Cause if you send a response again, it will throw a different error
f
ok that doesn't seem to work
Copy code
ts
@Catch(STError)
export class SupertokensExceptionFilter implements ExceptionFilter {
  handler: ErrorRequestHandler;

  constructor() {
    this.handler = errorHandler();
  }

  catch(exception: Error, host: ArgumentsHost) {
    if (exception instanceof GQLSTError) return; // added this
    const ctx = host.switchToHttp();

    const resp = ctx.getResponse<Response>();
    if (resp.headersSent) {
      return;
    }

    this.handler(
      exception,
      ctx.getRequest<Request>(),
      resp,
      ctx.getNext<NextFunction>(),
    );
  }
}
now the exception isn't caught by this filter
neither by the other one though
oh wait
didn't register it on main.ts
okay so
how do I get the ExecutionContext from here?
catch(exception: Error, host: ArgumentsHost)
I used to access the GQL request by hardcoding the arg index like this
Copy code
ts
ctx.getArgByIndex(2)
by the way, the filter works, and...
Copy code
ts
catch(exception: Error, host: ArgumentsHost) {
    const context = host.getArgByIndex(2);
    if (context.res.headersSent) {
      return;
    }

    this.handler(exception, context.req, context.res, context.next);
  }
context.res.headersSent is set to true, so, I get the correct response back to the client but in the server console I get this error:
Copy code
Error: Cannot set headers after they are sent to the client 
    at new NodeError (node:internal/errors:371:5)
    at ServerResponse.setHeader (node:_http_outgoing:573:11)
    at D:\git\evt-server\node_modules\apollo-server-express\src\ApolloServer.ts:171:19
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
r
Yea.. in your erorr handler, you need to check if the err has
type == 'RESPONSE_SENT'
and if that's true, then you just return from the error handler
f
Copy code
ts
catch(exception: Error, host: ArgumentsHost) {
    if (exception.message === 'RESPONSE_SENT') return;
    const context = host.getArgByIndex(2);
    if (context.res.headersSent) {
      return;
    }

    this.handler(exception, context.req, context.res, context.next);
  }
now it's done, and it's still throwing the error
by debugging it I can see it enters into the if statement and return
but still
Copy code
Error: Cannot set headers after they are sent to the client
    at new NodeError (node:internal/errors:371:5)
    at ServerResponse.setHeader (node:_http_outgoing:573:11)
    at D:\git\evt-server\node_modules\apollo-server-express\src\ApolloServer.ts:171:19
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
r
Hmmm. I’m not too sure at the moment. Can you open an issue about this on o ur GitHub and we can suggest a solution that will work tomorrow or day after
f
I was going to... then I noticed it works with
antiCsrfCheck: false
... do you have any idea of why? Otherwise I'm ok with github issue
r
Right. So when you query the API, you must not be setting a rid header
How r you querying it?
Using postman? Or fetch / axios?
f
postman, I do not set the rid
r
So you should set rid. If you are querying your own api, set the value of it to rid: anti-csrf
18 Views