Introduction to the NestJS Tutorial Series
Hey, what's going on guys? My name is Anton and welcome back to a brand new video. Today is going to be the day where we start with a NestJS tutorial series. I am very excited to start this series because I have been using Express as well as NestJS for quite some time, and I actually use NestJS for most of my projects, if not all now. And you've actually seen me using NestJS for some of my videos, and I do have some tutorial series that are pretty long with Nest, but this is going to be a more, a more progressive, smaller-length video series where we're going to pretty much go through a lot of the framework. And I'm going to show you guys how you can build web applications using NestJS, and you're going to see why NestJS itself is very powerful and why you'd want to never use Express or any other framework once you switch over to Nest.
What is NestJS?
So in a nutshell, Nest is really just a framework that is actually built on top of Express. If you go through the documentation and if you've used a little bit of NestJS, you'll actually notice that a lot of things are very similar to how you would set up an Express app. You can think of it like just a large framework, tons of libraries that wrap around Express functions, and it does and adds a lot more functionality and gives you more customization. And it's also first-class support for TypeScript, which means that by default, you can actually use Nest with TypeScript. Though, I'm not sure if you can actually use NestJS with JavaScript; I've only ever used it with TypeScript. Okay, well actually, you know, since it's TypeScript in general, you can write JavaScript in TypeScript. So, yeah, that's pretty much it. But, um, anyways.
Core Concepts: Dependency Injection & IoC
So NestJS itself has many features that a lot of people might not have even heard of before, especially if you've never really understood frameworks such as Spring Boot or Spring, or frameworks that really go in on dependency injection as well as inversion of control. So that's them, those are the two main takeaways that you have to understand when it comes to developing NestJS applications. It's actually not too difficult to understand it, so I'll explain a little bit more down the road when we progress through this tutorial.
Okay, but I'm not going to bore you guys too much with a lot of the, you know, theory, at least not for now. And most of those things are already written on the documentation already. So what we're going to do is we're just going to immediately get started with setting up a simple NestJS application. You're going to see how easy it is to set it up, and it's going to be very easy to create our own routes. Okay.
Installing the NestJS CLI
So what we're going to do is you want to make sure first that you have the NestJS CLI installed. So go ahead and make sure you install that. Make sure you go into your console and type npm i (short for install) -g and what this will do is install globally. And you want to type in @nestjs/cli. Okay, so that will install the dependency globally for you, so you can use the NestJS CLI to pretty much scaffold a project very quickly. If you don't want to use the CLI, you can just clone a simple starter project from the official NestJS repository, but personally for me, I enjoy using the CLI tool. Okay.
Scaffolding a New Project
So it's actually really easy to set up a NestJS project. If you've actually used Angular before, Nest is actually inspired by Angular, so the CLI is actually very closely similar to the Angular CLI. Okay.
So what we're going to do is we're simply just going to type nest new and I'm going to go and just call this nestjs-tutorial. And you're going to see that it's going to give us a prompt. It's going to say, "We're scaffolding your application," and you can see that we have a bunch of files that have been generated. It's going to ask you which package manager would you like to use. I'm going to stick with Yarn. I personally like using Yarn because it's a lot faster. So we're going to hit enter and we're just going to wait for the application to pretty much finish scaffolding.
It shouldn't take too long. I would say at most take probably like 30 seconds to maybe a minute, depends on your computer though. Looks like it's already done. That was pretty fast. And, um, cool. So let's go ahead and get started. So let's cd into nestjs-tutorial and let's just take a look at the project itself and let's look at all the files and go over everything, so that way we are familiar with the project itself. So you guys will see how less scary it is. Okay.
Exploring Project Configuration Files
So this is what a NestJS project would look like once you have finished scaffolding. So you have lots of files like any ordinary project, but we'll go through it one by one.
So some of the simple files that you may have seen before are the ESLint files. So these are pretty self-explanatory, I'm not really going to explain it. You have your .gitignore files, you have your .prettierrc. Prettier is another formatter, so you can use the local .prettierrc configuration if you want.
There's a nest-cli.json file. So this is pretty much configuration for the CLI tool. So since you're working locally with this project, you can actually configure stuff in here to work with the NestJS CLI. I'm pretty sure there's more documentation on that. Let's see, it should be right over here. And you can see that there should be some mention about the configuration for the CLI. I personally have never really used that config file much, but I'm sure that if you wanted to figure some stuff out, it should probably be somewhere over here. There's got to be something somewhere over here. It'll be somewhere over here. So you guys can, you guys can just look into that. Okay, but anyways.
Understanding package.json Scripts
So, the package.json file, that's pretty straightforward. There are a lot of scripts. So you have prebuild script, you have build, format, start, start:dev. A lot of these are very straightforward. The ones that I personally use the most are start:dev, test, test:e2e, and test:watch as well as build. So whenever I need to build my NestJS application from TypeScript, compile down into JavaScript, I will run the nest build command. Or at least that's what my GitHub Actions pipeline will do, and then it will be ready to serve the application using the built, like the compiled, compiled JavaScript. Okay.
But there's also some configuration for Jest too. If you don't like it inside the package.json, you can actually put it inside its own Jest config file, but it's pretty much set up already in a way that you can actually run E2E tests as well as unit tests immediately. So we'll actually dive into unit testing with NestJS later.
We have a README, so like every project, scaffolder has a README. You have a tsconfig.build.json, okay, and you also have a tsconfig.json file. These are things that are pretty straightforward if you are already familiar with TypeScript, but that pretty much configures stuff for the compiler, specifically on how to properly build certain things. Okay.
The Build Process and the dist Folder
So let's go ahead and play around with some of the scripts that are listed in the package.json file. So if I go over to my terminal and if I just type yarn build, this will pretty much run this build script over here. So it'll actually run nest build, which you can see over here. It ran rimraf dist and then it ran nest build. And you can see that this dist folder is going to have all of the compiled or transpiled JavaScript code, and this is the code that you're actually going to execute on your server whenever your application is ready to be deployed. I should have said production server.
So in dev mode, you want to run everything with the TypeScript file. You want to execute all of this TypeScript code because it's a lot faster for you to develop. You can also just run the JavaScript as well, but there's a dev script that makes it a lot easier so that whenever you make changes to your code, it will actually watch everything and it'll restart the server, so that speeds up development. There's a start:prod script, so that's for production. You can see that it executes node dist/main, which pretty much executes this file over here. You can see that this main.js file is the compiled version of main.ts. Okay.
Um, so that's pretty much it with all these other files. So, like I said, these are pretty straightforward, we're not going to really talk about those. So let's go into the src folder and let's talk about what are these files that we see in the src folder. Okay.
The Source Folder: Controllers
So pretty much the very first one is app.controller.spec.ts. So this is just a simple spec file or a test file. So typically this is where you would write all of your test cases. For example, you can see that if you're familiar with testing, and if you've actually tested in Angular before, it's very similar. Okay, you can see over here we have a controller instance, and what it's doing is it's calling the controller's getHello method, and then it'll pretty much assert that response, or the return value of that function, to be 'Hello World!'. But we'll ignore testing for now. We're going to worry about that later on in this tutorial series. I'll teach you guys how to do that as well. Okay.
So we have the app.controller.ts file. So we're going to spend a lot of our times working with controllers. So this is a simple example of what a controller class looks like. Okay, you can see that it has a decorator called @Controller. So this annotates this class as a controller. Okay? And you can see that this controller has a constructor, and this constructor has an instance of the AppService class, which we'll get into in just a moment.
Right over here, we have a function called getHello, so that was similar to the, that's actually the function you saw over here that was being called in the spec file. And this getHello function returns a string, as you can see from the type annotation. And so we know that this appService.getHello() method call is actually going to return a string. Okay.
Now, every single GET request that you're going to be implementing, it's going to be done inside a controller. Likewise with your POST, PUT, and PATCH requests, or even DELETE requests, those are going to be done inside a controller class. And it's going to be done by creating a function, or I should say method, because we're dealing with classes. It's going to be done by creating a method to handle that request. You use the appropriate decorator. So let's say if we're dealing with GET requests, so we're trying to implement a GET request, we're going to use the @Get decorator. And what's going to happen is it's going to say, "Hey, look, whenever we call the specific route for this route over here..." So by default, since we don't have any prefixes for our API, this is just simply the the base route, the slash route, with no other additional prefix or suffix. So it's just going to give us, it's going to invoke this function. Okay? If you're using a POST request, you will use the @Post decorator, which we will cover later. So you can see that over here. Let's take a look at the App Service.
The Source Folder: Services and Injectables
So you can see that this AppService, and if you're using Visual Studio Code, you can actually right-click this and you can click on "Go to Definition," but or you can just click on this app.service.ts class or this file. So you can see that over here we have what's called an @Injectable. Now, this @Injectable decorator pretty much annotates a class that can actually be injected. It pretty much marks it as a provider. And what providers are, they can pretty much be injected into literally any part of your application. Remember what I mentioned earlier, the key takeaway with NestJS is that it uses dependency injection, okay? And it has its own inversion of control, its own IoC container that keeps track of all of the providers. And so essentially, what's going to happen is when you mark a service as an injectable, you can pretty much inject it anywhere in your application.
Okay? And what that means is instead of creating instances over and over and over again and all over your application, you can actually just inject the single instance that was created by the IoC container. Because essentially, there's a container that keeps track of all of the dependencies, okay, during runtime, and you can inject it whenever you want. Okay? So like I said, it will take a little bit of time to get used to this whole thing, but I'll explain it as we go. But this is just a class that is marked as an injectable, and it has a method called getHello, and it returns a string. Okay, and when we call this method, we call it this.appService.getHello(), that's going to be returned as a response to the route that we visit. Okay.
The Source Folder: Modules and Main Entrypoint
Now there's the app.module. Okay, so like I said, it's very similar to Angular. So these modules are pretty much their own, you can think of it like the main route of your entire, of your entire, like of your entire application. So the app.module.ts is the main module. This is where it has all of the controllers, has all the imports, and all of the providers and any exports. But typically, you will rarely probably ever have to export stuff in the app module. So typically, modules are very useful to make your code a lot more modular, and you can separate concerns into its own domain. So instead of having everything under one controller or one module, you can actually categorize everything to keep your code a lot more organized. Okay.
So like I said, we're not going to play around with the app.module.ts file much because this is the root module. But if we ever need to set up configurations or if we need to import other modules, we would import them inside AppModule.
And last but not least, the main.ts file. This is the main file, obviously. This is where we have a bootstrap function, okay, and it just gets called. And all this does is it pretty much just calls this static method create and it pretty much just bootstraps an application for us based on the AppModule. Okay? So this will pretty much take care of all of the dependencies for us. It'll initialize the entire Nest application, and all we do is we just call app.listen() to the port. Really similar to Express. Okay, and our application is up and running.
So that's pretty much just a high-level overview of how the base project looks like. And we've gone over a lot of things such as the controller and the service as well as the test file. Okay. Like I said, it might be a lot of terminology right now, but like I said, in the next video, we're going to go through everything one by one, and I'm going to make sure that you guys understand how to actually set up a NestJS application with NestJS principles.
Conclusion and What's Next
So I'm going to end this video right over here. So this will be our part one. In the next video, I'm going to show you guys how to create your own module, as well as your own controller and your own service, and how we can test that. So I'll see you guys in my next video. Peace.