docs/src/content/docs/features/bindings/advanced.mdx
import { FileTree } from "@astrojs/starlight/components";
This guide covers advanced techniques for customizing and optimizing the binding generation process in Wails v3.
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 only
The //wails:include directive allows you to include additional files with the generated bindings:
//wails:include js/*.js
package mypackage
This directive is typically used in package documentation comments to include additional JavaScript/TypeScript files with the generated bindings.
The //wails:internal directive marks a type or method as internal, preventing it from being exported to the frontend:
//wails:internal
type InternalModel struct {
Field string
}
//wails:internal
func (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.
The //wails:ignore directive completely ignores a method during binding generation:
//wails:ignore
func (s *MyService) IgnoredMethod() {}
This is similar to //wails:internal, but it completely ignores the method rather than marking it as internal.
The //wails:id directive specifies a custom ID for a method, overriding the default hash-based ID:
//wails:id 42
func (s *MyService) CustomIDMethod() {}
This can be useful for maintaining compatibility when refactoring code.
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 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[].
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>>);
}
}
The binding generator can generate TypeScript interfaces instead of classes using the -i flag:
wails3 generate bindings -ts -i
This will generate TypeScript interfaces for all models:
export interface Person {
Name: string;
Attributes: Record<string, string>;
Friends: string[];
}
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 -names
This will generate code that calls into the bound method by its fully-qualified name (<package>.<Service>.<Method>) and uses positional $0, $1, … parameter names:
export function Greet($0) {
let $resultPromise = $Call.ByName("main.GreetService.Greet", $0);
return $resultPromise;
}
This can make the generated code more readable and easier to debug, but it may be slightly less efficient.
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 -b
This will include the runtime code directly in the generated files, eliminating the need for the npm package.
If you don't need the index files, you can use the -noindex flag to disable their generation:
wails3 generate bindings -noindex
This can be useful if you prefer to import services and models directly from their respective files.
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 user
func (s *AuthService) Login(req LoginRequest) LoginResponse {
// Implementation...
}
// GetCurrentUser returns the current user
func (s *AuthService) GetCurrentUser() User {
// Implementation...
}
// Internal helper method
//wails:internal
func (s *AuthService) validateCredentials(username, password string) bool {
// Implementation...
}
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 result
func (s *DataService) Process(data string) ProcessingResult[map[string]int] {
// Implementation...
}
// ProcessBatch processes multiple data items
func (s *DataService) ProcessBatch(data []string) ProcessingResult[[]map[string]int] {
// Implementation...
}
// Internal helper method
//wails:internal
func (s *DataService) parseData(data string) (map[string]int, error) {
// Implementation...
}
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.