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:
req.headers.authorization
and req.headers.Authorization
has different data types?req.headers.authorization
, so what about req.headers.Authorization
?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[]
.
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
.
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.
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.