Docs

Documentation versions (currently viewingVaadin 24)

TypeScript Generator

The TypeScript generator produces TypeScript files that map to Java services annotated with @BrowserCallable or @Endpoint.

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});
}
  1. This line is a static part of any generated TypeScript class. connect-client.default.ts is another generated file, which includes default configurations for the ConnectClient and exports its instance as client.

  2. 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 application/json content type is considered to be one with no parameters.

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 200 response objects. Other response objects are ignored.

{
  "tags": ["UserEndpoint"], 1
  "requestBody": {
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "id": {
              "type": "number"
            }
          }
        }
      }
    }
  },
  "responses": {
    "200": {
      "content": {
        "application/json": {
          "schema": {
            "type": "boolean"
          }
        }
      }
    }
  }
}
  1. 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 the Default.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"
                }
              }
            }
          }
        }
      }
    }
  }
}