Creating a backend for your application has typically required a totally different skill set to what you might use for the front end. Over time, those lines have blurred as the likes of NodeJS and server-side JavaScript have risen in popularity. Now, with NestJS, you might not even feel like you are working on a backend at all.
NestJS is a NodeJS framework for building server-side applications. The major thing that drew me towards NestJS was its similarities to standard Angular architecture. You will find a lot of the same concepts used in NestJS as you will in an Angular, including:
- TypeScript
- Imports/Exports
- Services/Providers
- Modules (e.g. app.module.ts)
- Pipes
- Guards
Although NestJS introduces its own unique concepts as well – including additional decorators like @Controller
, @Get
, and @Post
– the general methodology is very much Angular inspired. It seems to be that anywhere that the framework can be like Angular, it is (and that is fantastic).
If you are familiar with Angular, or building Ionic/Angular applications, you are already going to feel comfortable with many of the key concepts for NestJS.
As much as I enjoy building servers with vanilla NodeJS and Express (NestJS uses Express behind the scenes), I feel much more at home with NestJS. NestJS brings that Angular experience to the backend – it’s super easy to get started with built-in generators in the Nest CLI and TypeScript support is included by default.
In this tutorial, we are going to focus on getting a simple project set up that uses Ionic for the frontend and NestJS for the backend. For now, we will just be focusing on a bare-bones “Hello world!” style example. We will not be focusing on exploring the theory of NestJS in-depth, and we will only be covering the key concepts required to get a simple server running. In future tutorials, we will cover other aspects of NestJS.
Before We Get Started
Last updated for Ionic 4.0.0-beta.7 and NestJS 5.0.0
This tutorial will assume a basic understanding of Ionic and Angular. It will also help to have a general sense of how integrating a backend with an Ionic applications works – if you are not already familiar, you might be interested in reading this article.
If you’re not familiar with Ionic already, I’d recommend reading my Ionic Beginners Guide or watching my beginners series first to get up and running and understand the basic concepts. If you want a much more detailed guide for learning Ionic, then take a look at Building Mobile Apps with Ionic & Angular.
1. Create a Client/Server Project Structure
When we are creating an Ionic application with a backend, we are going to require two separate projects: the Ionic project, and the NestJS project.
A structure I like to use is to use a single folder for the entire project, which contains a client folder for the Ionic project, and a server folder for the NestJS project. You don’t need to use this structure if you don’t want to, just make sure that you are not creating your NestJS project inside of your Ionic project (or vice versa).
In order to start a new NestJS project, you can install the NestJS CLI:
npm i -g @nestjs/cli
and then you will just need to run:
nest new server
I used server
as the project name here because, as I mentioned, I want the NestJS project to be contained inside of a folder called server – you can supply a different name for the project if you wish.
Once you have generated the NestJS project, you should have a quick browse around the files and folders that were created. As I mentioned, we are going to cover more of the theory stuff in future tutorials, but just from looking around you will likely notice a few similarities between the NestJS project and an Angular (or Ionic/Angular) project.
2. Create a Simple NestJS API
Unlike a frontend client-side application built with Ionic/Angular, a backend/server application primarily performs the function of listening for requests and responding to those requests.
The way in which we would integrate out front-end Ionic application with our backend NestJS server is by making HTTP requests from our Ionic application to the URL of the server where our NestJS backend is hosted. If our NestJS server was running on http://localhost:3000
then perhaps we would make an HTTP request from our Ionic application to http://localhost:3000/messages
. Our NestJS application would then receive that request and then it can send a response back to the Ionic application (what that response is will depend on what it is you code the server to do).
In order to set up a simple API that responds appropriately to that request, we are going to need to set up a @Controller
in NestJS. A @Controller
is a NestJS concept that allows us to set up routes for the application, and those routes can handle responding to requests.
Run the following command to generate a
messages
controller:
nest g controller messages
This will create a basic controller template at src/messages/messages.controller.ts that looks like this:
import { Controller } from '@nestjs/common';
@Controller('messages')
export class MessagesController {}
As is typical in Angular applications, we just have an exported class topped with a particular decorator – in this case, we are just using the NestJS @Controller
decorator to identify this class as a controller.
Also note that this controller is automatically added to app.module.ts if you use the generator:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MessagesController } from './messages/messages.controller';
@Module({
imports: [],
controllers: [AppController, MessagesController],
providers: [AppService],
})
export class AppModule {}
Again, this is all rather Angular-esque – instead of using @NgModule
NestJS is just using @Module
but the general concept remains the same. Modules bundle chunks of functionality together, and in our root module, we are including the various dependencies that will be used throughout the application.
Our controller is going to allow us to listen to and respond to requests on the messages
route. In order to allow for GET
requests, we will also need to import the @Get
decorator and use it to decorate a method that will return a response.
Modify src/messages/messages.controller.ts to reflect the following:
import { Controller, Get } from '@nestjs/common';
@Controller('messages')
export class MessagesController {
@Get()
getMessages() {
return {
message: 'In a real example, I would return all of the messages',
};
}
}
By adding the @Get
decorator above getMessages
, this method will now be triggered when we activate the messages
route by making a request to http://localhost:3000/messages
. Whatever this method returns is what will be supplied in response to that request. We are just supplying a simple message in return, but in a real application, you might fetch some actual messages from a database before returning a response to the requester.
If we wanted to set up an additional route that would allow us to grab a specific message from the backend, we might do something like this:
import { Controller, Get, Param } from '@nestjs/common';
@Controller('messages')
export class MessagesController {
@Get()
getMessages() {
return {
message: 'In a real example, I would return all of the messages',
};
}
@Get(':id')
getMessage(@Param('id') id) {
return {
message: `In a real example, I would return the message with an id of ${id}`,
};
}
}
Now we are importing and using the @Param
decorator, which will allow us to grab parameters from our routes. Once again, the way in which you supply parameters through routes is very similar to Angular routes – we just prefix the desired parameter with a :
. With this additional route defined, we would now be able to make a request to http://localhost:3000/messages/123
and the response we receive will be able to make use of the id
that was supplied.
3. Run the NestJS Server
If we now start our NestJS server by running:
npm run start
inside of our NestJS project, and then navigate to:
http://localhost:3000/
We will first see the Hello world!
message that the default project generates by default (this code is contained in the app.controller.ts file). However, if we then navigate to:
http://localhost:3000/messages
We will see the message we are returning from our getMessages
method:
In a real example, I would return all of the messages
If we then navigate to:
http://localhost:3000/messages/14
we would see the following result:
In a real example, I would return the message with an id of 14
We can see that the server has been able to grab the id
we supplied and use it in its response. In a real-world scenario, we would probably use that id
to look up a specific record in a database.
Interacting with our server through by navigating to URLs through our browser is all well and good, but this isn’t how we would interact with the server generally. We would be making HTTP requests from our client-side application, which in this case is our Ionic application.
Let’s investigate how to do that.
4. Enable CORS (Cross-Origin Resource Sharing)
Before we can make requests to our server (in development at least), we will need to enable CORS (Cross-Origin Resource Sharing). By default, requests from one domain to another are blocked unless the server specifically allows it. Since our Ionic application will be running on localhost:8100
and the server will be running on localhost:3000
our requests from Ionic to NestJS will be blocked.
If you want to know more about what CORS is exactly, I would recommend reading Dealing with CORS (Cross-Origin Resource Sharing) in Ionic Applications.
Enabling CORS in a NestJS server is simple enough, we just need to add app.enableCors()
to the main.ts file in our NestJS project:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
await app.listen(3000);
}
bootstrap();
Make sure to stop and re-run your server after making these changes:
Ctrl + C
npm run start
5. Integrating a NestJS API with an Ionic Application
All we have left to do now is to make the request from our Ionic application to the NestJS server we have running (you do need to make sure that you keep it running, otherwise the request won’t work).
Create the following service to interact with the NestJS API:
ionic g service services/Messages
We will also be making use of the HttpClient library, so make sure that you have the HttpClientModule set up the root module file for your Ionic project.
Modify src/app/services/messages.service.ts to reflect the following:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs'
@Injectable({
providedIn: 'root'
})
export class MessagesService {
constructor(private http: HttpClient) {
}
getMessages(): Observable<Object> {
return this.http.get('http://localhost:3000/messages');
}
getMessage(id: string): Observable<Object> {
return this.http.get(`http://localhost:3000/messages/${id}`);
}
}
We’ve just created two simple methods that will make our HTTP requests to the server for us. Now, all we need to do is make use of these somewhere – as an example, we are just going to add it to the ngOnInit
hook for the home page.
Modify src/app/home/home.page.ts to reflect the following:
import { Component, OnInit } from '@angular/core';
import { MessagesService } from '../services/messages.service';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
constructor(private messages: MessagesService){
}
ngOnInit(){
this.messages.getMessages().subscribe((res) => {
console.log(res);
});
this.messages.getMessage('12').subscribe((res) => {
console.log(res);
});
}
}
If we were to serve our Ionic application now (whilst the NestJS server remains running) we would see the following values output to the console:
As you can see, both requests have received a successful response from our NestJS server.
Summary
We have just scratched the surface of NestJS, so there will be plenty more tutorials to come in the future that will cover all sorts of concepts and examples. The good news is that if you already know Angular, then you already know a lot about NestJS. In the meantime, it is always a good idea to have a read through the official documentation.