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#01GET /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.javapublic class CustomerService {
public CustomerService(KeyService keyService) {}
}
- an instance of type
KeyServiceis passed to the constructor. - we don't even use
@Autowiredat 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
finalkeyword. 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
KeyServiceso 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.
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