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.