ngconf
Published in

ngconf

“Fake it till you make it” or how to mock backend response in an Angular application

drawing of a girl drawn with a dotted line and a girl with the filled line

We, frontend people, can control huge amount of things on the client side (in the browser or in the web app itself). We can implement the best user experience and visualize data in the best way… as long as there is data. No data — no app. At this point, the infamous “Fake it till you make it” comes into play. It suggests that by imitating confidence, competence, and an optimistic mindset, a person can realize those qualities in their real life and achieve the results they seek. In web tech words it says: “let’s mock backend response and keep developing features for our client-side application as if we had real data, so that once we have it, we can handle it properly”.

Indeed, mocking backend is useful in various situations:

  • You don’t want to wait for backend implementation. You’ve got your Swagger definition, but there is no service yet.
  • You don’t want to wait for test users. In some cases, it is really hard to find a test user with a particular edge case configuration.
  • You might be not even allowed to change your test users or the change is not revertible. Imaging developing an onboarding app which should be only displayed to the users once (during the onboarding).
  • You might be just not interested in backend, for example if you are working on a frontend tutorial or a PoC.

Anyway, in case you want to mock your backend response, keep on reading. I’ve got several options for you.

Hardcoded values

Let’s assume that you have an application that displays some bitcoin data (everyone loves bitcoin data these days!). In your service you might have a method to fetch it from some API. The straightforward approach would be to comment out the actual implementation and to replace it with a hardcoded observable. RxJs operator “of” will do the job for you.

public fetchBitcoinData(): Observable<BitcoinMarketData[]> {   //return this.http.get<BitcoinMarketData[]>('/v1/markets.json');   return of([{...}]);
}

In case your data is verbose (like 200 bitcoin market data entries), you might want to store it in a separate file and reference it in your service.

import fakeBitcoinData from '../assets/fake-data/fake-bitcoin-data.json';public fetchBitcoinData(): Observable<BitcoinMarketData[]> {   // ... Commented out actual implementation   return of(fakeBitcoinData);
}

It is important that you name your import somehow instead of using import * from ‘…’, otherwise your data will be imported as a module instead. Additionally, you will need to add “resolveJsonModule” and ”esModuleInterop” in tsconfig.json file under compiler options as shown below:

{  "compilerOptions": 
{ "resolveJsonModule": true,
"esModuleInterop": true
}
}

This solution will work just fine, yet it has several limitations:

  • Only mocking GET requests possible
  • No error response mocking possible
  • Far away from the actual implementation

Let’s try to fix the last aspect. Luckily, we can use Angular-native HttpClient to request local data. We just have to point the API url to our data:

public fetchBitcoinData(): Observable<BitcoinMarketData[]> {   //return this.http.get<BitcoinMarketData[]>('/v1/markets.json');   return this.http.get<BitcoinMarketData[]>('assets/fake-data/fake-
bitcoin-data.json');
}

HttpInterceptor

In case you use an external lib and have no access to the service, there is another Angular-native method to mock your requests — HttpInterceptor. Interceptors are a unique type of Angular Service that we can implement. Interceptors allow us to intercept incoming or outgoing HTTP requests using the HttpClient. By intercepting the HTTP request, we can modify or change the value of the request… or response!

Let’s assume that you have a library that provides you recommendations whether you should buy or sell your bitcoins. In FakeBackendHttpInterceptor you can check for this particular http request and alter the response. Don’t forget to provide FakeBackendHttpInterceptor in app.module.ts. Use providers: [fakeBackendProvider] for this.

@Injectable()
export class FakeBackendHttpInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler):
Observable<HttpEvent<any>> {
return this.handleRequests(req, next);
}
handleRequests(req: HttpRequest<any>, next: HttpHandler): any {
const { url, method } = req;
if (url.endsWith('/suggestion') && method === 'GET') {
return of(new HttpResponse({ status: 200, body: 'BUY'}))
.pipe(delay(500));

}
// if there is not any matches return default request.
return next.handle(req);
}
}
/**
* Mock backend provider definition for app.module.ts provider.
*/
export const fakeBackendProvider = {
provide: HTTP_INTERCEPTORS,
useClass: FakeBackendHttpInterceptor,
multi: true,
};

If this solution is what you were looking for, check out the following article — Angular HttpInterceptor Usage Like Fake Backend.

HttpInterceptor gives you more freedom regarding mocking data from external libraries, yet it brings additional limitations with it.

  • You have to implement additional Angular logic in your application.
  • And even more dramatic — what if you forget to remove fakeBackendProvider from providers in app.module.ts before pushing your code?

We can do better. Let’s have a look at my favourite solution.

JSON server

json-server is a JavaScript library for mocking REST APIs. You can install it with the following command.

npm install -g json-server

To spin a full-fledged fake backend service, you need to create a .js file for the server (e.g. server.js) and a .json file for your database (e.g. db.json). Your server should point to the .json file (jsonServer.router(‘apps/fake-backend/src/json-server-backend/db.json’)).

//server.jsconst jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('apps/fake-backend/src/json-server-backend/db.json');
const middlewares = jsonServer.defaults();
server.use(middlewares);
//server.get('/api/suggestion', (req, res) => res.status(500).jsonp({}));server.use(jsonServer.rewriter({
'/api/suggestion': '/suggestion',
'/v1/*': '/bitcoinMarketData',
})
);
// Use default router
server.use(router);
server.listen(3000, () => {
console.log('Fake-Backend Server is running');
});

Your fake database should provide mock responses for the routed requests. You will get the array referenced by bitcoinMarketData for your request to ‘/v1/markets.json’. ‘/api/suggestion’ will get “SELL” as response. Note that you can even mock error responses (cf. commented out code in server.js).

//db.json{
"suggestion": ["SELL"],
"bitcoinMarketData": [
{
"currency": "EUR",
"high": null,
"latest_trade": 1566979086,
"weighted_price": null,
"bid": 9173.0,
"volume": 0,
"ask": 9219.89,
"low": null,
"duration": 90181,
"close": 9282.49,
"avg": null,
"symbol": "btcdeEUR - fake data from JSON Server",
"currency_volume": 0
},
{...}
]
}

Once we are done with server configuration, we have to tell Angular to proxy the requests to our fake backend. To do so, you need to create a parametrised proxy configuration (e.g. proxy.conf.js at the root level).

//proxy.conf.jsvar defaultTarget = 'http://api.bitcoincharts.com/';
module.exports = [
{
context: ['/v1/**', '/api/suggestion*'],
target: process.env['FAKE_BACKEND']
? 'http://localhost:3000': defaultTarget,
secure: true,
changeOrigin: true,
},
];

Now we need to tell Angular to use this proxy configuration in angular.json.

//angular.json...
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "proxy-for-angular:build",
"proxyConfig": "proxy.conf.js",
"port": 4200
},
...

Finally, we add some additional scripts in package.json to switch between real and fake backend.

"scripts": {
...
"start": "nx serve",
"fakebackend": "node apps/fake-backend/src/json-server-
backend/server.js",
"start:fakebackend": "FAKE_BACKEND=true ng serve --ssl=true"
},

What happens here? We’ve added “fakebackend” to start our fake backend. We can now run “npm run start:fakebackend my-app” and the requests will be proxied to json server. No hardcoded values, no additional logic. Even more — GET, POST and error responses out-of-the-box! If you run “npm run start my-app” the requests will be proxied to the real backend, “restoring” the original functionality. While the only limitation of json-server is an additional dev dependency that you get, there is still something that we are missing.

As of now, I was talking about developer perspective. How about testers? None of the options above will help them to test edge cases or error handling. Of course, they could shut down the server and check it then. However, I bet it is not always possible, especially with a company-wide used backend service. In this case you might need an additional option.

Cookies

One of possible solutions would be to write a script for your middleware (e.g. APIC) that checks for specific cookies — e.g. errorapi, errorcode and errorbody. If these cookies are present in the request and the API url matches the value of errorapi cookie, it will not be propagated to the actual server. Instead, the content of errorcode and errorbody will be returned. This way the tester can manipulate the backend responses from the browser.

To sum up, you’ve got several options to mock backend responses in Angular — hardcoded values, requesting local data via HttpClient, altering responses with HttpInterceptor, installing json-server or setting cookies. The right approach depends on your needs and the needs of your fellow developers and testers. Well, enough faking, let’s make it!

Code examples from this story can be found in the following repo: https://github.com/korneevamg/fake-backend

Additional resources:

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store