NestJS tutorials

NestJS is a react framework for building efficient, scalable, server-side applications. It also provides built in dependency injection out of the box, which I really like the sound of!

I plan on learning about this framework and the ansillary frameworks, libraries and modules that surround it. I’m fairly new to front-end development, so I hope this will be useful to people in a similar position.

Getting Started

First up, read the NestJS intro and overview docs, which shows how to setup the dev environment, how to generate a hello world app, and some of the basics of how things fit together.

I often come up with trivial problems to solve when having a go of new frameworks. In this case I wanted to eventually implement these rules:

AskQuestion
 - what's for tea?
   - get strategy based on person asked
     - if mike, provide response based on day of the week
     - if mrs, randomise response between "beans on toast" and "scrambled eggs on toast"
     - if baby, then always return "playdough"

IoC

After reading a bit deeper into the nestJS docs for providers and custom providers it starts to become a bit clearer in that you can inject any class in place of any other class.

Here’s some sample code to showcase this.

question.controller.ts

import { Query, Get, Controller } from "@nestjs/common";
import { QuestionAnswerer } from "./QuestionAnswerer";

@Controller()
export class QuestionController {

    constructor(
        private readonly questionAnswerer: QuestionAnswerer) {
        // ^-- the dependency is injected here
    }

    @Get('/answer')
    getAnswer(
        @Query('questionTarget') questionTarget: string
    ): string {
        return this.questionAnswerer.getAnswer(questionTarget);
    }
}

QuestionAnswerer.ts

import { Injectable } from "@nestjs/common";

@Injectable()
export class QuestionAnswerer {
    getAnswer(questionTarget: string): string {
        return "Hello World!";
    }
}

@Injectable()
export class MockQuestionAnswerer {
    getAnswer(questionTarget: string): string {
        return "Hello Mocked World!";
    }
}

question.controller.spec.ts

import { QuestionController } from "./question.controller";
import { Test, TestingModule } from "@nestjs/testing";
import { QuestionAnswerer, MockQuestionAnswerer } from "./QuestionAnswerer";

describe('QuestionController', () => {
  let questionController: QuestionController;
  
  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [QuestionController],
      providers: [{
        provide: QuestionAnswerer,
        useClass: MockQuestionAnswerer, 
        // ^-- This is where the type mapping is done
      }],
    }).compile();

    questionController = app.get<QuestionController>(QuestionController);
  });
  
  describe('root', () => {
    it('should return "Hello Mocked World!"', () => {
      expect(questionController.getAnswer("mike")).toBe('Hello Mocked World!');
    });
  });
});

The problem?

QuestionController has a dependency on QuestionAnswerer, but QuestionAnswerer has been swapped out for MockQuestionAnswerer. Great.

However QuestionAnswerer and MockQuestionAnswerer don’t have anything to do with each other. In theory they might not implement the same members and functions, so how can we safely swap one out for the other? If I change the name of MockQuestionAnswerer.getAnswer to getAnswer2 I get the error TypeError: this.questionAnswerer.getAnswer is not a function. And if I run nest build it still compiles, which shows that it’s a runtime error right? eek!

So why can’t we just inject interfaces?

I’ve read several times that NestJS doesn’t allow for interface based dependency injection because of “language limitations”. Every example I’ve seen appears to inject the dependency as a concrete type, but can be swapped out for some other arbitrary type. How can we write SOLID code without Dependency Inversion? And I must be missing something, because I’m seemingly stumbling upon javascripty weak type issues all over the place.

This blog post shows a way to implement your own IoC container by creating a provider that maps the interface to the concrete type. It looks much better than my attempt above, but I’m not 100% happy with it because:

  1. it defines the interface as a string in both the provider and the @Inject attribute, rather than a strongly typed… type. There doesn’t appear to be framework compile-time magic going on here either, because if you change the interface string it will just fail at runtime. Not ideal.

  2. I’d rather just create all of my mappings at the root level of the application, rather than a single mapping provider for every type in my application. Though I suspect I can do this anyway for each NextJS module.

TDD

Upon having a think about how to approach this with TDD in mind, I decided to do a bit of research into how IoC works with NestJS and Jest.

[Work in progress]

Popular posts from this blog

Taking a memory dump of a w3wp process

sp_blitzIndex

GitLab Badges