Frequently, frameworks such as Angular are used in order to facilitate development and make code easier to read and generally more accessible, for example by imposing a standard structure on projects and by placing specific code artefacts at specific locations so developers who’re unfamiliar with a project nevertheless will quickly find their way around.
The two sides of such a web application typically communicate via HTTP-based RESTful APIs provided by the server.
This clear separation between client and server side has numerous benefits, e.g.
- flexibility: The business logic implementation isn’t tied to a specific view technology.
- more dynamic user-facing behaviour instead of statically generated pages
- scalability: Server and client can be deployed independently. Moreover, the client runs on the user’s machine and therefore doesn’t necessarily or directly affect server resources.
- offline capabilities: Even if the server isn’t reachable the application can continue to run (see Progressive Web Apps (PWAs)) and resynchronise when the server becomes available again.
However, as with almost any design pattern, this separation also comes with downsides, one of which is the need to keep your client-side model in sync with its server-side counterpart. If you don’t pay close attention to this the model on the server and that on the client over time will inevitably diverge, which in turn will make your software much harder to maintain.
There are a few approaches for alleviating this problem without having to resort to doing this work manually. One of these approaches is using Swagger and / or OpenAPI for defining your API and then having Swagger Codegen generate a client-side SDK for accessing that API so you don’t have to write it yourself.
Currently though, I’m involved in creating a workflow support tool that uses Spring Data REST for providing REST resources and Spring REST Docs for documenting the API (a task that’s otherwise often delegated to Swagger, too because while Swagger is a comprehensive API toolkit its most obvious and immediate benefit is an automatically generated human-readable API documentation). As for client development Angular and TypeScript are used for this workflow tool.
So, a Swagger or OpenAPI definition to generate a TypeScript client from would be expedient.
I thought: “Spring REST Docs also provides a machine-readable API definition because it can not just be used for documenting an API.” In fact, that’s almost just a byproduct of its main purpose: Writing accessible acceptance tests for APIs that serve as both API definition and documentation.
So, I did a bit of research and asked around but couldn’t quite find what I was looking for. Puzzled, because I couldn’t believe I really was the first one to encounter this problem, it dawned on me that I was thinking too complicated: Spring Data REST already supplies metadata about the API it provides, particularly about the data types used!
However, alternatively when sending
Accept header Spring Data REST responds with metadata in a JSON Schema format.
JSON Schema in turn plays very well with Angular and TypeScript. For instance, there are libraries that allow you to dynamically create forms from data types defined with JSON Schema.
What I still needed though, was a way to not just read a JSON file – or a bunch of those – but to iterate over all the REST API endpoints provided by the server via HTTP and process the metadata supplied by each of those.
Therefore, I created an npm package called spring-data-rest-json-schema-to-typescript-definitions that solves this problem in a reusable fashion. This will hopefully allow others – including my future self – can build upon this in their Angular (or in fact any TypeScript-based) applications. Generating TypeScript declarations in this manner should help you keep your data model in sync between client and server and therefore increase overall software quality.
For further information please have a look at the spring-data-rest-json-schema-to-typescript-definitions GitHub repository and the project documentation.