takeaway

This paper is a short story, mainly to explain the design idea of encapsulation network request, for the following gRPC encapsulation theory. We will introduce packaging design ideas from a simple scenario.

scenario

Implement the simplest scenario:

url method params response remarks
/test POST { name: string } { message: string } Interface function: If the input parameter is {name: ‘XXX ‘}, the return value is {message: ‘Hello, XXX ‘}

Code implementation

The traditional RESTful express + FETCH implementation is as follows:

The service side

// by express
const express = require('express');
const app = express();

app.use(express.json({type: 'application/json'}));

app.post("/test".function (req, res, next) {
  /* Body. Name is a string */
  res.send({ message: "Hello," + req.body.name });
});

app.listen(3000);
Copy the code

The client

The code implementation consists of three layers: encapsulation, declaration and invocation. For detailed analysis, see the following article. First look at the code implementation:

encapsulation

Request. Ts – Handles initialization, formatting, exceptions and other logic uniformly;

// request.ts
export const apiPost = <R>(
  url: string.data: Record<string, unknown>
): Promise<R> =>
  fetch(url, {
    method: "POST".body: JSON.stringify(data),
    headers: new Headers({
      "Content-Type": "application/json",
    }),
  })
    .then((res) = > {
      if (res.status === 200) {
        return res.json();
      }
      throw new Error(res.statusText);
    })
    .catch((error) = > {
      console.log(error);
    });
Copy the code

The statement

Services. ts – Use “wrapped” base functions to implement business functions and add parameters and return values to the TS declaration;

// services.ts
import { apiPost } from "./request";

export const sayHello = (txt: string) = > {
  return apiPost<{ message: string} > ("/test", { txt });
};
Copy the code

call

Index. ts – Uses the “declared” business function, at which point TS has played the role of checksum autoprompt.

sayHello("world").then((res) = > {
  console.log(res.message); // Hello, world
});
Copy the code

parsing

Let’s focus on the client side. The code is divided into three modules: encapsulation, declaration and call. Why? Let’s go back to front.

【 Call 】 The most important thing is to use enough convenient, to do minimalist. As you can see from the previous section, there is no extra code in this section, but you can enjoy the convenience of TS. On the extreme side, if you cut back from TS to JS in the future, none of the code here needs to be changed. let

[Declaration] The “call” part is minimalist because the “declaration” part declares all TS that need to be declared. As you can see, every function needs to declare input and output parameters, so the most important part of this section is the TS declaration. Of course, the convenience of the TS declaration here depends on the “encapsulation” part of the lower level.

The core goal of encapsulation is low-level decoupling. That is, no matter the underlying fetch, AXIos or even gRPC implementation is used, apiPost functions are exposed externally and the use method remains unchanged, which can fully ensure the robustness and extensibility of the code. In addition, as mentioned above, this part determines the convenience of the “declaration” layer, so it can be a test of design and abstraction, especially when dealing with generics of various TS types.

conclusion

In short, focus on two things: one is to achieve low-level decoupling, and the other is to achieve minimalist use. No matter how you split it, take off a few layers, as long as you can achieve “extreme ends”.

In intense competition, the winning system can go to absurd extremes in maximizing or minimizing one or more variables. — Poor Charlie’s Book