Blog

Web technology tidbits

In-memory web API for testing Angular apps

Background

Front end and middleware components must agree on an interface for authentication, authorization, exchanging data, etc., and engineers developing these components often work on the same team or closely together. These components, however, do not require tight coupling, and separately developing and running components can increase feature velocity and improve reliability. To separate concerns between an Angular application and its middleware, let’s set up an in-memory API that runs in the debugging environment, while automatically switching to the HTTP API when the application runs in production.

Configuration

To get started, create an Angular CLI project.

Open a terminal to the project directory, and run the command ‘npm install --save-dev angular-in-memory-web-api'. This project gives us the bulk of the functionality we need to run an in-memory API.

Next, create a directory named ‘in-memory-api’ (my-app/src/app/in-memory-api). Open a terminal to this directory, and run the command ‘ng g service app-in-memory-api’. This service will act as our data repository, returning the data to API requests.

import { InMemoryDbService } from 'angular-in-memory-web-api';
export class AppInMemoryApi implements InMemoryDbService {
createDb() {
let heroes = [
{
name: ‘Superman’,
description: ‘Flies through the air to save damsels in distress.’,
powers: [‘flight’]
},
{
name: ‘Batman’,
description: ‘Responds stealthily to rescue citizens of Gotham from crime.’,
powers: [‘technology’]
}
]
return { ‘heroes’: heroes };
}
}

The return object key is the name of the API route (which becomes ‘/api/heroes’ by default), and the value is the data we want to return when the in-memory API receives a request to this route.

With the data store created, create a directory named ‘modules’ (my-app/src/app/modules). Open the terminal to this directory, and run the command ‘ng g module app-http’. This module will return Angular’s HTTP service when the application is running in production, and our in-memory API service otherwise.

import { NgModule, Injector } from '@angular/core';
import {
HttpModule, XHRBackend, BrowserXhr,
ResponseOptions, XSRFStrategy
} from '@angular/http'; import {
InMemoryBackendService,
InMemoryDbService
} from 'angular-in-memory-web-api';
import { AppInMemoryApi } from '../in-memory-api/app-in-memory-api.service'
import { environment } from '../../environments/environment'; export function getXHRBackend(injector: Injector, browser: BrowserXhr,
xsrf: XSRFStrategy, options: ResponseOptions): any {
if (environment.production) {
return new XHRBackend(browser, options, xsrf);
} else {
return new InMemoryBackendService(
injector,
new AppInMemoryApi(),
{apiBase: 'api/latest/'}
);
}
} @NgModule({
imports: [HttpModule],
providers: [
{
provide: XHRBackend,
useFactory: getXHRBackend,
deps: [Injector, BrowserXhr, XSRFStrategy, ResponseOptions]
}
]
}) export class AppHttpModule {
}

The InMemoryDbService has a number of options described on the Github page for customizing the service to fit the needs of specific APIs. Inside the ‘getXHRBackend’ function is where we can configure these options. In the example above, I’ve changed the base route of the in-memory API from ‘api’ to ‘api/latest’ to demonstrate how we can adjust the default route for situations like API versioning.

Finishing touches

Finally, open ‘app.module.ts’ to import the custom HTTP service we created. Add an import statement to the top of the file and the ‘AppHttpModule’ reference within the ‘imports’ section of ‘@NgModule’.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppHttpModule } from './modules/app-http.module';
import { AppComponent } from './app.component'; @NgModule({
declarations: [
AppComponent,
],
imports: [
BrowserModule,
FormsModule,
AppHttpModule,
],
providers: [],
bootstrap: [AppComponent]
}) export class AppModule { }

Now, run ‘ng serve’ and point the browser to ‘http://localhost:4200/api/latest/heroes’. The data we added to our in-memory data store will be returned to the browser.

Conclusion

From here, we can add additional data and endpoints to the AppInMemoryApi class and call these from components throughout the application using the Angular Http module. With this framework in place, we can effectively engineer the front end of an application in isolation until we’re ready for integration testing with a middleware component. To run the application against the HTTP API rather than the in-memory API, run ‘ng serve --prod’.