TypeScript Generator
@BrowserCallable
or @Endpoint
.- Features
- Generator Architecture
- Examples
- Appendix: How a TypeScript class is generated from the OpenAPI specification
The TypeScript generator produces TypeScript files that map to Java services annotated with @BrowserCallable
or @Endpoint
. These files retain the properties and types of the Java classes to provide type safety in the frontend.
These annotated services are commonly named endpoints, although there is a single REST endpoint in Hilla, which in turn calls the browser-callable services. The term "endpoints" is used in this context to refer to those annotated Java services.
Features
The generator has support for multi-module projects. You can use standard Maven modules in your application, or even external dependencies, as there is no need for endpoints and entity classes to be in the same project as the Hilla application.
The generator is designed to be flexible. All parts of the generator are pluggable, which allows you to alter the default behavior or add a new one.
Note
|
Spring Boot enables a Java compiler option to retain parameter names in class files. If generated classes are missing parameter names, you might need to enable this option in your project. Use the javac -parameters option to enable support for multi-module projects and all JVM languages. See Configuration for details.
|
Generator Architecture
The generator consists of three parts:
- Java bytecode parser
-
The parser reads the Java bytecode and generates an OpenAPI scheme.
- TypeScript Abstract Syntax Tree (AST) generator
-
The AST generator reads the OpenAPI scheme and generates TypeScript endpoints that could be used in further frontend development.
- Runtime controller
-
The runtime controller provides runtime communication between the server and the client.
Hilla uses the OpenAPI Specification as a middle layer between endpoints and TypeScript endpoint clients. The implementation is based on OpenAPI specification 3.0. For details, see the appendix at the end of this page.
Examples
Generated TypeScript Endpoint
The UserEndpoint.ts
class is generated from the UserEndpoint.java
class.
/**
* User endpoint.
*
* This module has been generated from UserEndpoint.java
* @module UserEndpoint
*/
import client from './connect-client.default'; 1
/**
* Check if a user is admin or not.
*
* @param id User id to be checked
* Return Return true if the given user is an admin, otherwise false.
*/
export async function isAdmin( 2
id?: number
) {
return await client.call('UserEndpoint', 'isAdmin', {id});
}
-
This line is a static part of any generated TypeScript class.
connect-client.default.ts
is another generated file, which includes default configurations for theConnectClient
and exports its instance asclient
. -
Each method in the generated TypeScript class corresponds to a Java method in the
@Endpoint
-annotated class.
For more information about type mapping between Java and TypeScript, see Type conversion. You may also want to learn about Type nullability.
Appendix: How a TypeScript class is generated from the OpenAPI specification
Modules / Classes
The generator collects all the tags
fields of all operations in the OpenAPI document. Each tag generates a corresponding TypeScript file. The tag name is used for TypeScript module/class name, as well as the file name.
Methods
Each exported method in a module corresponds to a POST operation of a path item in paths object.
Note
|
The generator only supports the POST operation. If a path item contains operations other than POST , the generator stops processing.
|
The path must start with /
, as described in Patterned Fields. It’s parsed as /<endpoint name>/<method name>
, which is used as a parameter to call to Java endpoints in the backend. The method name from the path is also reused as the method name in the generated TypeScript file.
Method Parameters
The parameters of the method are taken from the application/json
content of the request body object.
To get the result as UserEndpoint.ts
, the request body content should be:
{
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "number"
}
}
}
}
}
}
Note
|
All the other content types of the request body object are ignored by the Hilla generator. This means that a method that doesn’t have the |
Method Return Type
The return type is taken from the 200
response object. As with the request body object, the generator is only interested in the application/json
content type.
Here’s an example of a response object:
{
"200": {
"description": "Return true if the given user is an admin, otherwise false.",
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
}
Note
|
Currently, the generator only recognizes |
{
"tags": ["UserEndpoint"], 1
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"id": {
"type": "number"
}
}
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
}
}
-
As mentioned in the operation object specification, in the Hilla generator,
tags
are used to classify operations into TypeScript files. This means that each tag has a corresponding generated TypeScript file. Operations that contain more than one tag appear in all the generated files. Operations with empty tags are placed in theDefault.ts
file.
Note
| Although multiple tags don’t break the generator, it might be confusing at development time if there are two identical methods in different TypeScript files. It’s recommended to have only one tag per operation. |
Here is an example OpenAPI document that could generate previous UserEndpoint.ts
:
{
"openapi" : "3.0.1",
"info" : {
"title" : "My example application",
"version" : "1.0.0"
},
"servers" : [ {
"url" : "https://myhost.com/myendpoint"
} ],
"tags" : [ {
"name" : "UserEndpoint"
} ],
"paths" : {
"/UserEndpoint/isAdmin" : {
"post": {
"tags": ["UserEndpoint"],
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [ "id" ],
"properties": {
"id": {
"type": "number"
}
}
}
}
}
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "boolean"
}
}
}
}
}
}
}
}
}