Skip to content

Instantly share code, notes, and snippets.

@LidiaKovac
Last active December 18, 2021 12:06
Show Gist options
  • Save LidiaKovac/250bdaf12ff0cab8168e266b5e8848da to your computer and use it in GitHub Desktop.
Save LidiaKovac/250bdaf12ff0cab8168e266b5e8848da to your computer and use it in GitHub Desktop.
Typescript + Express REST API - Mini-guide

TypeScript REST API

General notes:

  • Request and Response are both basic TS interfaces and express interfaces. You want to use the express one, so remember to import them correctly.
  • NextFunction is the interface for next() and is also imported from ExpressJS.
  • Async functions return Promises (which, in the TS language is Promise<T> where T is the type of data the Promise will return if fullfilled). In case of Express endpoints, you will use res.send(), not return, so T will be void.

Possible errors:

req.header("Authorization").replace("Bearer ", "") - error TS2532: Object is possibly 'undefined' when trying to get the authorization header

The solution to this error is actually very easy, but to understand it we need to think about data structure. The following explanation is a little bit more "technical" and dives into node_modules.

If we dig down into the typescript declarations of Express, we will discover that the request interface Express is an extesion of http.IncomingMessage where headers have a type IncomingHttpHeaders, coming from Node itself:

From /express/index.d.ts (express type declarations)

interface Request<
        P = core.ParamsDictionary,
        ResBody = any,
        ReqBody = any,
        ReqQuery = core.Query,
        Locals extends Record<string, any> = Record<string, any>
    > extends core.Request<P, ResBody, ReqBody, ReqQuery, Locals> {} //core.Request is the type we are going to explore next

From express-serve-static-core/index.d.ts (npm package to handle express type, it comes with express by default)

export interface Request<
    P = ParamsDictionary,
    ResBody = any,
    ReqBody = any,
    ReqQuery = ParsedQs,
    Locals extends Record<string, any> = Record<string, any>
> extends http.IncomingMessage,
        Express.Request {
    get(name: 'set-cookie'): string[] | undefined;
    get(name: string): string | undefined;

    header(name: 'set-cookie'): string[] | undefined;
    header(name: string): string | undefined; // <<<<<<<<<< LOOK HERE!
    //there's more down here, but we don't care about it

What we see in the line I have marked, is that the Request interface has a "header" method that takes a string as parameter. This means that the line of code req.header("Authorization") is not inherently wrong, but it still gives back an error. THe reason for the error is that the return type for the 1headermethod, isstring | undefined, which means that it could also return undefined` if you passed no headers. TS does not know in advance if you are going to pass an Authorization header!

To solve this, we need to "assure" typescript that the return value will not be undefined, and we can do so by adding a ! operator, which will tell TS that this value will NEVER be undefined when this code runs.

SOLUTION

req.header("Authorization").replace("Bearer ", "")

req.user - Property 'user' does not exist on type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>

  • This is due to the fact that an express Request does not bear the user prop in its type declaration. You can solve this by extending the original Request class and adding the User to it, like this:
import { Request } from "express"
export interface RequestWithUser extends Request {
	user: User //create the User interface according to your structure
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment