Chapter 1. Introduction
Every web developer relies heavily on one web framework or another (sometimes more if their services have different requirements) and companies will rely on many frameworks, but each has its own pros and cons. These frameworks provide just that, a frame for developers to build on top of, providing the basic functionality that any web framework must provide in order to be considered as a good choice for a developer or company to use in their tech stack. In this book, we will talk about many of those parts of the framework you would expect to see in a progressive framework such as Nest. These include:
- Dependency Injection
- Authentication
- ORM
- REST API
- Websockets
- Microservices
- Routing
- Explanation of Nest specific tools
- OpenApi (Swagger) Documentation
- Command Query Responsibility Segregation (CQRS)
- Testing
- Server-side rendering with Universal and Angular.
Nest provides more of these features because it is a modern web framework built on top of a Node.js Express server. By leveraging the power of modern ES6 JavaScript for flexibility and TypeScript to enforce type safety during compile time, Nest helps bring scalable Node.js servers to a whole new level when designing and building server-side applications. Nest combines three different techniques into a winning combination that allows for highly testable, scalable, loosely coupled and maintainable applications. These are:
- Object-Oriented Programming (OOP): A model that builds around objects instead of actions and reusability rather than niche functionality.
- Functional Programming (FP): The designing of determinate functionality that does not rely upon global states, ie. a function f(x) returns the same result every time for some set parameters that do not change.
- Functional Reactive Programming (FRP): An extension of FP from above and Reactive programming. Functional Reactive Programming is at its core Functional Programming that accounts for a flow across time. It is useful in applications such as UI, simulations, robotics and other applications where the exact answer for a specific time period may differ from that of another time period.
Topics discussed
Each of the topics below will be discussed in more detail in the following chapters.
Nest CLI
New in version 5 of Nest there is a CLI that allows for command line generation of projects and files. The CLI can be installed globally with:
npm install -g @nestjs/cli
Or through Docker with:
docker pull nestjs/cli:[
version]
A new Nest project can be generated with the command:
nest new[
project-name]
This process will create the project from a typescript-starter and will ask for the name
, description
, version
(defaults to 0.0.0), and author
(this would be your name). After this process is finished you will have a fully setup Nest project with the dependencies installed in your node_modules
folder. The new
command will also ask what package manager you would like to use, in the same way that either yarn
or npm
can be used. Nest gives you this choice during creation.
The most used command from the CLI will be the generate
(g) command, this will allow you to create new controllers
, modules
, servies
or any other components that Nest supports. The list of available components is:
class
(cl)controller
(co)decorator
(d)exception
(e)filter
(f)gateway
(ga)guard
(gu)interceptor
(i)middleware
(mi)module
(mo)pipe
(pi)provider
(pr)service
(s)
Note that the string in the brackets is the alias for that specific command. This means that instead of typing:
nest generate service[
service-name]
In your console, you can enter:
nest g s[
service-name]
Lastly, the Nest CLI provides the info
(i) command to display information about your project. This command will output information that looks something like:
[
System Information]
OS Version : macOS High Sierra NodeJS Version : v8.9.0 YARN Version : 1.5.1[
Nest Information]
microservices version : 5.0.0 websockets version : 5.0.0 testing version : 5.0.0 common version : 5.0.0 core version : 5.0.0
Dependency Injection
Dependency Injection is the technique of supplying a dependent object, such as a module or component, with a dependency like a service, thereby injecting it into the component’s constructor. An example of this taken from the sequelize chapter is below. Here we are injecting the UserRespository
service into the constructor of the UserService
, thereby providing access to the User Database repository from inside the UserService
component.
@
Injectable
()
export
class
UserService
implements
IUserService
{
constructor
(
@
Inject
(
'UserRepository'
)
private
readonly
UserRepository
:typeof
User
)
{
}
...
}
In turn this UsersService
will be injected into the UsersController in the src/users/users.controller.ts
file, which will provide access to the UsersService
from the routes that point to this controller. More about Routes and Dependency injection in later chapters.
Authentication
Authentication is one of the most important aspects of developing. As developers, we always want to make sure that users can only access the resources they have permission to access. Authentication can take many forms, from showing your drivers license or passport to providing a username and password for a login portal. In recent years these authentication methods have expanded out to become more complicated, but we still need the same server-side logic to make sure that these authenticated users are always who they say they are and persist this authentication so they do not need to reauthenticate for every single call to a REST API or Websocket because that would provide a pretty terrible user experience. The chosen library for this is ironically named Passport as well, and is very well known and used in the Node.js ecosystem. When integrated into Nest it uses a JWT (JSON Web Token) strategy. Passport is a Middleware that the HTTP call is passed through before hitting the endpoint at the controller. This is the AuthenticationMiddleware
written for the example project that extends NestMiddleware
, authenticating each user based on the email in the request payload.
@
Injectable
()
export
class
AuthenticationMiddleware
implements
NestMiddleware
{
constructor
(
private
userService
:UserService
)
{
}
async
resolve
(
strategy
:string
)
:
Promise
<
ExpressMiddleware
>
{
return
async
(
req
,
res
,
next
)
=>
{
return
passport
.
authenticate
(
strategy
,
async
(
/*...*/
args
:any
[])
=>
{
const
[,
payload
,
err
]
=
args
;
if
(
err
)
{
return
res
.
status
(
HttpStatus
.
BAD_REQUEST
).
send
(
'Unable to authenticate the user.'
);
}
const
user
=
await
this
.
userService
.
findOne
({
where
:
{
payload.email
}
});
req
.
user
=
user
;
return
next
();
})(
req
,
res
,
next
);
};
}
}
Nest also implements Guards, which are decoratorated with the same @Injectable()
as other providers. Guards restrict certain endpoints based on what the authenticated user has access to. Guards will be discussed more in the Authentication chapter.
ORM
An ORM is an Object-relational mapping and is one of the most important concepts when dealing with communication between a server and a database. An ORM provides a mapping between objects in memory (Defined classes such a User
or Comment
) and Relational tables in a database. This allows you to create a Data Transfer Object that knows how to write objects stored in memory to a database, and read the results from an SQL or another query language, back into memory. In this book, we will talk about three different ORMs: two relational and one for a NoSQL database. TypeORM is one of the most mature and popular ORMs for Node.js and thus has a very wide and flushed out feature set. It is also one of the packages that Nest provides its own packages for: @nestjs/typeorm
. It is incredibly powerful and has support for many databases like MySQL, PostgreSQL, MariaDB, SQLite, MS SQL Server, Oracle, and WebSQL. Along with TypeORM, Sequelize is also another ORM for relational data.
If TypeORM is one of the most popular ORMs, then Sequelize is THE most popular in the Node.js world. It is written in plain JavaScript but has TypeScript bindings through the sequelize-typescript
and @types/sequelize
packages. Sequelize boasts strong transaction support, relations, read replication and many more features. The last ORM covered in this book is one that deals with a non-relational, or NoSQL, database. The package mongoose
handles object relations between MongoDB and JavaScript. The actual mapping between the two is much closer than with relational databases, as MongoDB stores its data in JSON format, which stands for JavaScript Object Notation. Mongoose is also one of the packages that has a @nestjs/mongoose
package and provides the ability to query the database through query chaining.
REST API
REST is one of the main design paradigms for creating APIs. It stands for Representative State Transfer, and uses JSON as a transfer format, which is in line with how Nest stores objects, thus it is a natural fit for consuming and returning HTTP calls. A REST API is a combination of many techniques that are talked about in this book. They are put together in a certain way; a client makes an HTTP call to a server. That server will Route the call to the correct Controller based on the URL and HTTP verb, optionally passing it through one or more Middlewares prior to reaching the Controller. The Controller will then hand it off to a Service for processing, which could include communication with a Database through an ORM. If all goes well, the server will return an OK response to the client with an optional body if the client requested resources (GET request), or just a 200/201 HTTP OK if it was a POST/PUT/DELETE and there is no response body.
WebSockets
WebSockets are another way to connect to and send/receive data from a server. With WebSockets, a client will connect to the server and then subscribe to certain channels. The clients can then push data to a subscribed channel. The server will receive this data and then broadcast it to every client that is subscribed to that specific channel. This allows multiple clients to all receive real-time updates without having to make API calls manually, potentially flooding the server with GET requests. Most chat apps use WebSockets to allow for real-time communication, and everyone in a group message will receive the message as soon as one of the other members sends one. Websockets allow for more of a streaming approach to data transfer than traditional Request-Response API’s, because Websockets broadcast data as it’s received.
Microservices
Microservices allow for a Nest application to be structured as a collection of loosely coupled services. In Nest, microservices are slightly different, because they are an application that uses a different transport layer other than HTTP. This layer can be TCP or Redis pub/sub, among others. Nest supports TCP and Redis, although if you are married to another transport layer it can be implemented by using the CustomTransportStrategy
interface. Microservices are great because they allow a team to work on their own service within the global project and make changes to the service without affecting the rest of the project since it is loosely coupled. This allows for continuous delivery and continuous integration independent of other teams microservices.
GraphQL
As we saw above, REST is one paradigm when designing APIs, but there is a new way to think about creating and consuming APIs: GraphQL. With GraphQL, instead of each resource having its own URL pointing to it, a URL will accept a query parameter with a JSON object in it. This JSON object defines the type and format of the data to return. Nest provides functionality for this through the @nestjs/graphql
package. This will include the GraphQLModule
in the project, which is a wrapper around the Apollo server. GraphQL is a topic that could have an entire book written about it, so we don’t go into it any further in this book.
Routing
Routing is one of the core principles when discussing web frameworks. Somehow the clients need to know how to access the endpoints for the server. Each of these endpoints describes how to retrieve/create/manipulate data that is stored on the server. Each Component
that describes an API endpoint must have a @Controller(‘prefix’)
decorator that describes the API prefix for this component’s set of endpoints.
@
Controller
(
'hello'
)
export
class
HelloWorldController
{
@
Get
(
‘
world
’
)
printHelloWorld() {
return
‘
Hello
World
’
;
}
}
The above Controller is the API endpoint for GET /hello/world
and will return an HTTP 200 OK
with Hello World
in the body. This will be discussed more in the Routing chapter where you will learn about using URL params, Query params, and the Request object.
Nest specific tools
Nest provides a set of Nest.js specific tools that can be used throughout the application to help with writing reusable code and following SOLID principles. These decorators will be used in each of the subsequent chapters, as they define a specific functionality:
- @Module: The definition for this reusable package of code within the project, it accepts the following parameters to define its behavior. ⋅⋅ Imports: These are the modules that contain the components used within this module. ⋅⋅ Exports: These are the components that will be used in other modules, that import this module. ⋅⋅ Components: These are the components that will be available to be shared across at least this module through the Nest Injector. ⋅⋅ Controllers: The controllers created within this module, these will define the API endpoints based on the routes defined.
- @Injectable: Almost everything in Nest is a provider that can be injected through constructors. Providers are annotated with
@Injectable()
. .. Middleware: A function that is run before a request is passed to the route handler. In this chapter, we will talk about the difference between Middleware, Async Middlewares and Functional Middleware. .. Interceptor: Similar to Middleware, they bind extra logic before and after the execution of a method, and they can both transform or completely override a function. Interceptors are inspired by Aspect-Oriented Programming (AOP). .. Pipe: Similar to part of an Interceptors functionality, Pipe transforms input data to the desired output. .. Guard: A smarter and more niche Middleware, Guards have the singular purpose of determining if a request should be handled by the router handler or not. ..* Catch: Tell anExceptionFilter
what exception to look for and then bind data to it. - @Catch: Binds metadata to the exception filter and tells Nest that a filter is looking only for the exceptions listed in the
@Catch
.
Note: In Nest Version 4 not everything under @Injectable()
listed above uses the @Injectable()
decorator. Components, Middlewares, Interceptors, Pipes, and Guards each have their own decorator. In Nest Version 5 these have all been combined to @Injectable()
to reduced the differences between Nest and Angular.
OpenAPI (Swagger)
Documentation is very important when writing a Nest server, and is especially so when creating an API that will be consumed by others, otherwise the developer writing the clients that will eventually be consuming the API do not know what to send or what they get back. One of the most popular documentation engines out there is Swagger. Like with others, Nest provides a dedicated module for the OpenAPI (Swagger) spec, @nestjs/swagger
. This module provides decorators to help describe the inputs/outputs and endpoints of your API. This documentation is then accessible through an endpoint on the server.
Command Query Responsibility Segregation (CQRS)
Command Query Responsibility Segregation (CQRS) is the idea that each method should either be one that performs an action (command) or requests data (query), but not both. In the context of our sample app, we would not have the database access code directly within the Controller for an endpoint, but rather create a Component (Database Service) that has a method such as getAllUsers()
that will return all the users that the Controllers Service can call, thus separating the question and the answer into different Components.
Testing
Testing your Nest server will be imperative so that once it is deployed their are no unforseen issure and it all runs smoothly. There are two different kinds of tests you will learn about in this book: Unit Tests and E2E Tests (End-to-end Tests). Unit Testing is the art of testing small snippets or blocks of code, and this could be as granular as testing individual functions or writing a test for a Controller
, Interceptor
, or any other Injectable
. There are many popular unit testing frameworks out there, and Jasmine
and Jest
are two popular ones. Nest provides special packages, @nestjs/testing
specifically, for writing unit tests in *.spec.ts
and *.test.ts
classes.
E2E Testing is the other form of testing that is commonly used and is different from unit testing only in that it tests entire functionality rather than individual functions or components, which is where the name end-to-end testing came from. Eventually applications will become so large that it is hard to test absolutely every piece of code and endpoint. In this case you can use E2E tests to test the application from beginning to the end to make sure everything works along the way. For E2E testing a Nest application can use the Jest
library again to mock up components. Along with Jest
you can use the supertest
library to simulate HTTP requests.
Testing is a very important part of writing applications and should not be ignored. This is a chapter that will be relevant no matter what language or framework you end up working with. Most large scale development companies have entire teams dedicated to writing tests for the code that is pushed to production applications, and these are called QA developers.
Server-side rendering with Angular Universal
Angular is a client side application development framework and Angular Universal is a technology that allows our Nest server to pre-render the webpages and serve them to the client, which has numerous benefits that will be discussed in the Server-side Rendering with Angular Universal chapter. Nest and Angular pair very well together due to both using TypeScript and Node.js. Many of the packages that can be used in the Nest server can also be used in the Angular app because they both compile to JavaScript.
Summary
Throughout this book, you will go through each of the above topics in more detail, continuously building on top of prior concepts. Nest provides a clean well-organized framework that implements each of these concepts in a simple yet efficient way that is consistent across all modules because of the modular design of the framework.