Angular: when you need to saw the application, but the backend is not ready yet

    If you are involved in frontend development, then you probably know the following mise-en-scenes: the terms of the project are rapidly shrinking, your management, or the customer, or both, together want to see with their eyes the working application right now, even with fake data. At the same time, there may be a back, but it is the api layer of interaction with the front that is missing from the word at all.

    So, recently I faced such a situation, and I am developing a frontend for angular (people sitting around on chairs sluggishly patted, someone nodded understandingly).

    Now I’ll try it seriously. On the one hand, the situation is not uncommon, and many solutions can be chosen.

    Several solutions came to mind:

    1. Hardcode component-level data
    2. Hardcode data at the level of resolver services, pin them to the necessary routes
    3. Hardcode data at the data service provider level
    4. Flush api, and, according to the agreed contracts, return hardcoded data

    But any of these options seemed an inexhaustible crutch, for each of which there were significant shortcomings.

    1. The first option disappeared right away - an absolutely inconvenient unused solution, as the project develops, everything will have to be rewritten.
    2. This solution could take place, but again, the project structure and the logic of the components will be rewritten.
    3. A possible option, you can even return stub data asynchronously, simulating a server call, however, as in previous solutions to the problem, our interceptors (if they exist, but they exist) would be out of work, and it turns out that the imitation of work with backing becomes incomplete.
    4. The last option seemed quite acceptable, devoid of the problems that the previous options had, but I didn’t want to write hardcode in the backend project simply for hygienic reasons.

    As a result, another option was chosen: to raise a separate web server, which would return data according to routes and contracts, and configure a separate configuration of the assembly and execution of angular. It turned out to be easy to do both.

    To implement the mock server, express was selected .

    Let's start with it.

    We select the place where we want to write code for the msk server, for example, in the mock-server directory next to the ng project.

    Next, you need to initialize the project and add the package with express.

    npm init

    npm install --save express

    Next, add a code that will return data to us. We create the index.js file, we take the code from the first tutorial.

    const express = require("express");
    const app = express();
    app.get("/url", (req, res, next) => {
      res.json(["Tony", "Lisa", "Michael", "Ginger", "Food"]);
    });
    app.listen(3000, () => {
      console.log("Server running on port 3000");
    });
    

    Start the server

    node index.js

    Let's check using postman:



    Everything is super, the server is working. Now let's configure one of the routes, as if we are requesting data from a real api. Let's say we need a list of all the books, fill the books.json file with books

    [
      {
        "rn": 0,
        "id": "0",
        "name": "Jungle",
        "type": 0,
        "wells": 10042,
        "default": false,
        "hidden": false,
        "author": "Admin"
      },
      {
        "rn": 1,
        "id": "1",
        "name": "Main",
        "type": 1,
        "wells": 156,
        "default": true,
        "hidden": false,
        "author": "User"
      }
    ]

    And update the application file:

    const express = require("express");
    const app = express();
    app.get("/api/book/", (req, res, next) => {
      const books = require('./books');
      res.json(books);
    });
    app.listen(3000, () => {
      console.log("Server running on port 3000");
    });
    

    And check:



    Great.
    Now let's get started with the angular app.
    Add the configuration that stores the address to the back to the environments / environment * .ts files.
    environment.ts:

    export const environment = {
      production: false,
      backend: 'http://localhost:5000/'
    }
    

    environment.prod.ts

    export const environment = {
      production: true,
      backend: 'http://localhost:5000/'
    }
    

    In normal mode, and in prod and in development mode, we will search for .net core api on port 5000, as described above. Next, we describe the configuration for the temporary backing
    environment.mock.ts

    export const environment = {
      production: false,
      backend: 'http://localhost:3000/'
    }
    

    Here, as you can see, we are looking for api on port 3000, where we will run express.

    Now we need an interceptor that will direct calls to the back to the correct server, depending on the configuration.

    @Injectable()
    export class RequestInterceptor implements HttpInterceptor {
      baseUrl: string;
      constructor() {
        this.baseUrl = environment.backend;
      }
      intercept(req: HttpRequest, next: HttpHandler): Observable> {
        return next.handle(this.modifyRequest(req));
      }
      private modifyRequest = (req: HttpRequest): HttpRequest => {
        if (req.url.startsWith('api/')) {
          const url = this.baseUrl;
          req = req.clone({
            url: url + req.url
          });
        }
        return req;
      }
    }
    

    It remains to configure the new configuration for assembling and launching the application to work with the mock server.

    To do this, we need to tweak angular.json a bit.

    In the section of your project, architect / build / configurations, add a new mock build configuration, and describe the replacement of environment files for this configuration. Also for the serve mode, create a mock configuration and specify the desired build option

    {
      "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
      "version": 1,
      "newProjectRoot": "projects",
      "projects": {
        "your-project": {
          /*****/
          "architect": {
            "build": {
              /*****/
              "configurations": {
                "production": {
                  /*****/
                },
                "mock": {
                  "fileReplacements": [
                    {
                      "replace": "src/environments/environment.ts",
                      "with": "src/environments/environment.mock.ts"
                    }
                  ]
                }
              }
            },
            "serve": {
              "builder": "@angular-devkit/build-angular:dev-server",
              "options": {
                "browserTarget": "your-project:build"
              },
              "configurations": {
                "production": {
                  "browserTarget": "your-project:build:production"
                },
                "mock": {
                  "browserTarget": "your-project:build:mock"
                }
              }
            }
          },
          /*****/
        }
      }
    }
    

    That's all, now it remains to run the project in the desired configuration

    
    ng serve --configuration=mock
    

    and check where calls to the back fly away:



    Everything is fine.

    In fact, this design will still help us a lot when we tighten integration and e2e tests to the project. I will try to write about this in the near future.

    Also popular now: