Difference between Spring boot and NestJS

NestJs is similar to Spring boot in many ways. however, there are a few differences that can be frustrating if you don't know about them.

Differences

Order of handler methods

Unlike Spring boot, the order of the handler methods declared inside the Controller is important.

let's take a look at the Controller implementation in NestJs below.

TS
@Controller('customers') export class CustomerController { /** Β * get document for the given region */** get document for the given region @Get(':docId/:region') documentByRegion( @Param('docId') documentId: string, @Param('region') region: string) {} /** * return statistics data */** /** return statistics data @Get('dashboard/stats') findStats() {} }

the findStats method is not called if you send a request for statistical data like this

REQ#01
GET /customers/dashboard/stats HTTP/1.1
  • this request is intercepted by documentByRegion.

Looking at the Routing rules, the /customers:docId/:region rule is wider than the /customers/dashboard/stats rule.

Spring boot routes the request [REQ#01] to dashboard/stats with the best match, but Nestjs evaluates the routing rules in the order of the methods declared in the Controller.

it checks them one by one from the top, and since the rule /customer/:docId/:region corresponds to request [REQ#01], it doesn't evaluate the more specific routing rules that follow it.

values are associated with the arguments as follows

  • documentId === 'dashboard'
  • region === 'stats'

order of handlers

place more specific routing rules first.

how should we order the following routing rules?

Routing rules
@Post('login') @Get('login') @Get(':userId') @Get('recent')

note that @Post and @Get are different methods, so the order doesn't matter.

in the @Get routing rule, login and recent are also distinguishable on their own, so the order doesn't matter.

however, :userId must be placed after login and recent.

otherwise, the :userId rule will intercept both the login and recent rules.

this happens over and over again.

Decorator VS Annotation

this is not a framework difference, it's a language difference.

The function implementation that does something similar to Java's @Annotation is called @Decorator() in typescript.

You can think of it as a means of implementing AOP.

Unlike Java's @annotaion, it is always called like a function. like @Injectable()

Dependency Injection

In Springboot, dependency injection is completed automatically by using @Autowired or by defining the required component as an argument to the constructor.

CustomerService.java
public class CustomerService { public CustomerService(KeyService keyService) {} }
  • an instance of type KeyService is passed to the constructor.
  • we don't even use @Autowired at this point.

In NestJS, besides controller, service, and repository, there is one more component called module.

when we move [CustomerService.java] to NestJS, we keep the following structure as a matter of practice

Module structure
- customer +- customer.controller.ts +- customer.service.ts +- customer.repository.ts +- customer.module.ts

and the module should register the classes to be dependency injected like this

customer.module.ts
@Module({ imports: [KeyModule], controllers: [CustomerController], providers: [CustomerService], // exports: [], }) export class SubjectsModule {}
  • imports: [KeyModule] - If you want to reference a component from another module, you need to put it in the imports like this.

we are importing KeyModule from customer.module.ts, but unlike Spring boot, if we want to inject services or repositories from other modules, we need to import their moudule as above.

if we replace CustomerService.java with NestJS, it should look like below.

customer.service.ts
@Injectable() export class CustomerService { constructor( readonly keyService: KeyService ) {}
  • it's a good idea to declare @Injectable(). It must be entered before the class to trace the object graph and make dependency injection work.
  • readonly - Java's final keyword. it is provided at the language level.

to use KeySerivce in customer.service.ts, the Service must be exposed in export to allow references in key.module.ts.

key.module.ts
@Module({ controllers: [KeyController], providers: [KeyService], exports: [KeyService], // Important! }) export class SubjectsModule {}
  • we need to expose KeyService so that it can be referenced by CustomerService.

and again, if you look at the code in customer.module.ts, it doesn't import the Service to reference, it imports the module that defines the Service.

this is quite cumbersome, which is why NestJS provides a separate CLI tool.

https://docs.nestjs.com/cli/overview

instead of coding controllers, services, and modules by hand, you can just type the following to create the files and automatically update the module graph.

Example of CLI
$ nest g s translation => creates translation.service.ts $ nest g co translation => creates translation.controller.ts
  • a shortened version of nest generate service translation.

for detailed usage, see the link below