Search code examples
typescriptexpressheaderapollo-server

Typescript typing http headers


I've got very confused about Typescript and its type definitions suggestions. I'm doing a Graphql API with Apollo Server and trying to implement authorization with JWT through request headers (I'm rewritting an existing API), so first I'm extracting token from request headers doing:

const token: string = req.headers.authorization

But doing that throws an error saying "type string | undefined is not assignable to type string" so I changed it to:

const token: string | undefined = req.headers.authorization

All right! But in the original API they tries to get authorization prop from req.headers.authorization or req.headers.Authorization, I don't know why but I tried to do the same doing:

const token: string | undefined = req.headers.authorization || req.headers.Authorization

And got a new error: "type string | string[] | undefined is not assignable to type string | undefined"

Then I changed it again to:

const token: string | undefined | string[] = req.headers.authorization || req.headers.Authorization

My questions:

  • Why req.headers.authorization and req.headers.Authorization has different data types?
  • I Google it and all tutorials about authorization uses req.headers.authorization, so what about req.headers.Authorization?

Solution

  • 1: Why req.headers.authorization and req.headers.Authorization has different data types?

    Your answer lies here, in the TypeScript definitions for node's http library.

    As you can see, req.headers.authorization is defined like this:

    'authorization'?: string;
    

    Because it's optional, it can be undefined. Otherwise, it's a string.

    Now look for where req.headers.Authorization would be defined. Spoiler alert, you won't find it on the list. That means you have to look back up to the type definition for req.headers:

    interface IncomingHttpHeaders extends NodeJS.Dict<string | string[]> {
      ...other stuff
    }
    

    Which means that for any string not defined in "other stuff", the type can be undefined, string, or string[].

    2: I Google it and all tutorials about authorization uses req.headers.authorization, so what about req.headers.Authorization?

    According to the HTTP Spec, headers are actually case-insensitive. Things like express (and by extension ApolloServer, which runs on express) lowercase them for you by default, so you should generally use req.headers.authorization.

    Bonus

    If you ever use apollo-server-lambda, for some reason (maybe for AWS reasons?) the capitalization is maintained, so you have to use event.headers.Authorization. That means that if you're going to do something in a generic space that can swap between different ApolloServer implementations, you're going to be better off looking in both places, accepting undefined | string | string[], and then checking to see if it's an array and using the first element.

    Unrelated [hopefully helpful] Semantics

    const token: string | undefined | string[] = req.headers.authorization || req.headers.Authorization
    

    The variable name token technically shouldn't be accurate. The authorization header is supposed to be Authorization: <type> <credentials>. If you're looking for a "token", it's probably in the header, after the word Bearer . Not everybody follows that, but you can usually split on the space and take the last piece, unless you really do want the whole header, in which case, ignore this bit.