Building your Own Simple Server
Everything we have dealt with so far has involved entirely local data. In other words, the Internet is not required — there is no data coming into the application and there is no data going out.
Many applications do operate this way, but many also need to communicate with the outside world. Maybe they want to backup data to some external server and sync across devices, maybe there is shared data in the application like a chat application or social media app, maybe you need to pull in the current weather data into the application. There are lots of scenarios where we might need to pull data into our application or send it out into the world wide web (and we will be doing exactly this in a real application soon).
But first, some theory and a fun little example. This module is going to focus on exploring the basics of communicating with an external API.
The Lucky Number API
To help remove some of the mysteriousness around servers if you have never built your own… we are going to build our own API/server! If you are not aiming to be a backend or fullstack developer then this isn’t likely something you will actually need to do, but it will help to have a basic understanding of what is going on.
The idea is reasonably simple. Our application will allow the user to guess a number, and if it is the “lucky” number they win! We could handle this entirely locally, we could just have our application generate the “lucky” number, but there are nefarious users out there. If we were giving away more than just the satisfaction of having won, then people could inspect the client side code to see how numbers are being generated.
If we handle this on an external server, the nefarious users now no longer have access to see how this number is generated — all they get is the result. That isn’t to say this approach is perfectly secure, I’m sure some clever hackers could still probably find a way to cheat the system but it’s good enough for our demonstration.
Although this is just a fun example, handling logic on servers in this way is also useful in the real world. Using secret API keys is a common example. Let’s say you have some API key that needs to be kept secret, and you use that API key to access some service that your application uses. You can’t just put that API key in your client side/frontend code (the Angular application) because anybody could easily steal the API key and use it themselves. If you instead store that key on your server, and have your server handle making the requests using the API key instead, the API key is now no longer exposed to your users (and your frontend application makes requests to your server).
Ok, let’s get into building this server!
Creating a REST API with NestJS
NestJS is an amazing way to build backend applications. We aren’t really going
to be learning anything about it in this course, we are just going to use the
starter template to create the most basic REST server possible. Again, not going
to dive too deep into what
REST is, but
the basic idea is that it will allow us to make GET requests to a URL to
retrieve data from the server, and POST requests to the server to send it
data.
NestJS is basically a way to build backend/server applications in an Angular style. Rather than using bare bones NodeJS, NestJS makes it easier for us to build servers with NodeJS by giving us a lot of things out of the box. It is not the same thing as Angular, but so many of the concepts come from Angular that you already have a massive head start in using NestJS if you know Angular concepts already. Hopefully this little teaser makes you curious to dive into NestJS a little more on your own.
npx @nestjs/cli new apiOnce the application is generated, open it in your editor.
@Get() getHello(): string { return this.appService.getHello(); }I’m not going to dive into NestJS concepts, but the basic idea here is that
a controller controls what happens when the application makes a request to certain endpoints
on your server. For example if a GET request is made to
https://my-api.com/users or https://my-api.com/users/1. To do this, we
decorate a function inside of the @Controller('users') with the @Get()
decorator and whatever that function returns is what the request will return.
Since no argument is supplied to @Get(), the code above is just defining the
GET response for the default endpoint (i.e. when we make a request to
https://my-api.com/). We are going to keep things super simple here because
this module isn’t about learning how to build servers, it’s about learning how
to integrate with them.
@Get() getLuckyNumber(): number { return Math.floor(Math.random() * 100); }Now our default endpoint is going to return a random number between 0 and 99
— this isn’t the greatest way to generate a random number (random numbers
generated by a computer are never really random) but it will do for us!
There is one more thing we will need to do before our server will work, we need to enable CORS or Cross-origin Resource Sharing. The basic idea is that if we try to make a request to our server from some other domain, which is what we will be doing in the case of our Angular application, it will be blocked by default. To allow other domains to make requests to our server we need to enable it.
async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors(); await app.listen(3000);}Now we can start our server with:
npm run start:devand you should see something like this:
[NestApplication] Nest application successfully started +2msYour server should now be running at this address:
http://localhost:3000We are just running this locally on our own machine for development purposes, but when we try to access it from our Angular application we will still need to use the same techniques as if it were hosted on the Internet.
Integrating an Angular application with a Server
Now let’s see if we can make a GET request from an Angular application to that
server that is now running on our machine. I will walk you through the specific
steps of making an HTTP request to the server, but I am going to leave
creating a new application to you. You can set it up however you like but we can
run all the code we need just from the root component.
If you need to, you can go back to the start of the Quicklists module to see how that application is generated.
As usual, if you want a bit of a challenge you might try your hand at setting up your own feature with routes within the application, but that isn’t totally necessary for this example.
This is something we have learned in the basics module, so you might want to go
back and review that if the details are fuzzy, but we are going to make use of
the HttpClientModule and HttpClient from Angular in order to pull in data
from our server.
import { ApplicationConfig, importProvidersFrom } from '@angular/core';import { provideRouter } from '@angular/router';
import { routes } from './app.routes';import { provideHttpClient } from '@angular/common/http';
export const appConfig: ApplicationConfig = { providers: [provideRouter(routes), provideHttpClient()],};import { HttpClient } from '@angular/common/http';import { Component, OnInit, inject } from '@angular/core';import { RouterOutlet } from '@angular/router';
@Component({ selector: 'app-root', imports: [RouterOutlet], template: ` <router-outlet></router-outlet> `, styles: [],})export class AppComponent implements OnInit { private http = inject(HttpClient);
ngOnInit() { this.http.get('http://localhost:3000/').subscribe((res) => { console.log(res); }); }}If you serve the application and check your console now, you should see something like this:
94We have successfully pulled a random number in from our server to our application! But… our approach is a bit flawed here. The server just tells us what the lucky number is… that’s not really a fun way to play. What if instead of just requesting the lucky number from the server, we create an endpoint where we can send our guess of the lucky number, and the server will tell us if we were right or not.
Making POST requests with a payload
import { Body, Controller, Get, Post } from '@nestjs/common';import { AppService } from './app.service';
export class GuessLuckyNumberDto { guess: number;}
@Controller()export class AppController { constructor(private readonly appService: AppService) {}
@Get() getLuckyNumber(): number { return Math.floor(Math.random() * 100); }
@Post() guessLuckyNumber(@Body() guessLuckyNumberDto: GuessLuckyNumberDto) { const luckyNumber = Math.floor(Math.random() * 100); const guess = guessLuckyNumberDto.guess;
if (guess === luckyNumber) { return { result: `You guessed ${guess}... that is correct!` }; } else { return { result: `You guessed ${guess}... but the correct answer was ${luckyNumber}`, }; } }}Now we will be able to make a POST request to the same endpoint and it will
use the guessLuckyNumber method instead. Since we want to send a payload/data
along with our request containing our guess we need to create a DTO or
Data Transfer Object. We have done that here with GuessLuckyNumberDto
which basically just defines what we expect that data to be. We then access that
DTO within our method.
Send a POST request from Angular
ngOnInit() { this.http.get('http://localhost:3000/').subscribe((res) => { console.log(res); });
const guess = { guess: 14 };
this.http.post('http://localhost:3000/', guess).subscribe((res) => { console.log(res); }); }If we check the console of the Angular application now we should see something like:
80{result: 'You guessed 14... but the correct answer was 52'}For a bit of fun — instead of hard coding our lucky number guess, see if you can create a form that will allow you to supply a number dynamically, and then when you submit that form it will check the number against the server.
If you want you could just console.log if the guess was right or not, or you
could also display that in the UI of the application. You could take this as far
as you want in the Angular application…
Add a history of previous guesses, add some styling, add an artificial delay after the request is submitted to make waiting for the guess more suspenseful.
Anything that seems remotely fun or interesting to you, just do that. These independent projects are what are going to help you learn the most, no matter how silly it might seem.