13

I want to use Facebook's DataLoader with Koa2 in Typescript. I want per-request DataLoader instances to go with my per-request database connections. How is this best achieved?

My current approach is to augment the Koa2 context but I'm failing because I don't know how to fix the type definition.

Here is my attempt at module augmentation...

import 'koa';

declare module 'koa' {
    namespace Application {
        interface BaseContext {
            dataLoader(): any;
        }
    }
}

Application.BaseContext.prototype.dataLoader = function() {
    console.log("Cannot find name 'Application' at line 11 col 1");
}

In addition to the error shown in the log call, I also get Property 'dataLoader' does not exist on type 'BaseContext' when I import the above and attempt to call dataLoader.

Cheers

sdc395
  • 255
  • 1
  • 3
  • 12

3 Answers3

20

Just figured out an approach that doesn't require any type-hacking:

interface ICustomAppContext {
  mySlowToInitializeClient: string;
}

interface ICustomAppState {
  poop: string;
}

const app = new Koa<ICustomAppState, ICustomAppContext>();

See https://github.com/DefinitelyTyped/DefinitelyTyped/blob/b081a5b7d0db7181901d7834f8a85206af263094/types/koa/index.d.ts#L434 for details.

skainswo
  • 356
  • 2
  • 5
  • 1
    That's the right answer, you don't need to use module augmentation is the type is already flexible – Waldemar Neto Sep 01 '20 at 21:03
  • 1
    This worked for me, but I had to extend the defaults: ``` interface CustomAppContext extends DefaultContext { meta: string; } const app = new Koa(); ``` – tekniskt Jan 06 '21 at 14:26
12

Take a look at Module Augmentation to understand more how this works.

You can indeed do something like:

import { Context } from "koa";

declare module "koa" {
    /**
     * See https://www.typescriptlang.org/docs/handbook/declaration-merging.html for
     * more on declaration merging
     */
    interface Context {
        myProperty: string;
        myOtherProperty: number;
    }
}
Jeroen Minnaert
  • 625
  • 1
  • 9
  • 19
7

OK, well I guess I still have a lot to learn. The solution to the typing problem seems to be...

import { BaseContext } from 'koa';

declare module 'koa' {
  interface BaseContext {
    dataLoader(): any;
  }
}

And, because BaseContext is an interface and not a class, you have to define the dataLoader implementation after you have instantiated Koa's Application class.

const app = new Application();

app.context.dataLoader = function() {
  console.log('OK, this works.');
}

app.context is the prototype used to create each request's context object.

I'd appreciate any comments regarding the correctness of this answer. Thanks.

Ah... learning in public.

sdc395
  • 255
  • 1
  • 3
  • 12