Ruhua is a front-end development student, proficient in various frameworks and JS tool chains, while Lei Juan is a Java program ape, familiar with Spring Boot, and they often cooperate in some project development. One day

The product manager

Front end separation

Ruhua is mainly responsible for the front end, such as interface, exchange, REST

API

start.aliyun.com/

@RestController
@RequestMapping("/user")
@CrossOrigin("*")
public class UserController {

    @GetMapping("/nick/{id}")
    public Mono<String> findNickById(@PathVariable("id") Integer id) {
        return Mono.just("nick: "+ 1); }}Copy the code
Copy

Lightning coil pins to the flower tell the REST API how to call. He chose axios package to tell Ray how good the HTTP client of this JS is, what browser ADAPTS to Node, supports Promise, and has a simple API, a demo, Very simple indeed.

const axios = require('axios').default;

(async () => {
    let userId = 1;
    let nick = await axios.get('/user/nick/' + userId)
            .then(function (response) {
                returnresponse.data; }) console.log(nick); }) ()Copy the code
Copy

OpenAPI 3 come

Within a few days, there were dozens of REST apis, and they weren’t working anymore. Often the URL needs to be adjusted, the parameters need to be changed, and the returned JSON object field is also adjusted. It is not clear on the nail, and what if other students join in? No documentation. They decided to use the OpenAPI 3 specification, which they could use as a reference to the standard OpenAPI documentation. This is not troublesome, of course, I will not hand-write the json specification, there is a SpringDoc-OpenAPI project, you can directly output openAPI specification file, very easy. Add a dependency to pom.xml as follows:

<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-webflux-ui</artifactId> The < version > 1.3.0 < / version > < / dependency >Copy the code
Copy

Add @operation to the corresponding Controller method and you’re done.

    @GetMapping("/nick/{id}")
    @Operation(description = "find nick by id")
    public Mono<String> findNickById(@PathVariable("id") Integer id) {
        return Mono.just("nick: " + 1);
    }

Copy the code
Copy

Code slightly adjusted, thunder volume OpenAPI specification address sent to Ruhua, http://xxxx:8080/swagger-ui.html, you can see the specification according to yourself. The interface is as follows:

Such as flowers halfway find thunder volume, is mainly perfect description, increase tag, convenient positioning, etc. In this process, ruhua is a little hurt, once the API is adjusted, thunder will nail ruhua, XXX API adjustment, you can check the new specification on the OpenAPI Console, you can adjust the code.

OpenAPI is not that simple

More and more API, already 50, spend a day to find thunder volume, discuss HTTP request convention issues. Some parameters are placed in the PATH of the URL, some in the query, some in the HTTP header, some in the GET/POST switch, some in the TEXT /plain API return. Some of them return JSON, and even though OpenAPI makes it quite clear that you’re spending a lot of time building the Config object axios requested, and the code is pretty ugly. There are config objects being built all over the place, and the parameters and return value types can’t be coded. I accidentally wrote the wrong parameter name and spent a whole morning debugging it.” You can say API changes, I have to spend a lot of time looking at those changes, and I can’t code diff,

IDE

You can generate node.js code according to OpenAPI specification, json file is there, do it yourself. You know, I can’t agree, token must be in header, consider URL-friendly, some variables in the Path of URL still makes sense, there are some security considerations, there are also performance requirements, Servlet Filter specification, etc., this is HTTP.

If you don’t want to read the OpenAPI specification, writing boring code and having to find a difference after the API is adjusted is a waste of the front end. After a morning’s discussion with Lei Volume, even with OpenAPI specifications, it is still a bit troublesome to automatically generate these fixed format codes from OpenAPI JSON, which requires the cooperation of the back end and output a lot of information to meet the requirements of code prompts and so on. And how to cooperate with webpack automation process is also troublesome, download the latest OpenAPI JSON, call scripts to generate JS code, it is not convenient to automate.

Let Spring Boot generate the NPM package

Can Spring Boot automatically generate an NPM package and then call the API provided by the package in the code? Package. json adds a dependency that is associated with the URL provided by the Spring Boot application as follows:

 "dependencies": {
    "@UserService/UserController": "http://localhost:8080/npm/@UserService/UserController"
  }
Copy the code
Copy

The next step is to decide on the service interface. Since JavaController already provides an API, why not reuse that API as a Service API, since you can’t just build HTTP requests and write axios boring code anyway? The final conclusions are as follows:

  • JavaScript code should look like this:
const userController = require("@UserService/UserController");

(async () => {
    letnick = await userController.findNickById(1); console.log(nick); }) ()Copy the code
Copy

  • TypeScript code should look like this:
import userController from "@UserService/UserController"

userController.findUserById(1).then(user => {
    console.log(user.nick)
});
Copy the code
Copy

FindNickById (id) is the best API. I don’t want to know what technology is used to implement it. In the future, URL adjustment, parameter adjustment, HTTP or HTTPS, even HTTP/2, can not let me change any code.

The technology behind Spring Boot’s NPM package generation

It’s technically perfectly fine, and it’s perfectly sound. For REST Controller, we already use @getMapping, @postMapping, @pathVariable, @requestParam, @requestBody, etc. These meta information already exists. We just need to find the corresponding Controller Spring Bean and use reflection to convert this information into JS Code.

Some technical implementation points:

  • There are three core files of NPM package: Json, index.js, and index.d.ts, package.json Needless to say, index.js is mostly generated js code for controller, whereas index.d.ts is for TypeScript.
  • Package is in tar.gz format. After package.json and index.js are generated, commons-compress is called to output TGZ format
  • Index. js will integrate axios, core a JS Class, Java Controller Method into js Class Method, and finally incorporate some axios code.
  • When dealing with JSDoc, you should be able to automatically generate corresponding tags for prompt code, and the IDE can also verify and correct errors.
  • Some missing information, call OpenAPI annotion complete, such as @operation, @apiResponse and so on.

Code in detail, how to go from Java is generated JS, types such as how to match, don’t dwell on here, there is a question to clarify that for the generated code, mainly strong process is correct, and don’t pay attention to the code can be read, the generated code is for the machine to read, if you see the protobuf generated code, no one can understand it. But I will do my best to make the code readable, since there are still some debugging issues.

Let’s take a look at the final result.

The final result

  • The code hints are perfect, the function hints and parameter hints are fine

  • Return value hints are also supported, mainly because of JSDoc’s @typedef

  • The attribute values of the parameter objects in the function can also indicate that it is not easy to make mistakes

Spring Boot generates NPM packages and calls based on service interfaces, masking all the low-level details. Instead of looking at the OpenAPI console, you can call the interface directly, regardless of whether the parameter is a path variable, a request variable, or a name in the HTTP header, let alone GET or POST. All of these details are generated directly from the Controller in the back.

Other features include OpenAPI annotation integration, OPTIONAL JS functions, default value support, and Deprecated API deprecation. TypeScrip support is also on the top of the list. In summary, simpler calls, and comprehensive reviews and code tips.

Although the project still provides OpenAPI swagger- uI.html, but it is not easy to read, the code is so simple, look at the swagger UI, although the Swagger console can do REST API testing, but how simple and fast compared to Jest.

test('findNickById', async () => {
    let nick = await userController.findNickById(1);
    console.log(nick);
});
Copy the code
Copy

The version of package.json generated is based on the point in time. After we complete the test, the command line will automatically submit it. Other students want to use it. The standard package name will do.

conclusion

Finally, this project was inspired by Vaadin’s blog two days ago. Many students are writing browser applications with TypeScript. How does TypeScrip communicate back to back?

The best way to do this, of course, is to call the API directly after import, rather than encapsulating XMLHttpRequest and providing a more convenient API, which AXIos already provides. The interface-based code is as follows:

import * as helloEndpoint from './generated/HelloEndpoint'
async makeCall() {
  const greeting = await helloEndpoint.sayHello('Vaadin');
  console.log(greeting);
}
Copy the code
Copy

The Java server code is as follows:

@Endpoint
public class HelloEndpoint {
  public String sayHello(String name) {
    return "Hello "+ name; }}Copy the code
Copy

This interface-based approach to writing code is very concise and completely mindless. Vaadin can do this mainly by generating the corresponding NPM package during compilation, such as HelloEndpoint, and providing it to TS calls.

Spring Boot NPM Package Generator Spring Boot NPM Package Generator Spring Boot NPM Package Generator Spring Boot NPM Package Generator Spring Boot NPM Package Generator Spring Boot NPM Package Generator Spring Boot NPM Package Generator

With this in mind, the NPM package of RSocket is also automatically generated. If I am a front-end student, I just want to call findNickById(ID) to implement the function, as for the underlying communication is REST API, gRPC or RSocket, whatever I do. Even with the best HTTP client encapsulation, interface conventions are not the most straightforward.

Of course, Python, Ruby and other languages can use the same mechanism, completely for the developer to block various HTTP clients, gRPC clients, RSocket clients, etc.

The final code is here: gitlab.alibaba-inc.com/leijuan/npm… We welcome your valuable suggestions.