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
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.
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