abelcastro.dev

Learn NestJS with MikroORM by Creating a Blog Project

2024-06-01

TypeScriptRest-APINestJSMikro-ORMExpressDocker

The Sports Dashboard now displays the results of the last matchday

2024-04-14

Rest-APIDjangoAngular

Avoiding Mocks in Testing Through Single Responsibility and Dependency Injection

2024-02-27

TypeScriptTesting
1
...
3
4
5
...
9

Abel Castro 2025 - checkout the source code of this page on GitHub - Privacy Policy

In the world of Node.js, frameworks like NestJS have transformed the landscape of building server-side applications by introducing a scalable, maintainable, and modular architecture. Coupled with MikroORM, an intuitive TypeScript ORM, you get a powerful duo that promotes code reliability and database management. This post will guide you through setting up a simple blog application using NestJS and MikroORM.

Prerequisites

Before we dive in, ensure you have the following installed:

  • Node.js
  • Docker (for running the PostgreSQL database)

Step 1: Setting Up Your NestJS Project

First, install the NestJS CLI globally, if you haven't already:

npm install -g @nestjs/cli

Create a new project:

nest new blog-nest

Navigate to your project directory:

cd blog-nest

Step 2: Integrating MikroORM

Install MikroORM and the PostgreSQL driver:

npm install @mikro-orm/core @mikro-orm/nestjs @mikro-orm/postgresql pg

Create a mikro-orm.config.ts at the root of your project to configure MikroORM:

import { MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs';
import { Post } from './src/posts/post.entity';

const config: MikroOrmModuleSyncOptions = {
  entities: [Post],
  dbName: 'blog',
  type: 'postgresql',
  user: 'test',
  password: 'test',
};

export default config;

Step 3: Running PostgreSQL in Docker

Set up a docker-compose.yml file to run PostgreSQL:

version: '3.8'
services:
  postgres:
    image: postgres:latest
    environment:
      POSTGRES_DB: blog
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
    ports:
      - "5432:5432"
    volumes:
      - postgres-data:/var/lib/postgresql/data
volumes:
  postgres-data:

Start your PostgreSQL database:

docker-compose up -d

Step 4: Building the Blog API

Define your Post entity:

import { Entity, PrimaryKey, Property } from '@mikro-orm/core';

@Entity()
export class Post {
  @PrimaryKey()
  id!: number;
  @Property()
  title!: string;
  @Property()
  content!: string;
}

Create the Posts module, service, and controller to handle CRUD operations.

Posts Module

import { Module } from '@nestjs/common';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { PostsController } from './posts.controller';
import { PostsService } from './posts.service';
import { Post } from './post.entity';

@Module({
  imports: [MikroOrmModule.forFeature([Post])],
  controllers: [PostsController],
  providers: [PostsService],
})
export class PostsModule {}

Posts Service

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@mikro-orm/nestjs';
import { EntityRepository } from '@mikro-orm/core';
import { Post } from './post.entity';

@Injectable()
export class PostsService {
  constructor(
    @InjectRepository(Post)
    private readonly postRepository: EntityRepository<Post>
  ) {}

  async findAll(): Promise<Post[]> {
    return this.postRepository.findAll();
  }
}

Posts Controller

import { Controller, Get } from '@nestjs/common';
import { PostsService } from './posts.service';
import { Post } from './post.entity';

@Controller('posts')
export class PostsController {
  constructor(private readonly postsService: PostsService) {}

  @Get()
  async findAll(): Promise<Post[]> {
    return this.postsService.findAll();
  }
}

Step 5: Running the Application

Run your application and navigate to http://localhost:3000/api/posts to see the posts retrieved from the database:

npm run start

Conclusion

You've just set up a basic blog API using NestJS and MikroORM, with a PostgreSQL database running in a Docker container. This project lays the foundation for building scalable and maintainable applications with advanced features like data mapping, dependency injection, and modular architecture.

For the complete source code, check out the GitHub repository: blog-nest.

NestJS and MikroORM are powerful tools in your Node.js arsenal. By mastering them, you can greatly enhance your backend development capabilities. Happy coding!

The Sports Dashboard now displays the results of the last matchday.

Sports Dashboard 2.0

I've been making some updates to the Sports Dashboard to showcase the results of the latest matchday across available leagues. My aim with this project is to provide a convenient overview of major football leagues in Europe without needing to navigate through sports news websites.

To achieve this, I've implemented a new task that retrieves results data from APIFootball, processes it into the required format, and then stores it in the database. The code is designed to be adaptable to different data providers and includes a DataProviderInterface, allowing to easy use other data providers if it is needed.

This task runs once a day, similar to the task for retrieving standings. The results are now accessible through the endpoint GET /api/sports/standings/.

Please note that since the dashboard only displays results from the last matchday, there may be instances where the data appears incomplete if not all games have been played yet.

The code is publicly available here. Enjoy!

I personally hate to use mocks or spies when I write tests. Often they make tests more fragile, unclear and unreliable. Personally a way that I learned to avoid them is to adhere to principles like Single Responsibility Principle (SRP) and Dependency Injection (DI). 

One way to reduce the need for mocks is by designing your code with principles like Single Responsibility Principle (SRP) and Dependency Injection (DI). This makes the code more modular, easier to test, and less reliant on mocks.

Let me show you what I am talking about with 2 examples:

Bad Example: Hard-coded Dependency

Consider a TypeScript class that directly creates and uses an external library or service within its methods. This tight coupling makes it difficult to test without resorting to mocks.

class EmailService {
    sendEmail(to: string, subject: string, body: string) {
        // Directly using an external email sending library
        const emailClient = new ExternalEmailClient();
        emailClient.configure();
        emailClient.send(to, subject, body);
    }
}

Testing the Bad Example

To test the bad example, you'd typically need to use a library that supports mocking global objects or constructors, which can be cumbersome and lead to brittle tests. However, TypeScript itself doesn't directly support mocking like some dynamic languages do, so you'd have to rely on a JavaScript testing framework that allows such operations, like Jest.

Here's an illustrative example using Jest to mock ExternalEmailClient: In this example, EmailService is directly dependent on ExternalEmailClient. To test EmailService's sendEmail method, you would have to mock ExternalEmailClient, which complicates the test setup.

// Assume ExternalEmailClient is imported from somewhere
jest.mock('./ExternalEmailClient', () => {
  return jest.fn().mockImplementation(() => {
    return {
      configure: jest.fn(),
      send: jest.fn(),
    };
  });
});

describe('EmailService', () => {
  it('should send an email using ExternalEmailClient', () => {
    const emailService = new EmailService();
    emailService.sendEmail('test@example.com', 'Test Subject', 'Test Body');
    // Assertions to verify that ExternalEmailClient was called correctly
    // This part is highly dependent on the mocking framework's API
  });
});

This approach has several downsides:

  • It relies on global mocks, which can affect other tests if not properly isolated or reset.
  • The test is more focused on implementation details (i.e., it tests whether ExternalEmailClient was called) rather than the outcome or behavior of the EmailService.

Good Example: Single Responsibility and Dependency Injection

A better approach is to refactor the code to follow the Single Responsibility Principle and use Dependency Injection. This way, each class has only one reason to change, and dependencies are injected rather than hard-coded.

interface IEmailClient {
    configure(): void;
    send(to: string, subject: string, body: string): void;
}

class EmailService {
    private emailClient: IEmailClient;

    constructor(emailClient: IEmailClient) {
        this.emailClient = emailClient;
    }

    sendEmail(to: string, subject: string, body: string) {
        this.emailClient.configure();
        this.emailClient.send(to, subject, body);
    }
}

class ExternalEmailClient implements IEmailClient {
    configure(): void {
        // Configuration logic for the external email client
    }

    send(to: string, subject: string, body: string): void {
        // Sending logic for the external email client
    }
}

Testing the Good Example

Testing the good example is more straightforward and doesn't typically require special mocking libraries. You can easily create a mock or stub that implements the IEmailClient interface and pass it to the EmailService constructor.

Here's an example using a simple mock object:

class MockEmailClient implements IEmailClient {
  configureCalled = false;
  sendCalledWith: [string, string, string] | null = null;

  configure(): void {
    this.configureCalled = true;
  }

  send(to: string, subject: string, body: string): void {
    this.sendCalledWith = [to, subject, body];
  }
}

describe('EmailService', () => {
  it('should send an email using the provided email client', () => {
    const mockEmailClient = new MockEmailClient();
    const emailService = new EmailService(mockEmailClient);
    emailService.sendEmail('test@example.com', 'Test Subject', 'Test Body');

    expect(mockEmailClient.configureCalled).toBe(true);
    expect(mockEmailClient.sendCalledWith).toEqual(['test@example.com', 'Test Subject', 'Test Body']);
  });
});

This testing approach has several advantages:

  • It's clear and straightforward. The mock implementation is easy to understand and control.
  • It focuses on the behavior rather than the implementation. You're testing that EmailService uses its IEmailClient dependency correctly, not how IEmailClient is implemented.
  • It doesn't require any global mocking or special mocking frameworks, making the tests more reliable and less prone to interference.

Conclusion

The refactored (good) example not only adheres to the Single Responsibility Principle and employs Dependency Injection for better design but also significantly improves testability. Tests become more focused on behavior rather than implementation, are easier to write and understand, and are less brittle.