Advanced Binding
This guide covers advanced techniques for customizing and optimizing the binding generation process in Wails v3.
Customizing Generated Code with Directives
Section titled “Customizing Generated Code with Directives”Injecting Custom Code
Section titled “Injecting Custom Code”The //wails:inject directive allows you to inject custom JavaScript/TypeScript code into the generated bindings:
//wails:inject console.log("Hello from Wails!");type MyService struct {}
func (s *MyService) Greet(name string) string { return "Hello, " + name}This will inject the specified code into the generated JavaScript/TypeScript file for the MyService service.
You can also use conditional injection to target specific output formats:
//wails:inject j*:console.log("Hello JS!"); // JavaScript only//wails:inject t*:console.log("Hello TS!"); // TypeScript onlyIncluding Additional Files
Section titled “Including Additional Files”The //wails:include directive allows you to include additional files with the generated bindings:
//wails:include js/*.jspackage mypackageThis directive is typically used in package documentation comments to include additional JavaScript/TypeScript files with the generated bindings.
Marking Internal Types and Methods
Section titled “Marking Internal Types and Methods”The //wails:internal directive marks a type or method as internal, preventing it from being exported to the frontend:
//wails:internaltype InternalModel struct { Field string}
//wails:internalfunc (s *MyService) InternalMethod() {}This is useful for types and methods that are only used internally by your Go code and should not be exposed to the frontend.
Ignoring Methods
Section titled “Ignoring Methods”The //wails:ignore directive completely ignores a method during binding generation:
//wails:ignorefunc (s *MyService) IgnoredMethod() {}This is similar to //wails:internal, but it completely ignores the method rather than marking it as internal.
Custom Method IDs
Section titled “Custom Method IDs”The //wails:id directive specifies a custom ID for a method, overriding the default hash-based ID:
//wails:id 42func (s *MyService) CustomIDMethod() {}This can be useful for maintaining compatibility when refactoring code.
Working with Complex Types
Section titled “Working with Complex Types”Nested Structs
Section titled “Nested Structs”The binding generator handles nested structs automatically:
type Address struct { Street string City string State string Zip string}
type Person struct { Name string Address Address}
func (s *MyService) GetPerson() Person { return Person{ Name: "John Doe", Address: Address{ Street: "123 Main St", City: "Anytown", State: "CA", Zip: "12345", }, }}The generated JavaScript/TypeScript code will include classes for both Person and Address.
Maps and Slices
Section titled “Maps and Slices”Maps and slices are also handled automatically:
type Person struct { Name string Attributes map[string]string Friends []string}
func (s *MyService) GetPerson() Person { return Person{ Name: "John Doe", Attributes: map[string]string{ "hair": "brown", "eyes": "blue", }, Friends: []string{"Jane", "Bob", "Alice"}, }}In JavaScript, maps are represented as objects and slices as arrays. In TypeScript, maps are represented as Record<K, V> and slices as T[].
Generic Types
Section titled “Generic Types”The binding generator supports generic types:
type Result[T any] struct { Data T Error string}
func (s *MyService) GetResult() Result[string] { return Result[string]{ Data: "Hello, World!", Error: "", }}The generated TypeScript code will include a generic class for Result:
export class Result<T> { "Data": T; "Error": string;
constructor(source: Partial<Result<T>> = {}) { if (!("Data" in source)) { this["Data"] = null as any; } if (!("Error" in source)) { this["Error"] = ""; }
Object.assign(this, source); }
static createFrom<T>(source: string | object = {}): Result<T> { let parsedSource = typeof source === "string" ? JSON.parse(source) : source; return new Result<T>(parsedSource as Partial<Result<T>>); }}Interfaces
Section titled “Interfaces”The binding generator can generate TypeScript interfaces instead of classes using the -i flag:
wails3 generate bindings -ts -iThis will generate TypeScript interfaces for all models:
export interface Person { Name: string; Attributes: Record<string, string>; Friends: string[];}Optimizing Binding Generation
Section titled “Optimizing Binding Generation”Using Names Instead of IDs
Section titled “Using Names Instead of IDs”By default, the binding generator uses hash-based IDs for method calls. You can use the -names flag to use names instead:
wails3 generate bindings -namesThis will generate code that uses method names instead of IDs:
export function Greet(name) { let $resultPromise = $Call.ByName("Greet", name); return $resultPromise;}This can make the generated code more readable and easier to debug, but it may be slightly less efficient.
Bundling the Runtime
Section titled “Bundling the Runtime”By default, the generated code imports the Wails runtime from the @wailsio/runtime npm package. You can use the -b flag to bundle the runtime with the generated code:
wails3 generate bindings -bThis will include the runtime code directly in the generated files, eliminating the need for the npm package.
Disabling Index Files
Section titled “Disabling Index Files”If you don’t need the index files, you can use the -noindex flag to disable their generation:
wails3 generate bindings -noindexThis can be useful if you prefer to import services and models directly from their respective files.
Real-World Examples
Section titled “Real-World Examples”Authentication Service
Section titled “Authentication Service”Here’s an example of an authentication service with custom directives:
package auth
//wails:inject console.log("Auth service initialized");type AuthService struct { // Private fields users map[string]User}
type User struct { Username string Email string Role string}
type LoginRequest struct { Username string Password string}
type LoginResponse struct { Success bool User User Token string Error string}
// Login authenticates a userfunc (s *AuthService) Login(req LoginRequest) LoginResponse { // Implementation...}
// GetCurrentUser returns the current userfunc (s *AuthService) GetCurrentUser() User { // Implementation...}
// Internal helper method//wails:internalfunc (s *AuthService) validateCredentials(username, password string) bool { // Implementation...}Data Processing Service
Section titled “Data Processing Service”Here’s an example of a data processing service with generic types:
package data
type ProcessingResult[T any] struct { Data T Error string}
type DataService struct {}
// Process processes data and returns a resultfunc (s *DataService) Process(data string) ProcessingResult[map[string]int] { // Implementation...}
// ProcessBatch processes multiple data itemsfunc (s *DataService) ProcessBatch(data []string) ProcessingResult[[]map[string]int] { // Implementation...}
// Internal helper method//wails:internalfunc (s *DataService) parseData(data string) (map[string]int, error) { // Implementation...}Conditional Code Injection
Section titled “Conditional Code Injection”Here’s an example of conditional code injection for different output formats:
//wails:inject j*:/**//wails:inject j*: * @param {string} arg//wails:inject j*: * @returns {Promise<void>}//wails:inject j*: *///wails:inject j*:export async function CustomMethod(arg) {//wails:inject t*:export async function CustomMethod(arg: string): Promise<void> {//wails:inject await InternalMethod("Hello " + arg + "!");//wails:inject }type Service struct{}This injects different code for JavaScript and TypeScript outputs, providing appropriate type annotations for each language.