Overview
Explains the background and principles of eliminating the bottleneck of writing the same API contracts and types twice while developing both backend and frontend, and changing repetitive development tasks like router, handler, and MongoDB field strings to source-based generation.
Origin story
I work on both backend and frontend. When creating a feature, I had to write endpoints and request/response DTOs in Java WebFlux, and then write the same URL and TypeScript types on the frontend. To match the Swagger documentation directly, I repeatedly transferred information already existing in the backend to another format.
The biggest bottleneck was rewriting the API contracts created in the backend into a form usable by the frontend. If any of the URL, HTTP method, path/query parameters, or request/response structures were modified differently on either side, problems were discovered later than compilation. Beyond just being a hassle, the discrepancies arising from managing the same content twice by humans were a bigger issue.
I also wanted to reduce the repetition when creating new RouterFunctions and handlers. If a reference like ApiAccountHandler::search was added to the router, I thought it should automatically create at least the skeleton of a non-existent handler class and method. Writing entity field names like "username" as strings when creating MongoDB queries was also cumbersome and prone to typos, so I included a feature to read entity sources and generate Java enums within the same tool.
Thus, Swagger/OpenAPI generation, handler skeleton generation, and Mongo entity field enum generation were created first, and later added AsyncAPI generation while using RSocket. Although each seems like a separate idea, the starting point is the same. It is about not having humans rewrite facts already noted in the backend source, and letting development tools create parts that can be read by machines.
Development philosophy
It is based on the backend source.
Uses existing information in RouterFunction, handler, DTO, and entity as the source for API documentation and generated code. The key is not to manage the same contract in a separate file.
Choose development-time automation over runtime magic.
It analyzes the source in the local development environment rather than being a framework that intercepts production requests, generating actual files. The results can be visually confirmed and version controlled.
Follows project conventions predictably.
Does not aim for a universal compiler that understands all Java code. Prioritizes analyzing the WebFlux functional endpoint structure I actually use with clear rules.
Eliminates repetitive connection segments.
Creates Swagger from backend endpoints, and connects the flow where the frontend generator reads that document to create services and types into a single automation chain.
What to read from the backend
RouterFunction source
Reads the HTTP method, nested path, handler method reference, and predicate.
Handler source
Reads the request body, query/path values, and response publisher type.
Request / Response DTO
Links the already defined Java type in the backend to the OpenAPI schema.
Mongo entity source
Reads the Java field and the storage raw name of @Field, and the @Document collection.
RSocket controller
Reads the @MessageMapping route and request/response payload type.
What to create instead of repetitive writing
swagger.json
Creates a REST endpoint as an API contract for the front service and TypeScript type generator.
asyncapi-rsocket.json
Creates a contract for the RSocket route and payload that the front RSocket client generator can read.
Handler source
Generates and corrects the class and method skeleton based on the handler reference written in RouterFunction.
{Entity}Fields enum
Creates an enum for the entity's Java field and storage raw name to avoid repeating string field names.
CollectionNames enum
Generates collection names declared in @Document for use instead of strings.
Automation flow in the current project
• Writes RouterFunction, handler, and Java request/response DTO in the backend.
• The local profile's watcher detects source changes and updates swagger.json or asyncapi-rsocket.json.
• The front's @byeolnaerim/typed-rx-http generation script reads the documentation to generate service functions and TypeScript types.
• In the screen code, imports the generated functions without rewriting the URL and response type.
• Updates the field enum and collection enum used in queries when the entity changes.