Setting Status Code and Handling 404 Pages in Angular Universal
Tags: javascript, angular, typescript, tutorial
Published: 2020-01-17 16:59:18

If you use Angular Universal, you probably know that in addition to Server Side Rendering, Universal provides you with the full range of Node.js functionality on the server-side.  
Today we will examine how to use Express.js’s popular `Request` and `Response` API within our Angular applications.  
To illustrate the setup process, I’ll show how I created the simple 404 Not Found page on this blog.

### Laying the foundation
Let’s first create a `NotFoundComponent`, to which we will redirect our users:
```typescript
@Component({
 selector: 'blog-not-found',
 template: `<h2>Seems like this page doesn't exist :(</h2>`
})
export class NotFoundComponent {}
```
And set up proper routes and redirects for our newly-created `NotFoundComponent`:
```typescript
... // other routes
{
   path: '404',
   component: NotFoundComponent
 },
...
```
Now if we go to our 404 page, we’ll see the following:

![The screenshot of NotFound](https://firebasestorage.googleapis.com/v0/b/my-blog-5360d.appspot.com/o/uploads%2Fscreen-shot-2019-12-23-at-121909-ampng-0421?alt=media&token=b0d3b501-630c-4218-831e-9a5065c877c8)

All good, right? Not quite. You see, our Not Found page clearly works for the users (except the godly design, perhaps) but robots (such as search engines) still perceive it to be a valid page of our website that needs to be indexed.  
We can verify this if we look at the Network tab in the DevTools, where we see that the status code for our page is 200 (success) instead of expected 404 (not found):

![Screenshot of wacky response code in devtools](https://firebasestorage.googleapis.com/v0/b/my-blog-5360d.appspot.com/o/uploads%2Fscreen-shot-2019-12-23-at-122419-ampng-0427?alt=media&token=a52a62bc-8fe8-412a-a30e-c3b1546f5b00)

### Using Express.js Request and Response Objects within our application
To set the status code, we will use the `Response` object.
In case you’re not familiar with them, [`Request`](https://expressjs.com/en/api.html#req) (aka `req`) and [`Response`](https://expressjs.com/en/api.html#res) (aka `res`) are the primary way of processing HTTP requests in Express.    

### Providing the Response object to our Angular app
Looking at the [source code of Universal](https://github.com/angular/universal/blob/a922918bcd26339173eea76f3fd2fbac41a39064/modules/express-engine/src/main.ts#L83), we see that unlike `REQUEST`, `RESPONSE` provider is optional and only provided if there is a `res` object in the `RenderOptions`:

```typescript
if (res) {
    providers.push({
        provide: RESPONSE,
        useValue: res
    });
}
```

Therefore, in our `server.ts` file we need to add `res` to the `RenderOptions` object when rendering our pages:

```typescript
app.get('*', (req, res) => {
 res.render('index', { req, res });
});
```
Now we can successfully inject the `req` and `res` objects into our `NotFoundComponent`:
```typescript
import { Optional, Inject } from '@angular/core';
import { RESPONSE, REQUEST } from '@nguniversal/express-engine/tokens';
import { Request, Response } from 'express';
/*
 ...
 ...
*/
constructor(@Optional() @Inject(REQUEST) private request: Request,
            @Optional() @Inject(RESPONSE) private response: Response){
```

Notice that I added the `@Optional()` decorator. This is because `Request` and `Response` objects are purely Express concepts and thus can’t exist in the Browser context. With `@Optional()`, these objects will be equal to null in a Browser environment.

### Setting response status code
Now that we injected the Response object into our `NotFoundComponent`, we can use it as follows:
```typescript
if (isPlatformServer(this.platformId)) {
  this.response.status(404);
}
```

As I mentioned earlier, `Request` and `Response` objects are only available in the Node context, hence before using them we need to ensure we’re executing on the server side by checking `isPlatformServer(...)`.    

Full code of the `NotFoundComponent`:
```typescript
import { Component, OnInit, Optional, Inject, PLATFORM_ID } from '@angular/core';
import { RESPONSE, REQUEST } from '@nguniversal/express-engine/tokens';
import { isPlatformServer } from '@angular/common';
import { Request, Response } from 'express';
 
@Component({
 selector: 'blog-not-found',
 template: `<h2>Seems like this page doesn't exist :(</h2>`
})
export class NotFoundComponent implements OnInit {
 constructor(@Optional() @Inject(REQUEST) private request: Request,
             @Optional() @Inject(RESPONSE) private response: Response,
             @Inject(PLATFORM_ID) private platformId: any) { }
 
 ngOnInit() {
   if (isPlatformServer(this.platformId)) {
     this.response.status(404);
   }
 }
}
```

Now let’s run our app once again and go to 404 with DevTools open:

![404 status code screenshot](https://firebasestorage.googleapis.com/v0/b/my-blog-5360d.appspot.com/o/uploads%2Fscreen-shot-2019-12-23-at-125250-ampng-0428?alt=media&token=4fd87b62-26c9-4449-a1a7-cc079b4629cb)

As you can see, it now works just as we wanted it. Both users and robots must be tremendously happy!

![Dancing panda gif](https://media.giphy.com/media/l3V0lsGtTMSB5YNgc/source.gif)

**Note:** I didn’t show how to use the `Request` object here. However, once injected in the constructor (shown above), it can be used in a fashion similar to `Response`.