Mastodon hachyterm.io

You’ve got a Node.js back-end server running Express and want to store sessions. You use TypeScript for your server.

Let’s see some example code.

Excerpt:

// src/resolvers/AuthResolver.ts
import { MyContext } from '../graphql-types/MyContext';
// more imports

// { ... }

@Mutation(() => UserResponse)
  async login(
    @Arg("input") { email, password }: AuthInput,
    @Ctx() ctx: MyContext
  ): Promise<UserResponse> {
      const user = await User.findOne({ where: { email } })

      if (!user) {
        return invalidLoginResponse;
      }

      const valid = await argon2.verify(user.password, password);

      if (!valid) {
        return invalidLoginResponse;
      }

      ctx.req.session.userId = user.id;

      return user
      }

// { ... }

The above code shows a GraphQL resolver for a user login. When we receive the data, we try to find the user in the database and compare the password to a hash. If everything works out, we want to store the user id as part of the session.

Let’s take a look at the interface for MyContext:

// src/graphql-types/MyContext.ts
import { Request, Response } from 'express'

export interface MyContext {
  req: Request;
  res: Response;
}

This is basically a wrapper around the standard Express request and response objects.

The Problem

TypeScript does not compile.

error TS2339: Property 'session' does not exist on type 'Request'.

The Express request object is part of the Express.js package and it has a certain signature. You are trying to add something to the request object which doesn’t exist per TypeScript definitions.

A Possible Solution

We need to make sure that our project can access all necessary type declarations.

npm install -D @types/express

We use express-session for session management, so let’s install their types, too.

npm install -D @types/express-session

How can we extend the Express request object to solve TS2339 error?

We can make use of intersection types to combine types.

An intersection type combines multiple types into one. This allows you to add together existing types to get a single type that has all the features you need.

We can add the types from the express-session type declarations to the standard request object:

import { Request, Response, Express } from 'express'

export interface MyContext {
  req: Request & { session: Express.Session };
  res: Response;
}

Thoughts

This solution works well for combining the types from different libraries. If you want to extend the type with other definitions, you can use declaration merging. Links are below.

Further Reading