Mock/Fake Backend API in Angular – How To

Posted by

Developing in synchronization with the backend could be challenging sometimes. Various things like – down of environment, incorrect response or wrong endpoint behavior can complicate the process and make it hard for the front-end developer to get his tasks done.

A very popular among developers is to mock/fake a specific BE response and work with it until a reliable/stable API version is available again.

The simplest way to mock such data is to comment the http call and return a custom observable, like this:

function getProducts() {
    // return this.http.get(this.getProductsUrl);
    return of([
        {
            id:1,
            productName: "Apple"
        },
        {
            id:1,
            productName: "Orange"
        }
    ])
}

While it’s an option and will do the job, there are a few downsides:

  • In order someone else to use the same fake data, they have to do the same thing in their code
  • If the BE is down and you want to continue working on the FE, you have to manually go through all the services and mock the data
  • We have to be careful and don’t commit this data incidentally

And here comes the advantages of the solution I want to share with you:

  • We will have a central place with all our fake API data and logic. This way the other developers will be able to use your fake responses and vice versa
  • Backend is down ? Just start the application in “fake” mode and continue your development
  • You can commit this mocks, without affecting anything as they will only work when the application is started with specific configuration

Let’s get started 🙂

Create an empty Angular project

Choose a name for the project, mine will be “angular-mock-api” and use the default creation settings.

As we will need a real endpoint to mock/fake, I will use the following one as an example:

https://jsonplaceholder.typicode.com/users

This is basically a fake api, very popular among the front-end developers, because it’s free and allow us to prototype and test different solutions without having our own backend. 

Prepare real API call

The next step would be to import the HttpClientModule in app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

This will allow us to make http calls and receive/send data. Now, when the module is available, we can use it across our components by injecting it. Like this:

constructor(private http: HttpClient) {}

Right after the export class AppComponent, add the following:

 users$ = this.http.get('https://jsonplaceholder.typicode.com/users');

The component(app.component.ts) should look like this:

import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  users$ = this.http.get('https://jsonplaceholder.typicode.com/users');

  constructor(private http: HttpClient) { }
}

We are declaring a new variable users$ which is actually an observable. The observable will return the result from the above mentioned API which we will use for testing.

If you are wondering why we have a dollar sign as a suffix – it’s a common pattern in Angular to have the observables marked like this.

Now, go to the app.component.html and paste the following html:

<div *ngFor="let user of (users$ | async )">
  {{ user.name }}
</div>

Save the settings and start the application using 

npm start

You should see the following:

This is the actual response that we receive from the API. Now let’s fake it 🙂

Create FAKE API Handler

Create a folder with ‘mock’ or ‘fake’ or whatever you want name inside the app one.

The first thing to add there is our configuration file, where we want to define the endpoints we want to fake and put their handler. Later on, the handler can be separated, but for now it will be fine to keep them inside the config file.

So, create a mock.config.ts file with the following content:

import { HttpResponse } from '@angular/common/http';
import { of } from 'rxjs';

export default {
    GET: {
        'https://jsonplaceholder.typicode.com/users': {
            handler: getAllUsers
        }
    }
}

function getAllUsers() {
    return of(new HttpResponse({ status: 200, body:  [
        {
            id:1,
            name: "Viktor"
        },
        {
            id: 2,
            name: "John"
        }
    ]}))
}

The default export of this file will be the definitions. For now, we are having only one of type GET for the users endpoint. 

The fake handler will just return an Observable of HttpResponse with status 200 and two users in the body.

So far, so good. But how we will use this ? Did I hear interceptor ? Yes 🙂

We will create a new file mock.interceptor.ts(in the same folder) which will “intercept” the HTTP calls and if there is a fake one defined – will use the fake one. Otherwise, the real one.

Read more about interceptors here – https://angular.io/api/common/http/HttpInterceptor

The interceptor will contain the following:

import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs';
import { default as mockEndpoints } from './mock.config';

let currentMockEndpoint;

@Injectable()
export class HttpMockApiInterceptor implements HttpInterceptor {
    constructor() {}

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        currentMockEndpoint = mockEndpoints[request.method] && mockEndpoints[request.method][request.url] || null;

        return currentMockEndpoint ? currentMockEndpoint.handler() : next.handle(request);
    }
}

Nothing special here actually. We are checking if a fake endpoint handler is available, if there is one – using it, otherwise continue with normal call using next.handle(request)

Okay, we are having the interceptor, but it will never be executed until we include it in the providers declaration in the module.

We could put it in the app.module.ts directly, but this will mean that the interceptor will get executed every time a request is made. What I would like to do is create a mock module and one more environment. By having a “mock” environment we will be able to have this mock module(including the interceptor) only imported when the mock environment is started. This will help us to have the interceptor and the whole configuration around it only when we use the specific environment.

Again, in the mock folder, create mock.module.ts with the following content:

import { NgModule } from '@angular/core';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { HttpMockApiInterceptor } from './mock.interceptor';


@NgModule({
  declarations: [],
  imports: [],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: HttpMockApiInterceptor,
      multi: true
    }
  ]
})
export class MockModule { }

Nothing special here, just declaring the interceptor.

Now, go to the app.module.ts and add the following line right after the imports:

let extraModules = environment.mockApi ? [MockModule] : [];

And adjust the imports:

  imports: [
    BrowserModule,
    HttpClientModule,
    ...extraModules
  ]

As you may have already noticed, the MockModule will be imported only when the environment variable mockApi is not false/undefined/null. 

But we are not setting it in our application, the module will never be imported?!

Create MOCK Environment for easier use

The last step will be to have a new environment – mock. Create a file in the environments folder with name environments.mock.ts and put the following inside it:

export const environment = {
    production: false,
    mockApi: true
  };
  

Still a few things has to be done in order to have the environment configured. 

Got to the angular.json file and find the following lines:

          "configurations": {
            "production": {
              "fileReplacements": [

On the same level as the “production”, define another configuration like this:

            "mock": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.mock.ts"
                }
              ]
            }

This is telling the compiler what environment file to use when the application is started in “mock” environment.

And one more thing, right after

         "configurations": {
            "production": {
              "browserTarget": "angular-mock-api:build:production"
            },

Add

            "mock": {
              "browserTarget": "angular-mock-api:build:mock"
            }

When you are done with that, go to the package.json and include this script:

"start:mock": "ng serve --c=mock",

By executing 

npm run start:mock

the mock/fake API will be turned on and the interceptor available.

Run the application using this command and you should see the following:

This is the fake response we defined earlier in the mock.config.ts file. 

Stop the application and run only npm start and you will again see the actual/real response.

You can see the final code on GitHub – https://github.com/vikobg/first-class-js/tree/master/angular-mock-api

Hope you learned something new 🙂 

Good luck!