Apollo-server: How can I pass a 403 status code ?

Created on 7 Sep 2017  路  12Comments  路  Source: apollographql/apollo-server

How can I send a custom HTTP status code whenever any of the resolvers return a 403 response?

Most helpful comment

Hi everyone. I think that is a big flaw. Having GraphQL separated from HTTP transport doesn't mean we can easily always respond 200 OK. There's a lot of libraries in stack that are status-code aware. For example any clientside http request library will track status code to decide whether request is success or not. Having custom layer for extracting errors and making decision on success/failure is painful and weird in some cases. Parsing http logs to track failures is also a case. So, even if custom status code is not required, being able to expose non-200 response is a must-have here. Even optional, this will be an extremely useful feature.

All 12 comments

Hey @tusharmath
In the project that I have been working on, I have found that apollo-resolvers and apollo-errors (both from @thebigredgeek) to be a big help.

Using a base-resolver that can be extended by the other resolvers can give you the ability to check for any resolvers that return an error and pass that error to the client.
In your case, you can return a forbidden error and check in the request handler of the GraphQL endpoint for the error. The docs of the apollo-resolvers package can help you along the way.

@tusharmath do you NEED to send a 403 status code? I've found that it's best to indicate error state in the GraphQL response rather than relying on HTTP status codes. This isn't a RESTful API, so the status codes different (and less impactful) meaning with GraphQL than they do with traditional RESTful APIs.

@michieldewilde I went thru the above mentioned projects. It might solve my problem but at the same time I agree with @thebigredgeek. I guess I will close this issue for now.

@tusharmath no problem. Glad that you found a way to resolve the problem.

See also #473

Hi everyone. I think that is a big flaw. Having GraphQL separated from HTTP transport doesn't mean we can easily always respond 200 OK. There's a lot of libraries in stack that are status-code aware. For example any clientside http request library will track status code to decide whether request is success or not. Having custom layer for extracting errors and making decision on success/failure is painful and weird in some cases. Parsing http logs to track failures is also a case. So, even if custom status code is not required, being able to expose non-200 response is a must-have here. Even optional, this will be an extremely useful feature.

" There's a lot of libraries in stack that are status-code aware. For example any clientside http request library will track status code to decide whether request is success or not. " Exactly!

The problem is that GraphQL has been explicitly designed to return partially successful responses. Even if one resolver errors, others may still return results. A 200 status code seems appropriate in those cases.

Apollo Server does return 400 when a request is malformed for example, but for resolver errors you will have to interpret errors to know what failed.

Yeah, I've read a lot of adjacent topics and now I'm convinced that it's ok in many cases, such as requests batching and so on. However, there's still a lot of cases where only one request is performed, or where app business logic allows to set status code unambiguously. And, again, I think we should give a developer a way to control this, with default 200 OK behavior.

The graphql spec is still a little vague on error handling, and this seems to be a common use case.

I'm using three 'types' of error in production:

  1. System errors. Something went wrong at a stack level, nothing to do with the user. SQL, connection timeouts, etc. For this, I set a 500 response code, log an error to Sentry, and provide a vague _System error_ response back to the user to hide stack traces.

  2. User input errors. Form field missing, fails validation check (e-mails, etc) - that kinda thing. For this, I use a 200 response code and a GraphQL object type with fields ok and errors { field, message } so that the data can be parsed and inserted into the UI.

  3. Auth errors. The user doesn't have the requisite permissions to run the query. A 401 response and a general Unauthorized error is sent back.

Using response codes makes it easier to quickly parse the above three scenarios (which are the _only_ scenarios that should occur.)

In the UI, it becomes:

401 = return the user to the login page
500 = show a 'system error' banner at the top of the screen
200 = parse the response, checking for data.errors and updating the form UI as needed

I'm using Apollo Client for the front-end, but Graphene in Python on the back-end with Flask as the HTTP handler (FWIW, I'd _love_ to see an Apollo Server implementation in Python!).

In Python, that's achieved something like:

from flask import after_this_request

from api.app.exceptions import UnauthorizedException


def error_handler(ex):
    """Handler for error middleware"""
    # check to see if an unauthorized error was raised
    if isinstance(ex, UnauthorizedException):
        @after_this_request
        def add_401(response):
            response.status_code = 401
            return response

        return ex

    raise Exception("System error. Please try again later.")


class ErrorMiddleware:
    def resolve(next, root, info, **args):
        res = next(root, info, **args)

        return res.then(None, error_handler)

I think it makes sense to have something similar in Apollo Server, although I haven't delved under the hood enough to know whether this would be Apollo's concern, or - like my Python example - would be the upstream HTTP handler, e.g. Koa, Express, etc.

One use case in which status codes would be really beneficial to us is with error monitoring and building visualizations. It would be much easier to do so if we could set the status code with our returned errors.

Was this page helpful?
0 / 5 - 0 ratings