███████╗██╗░░░░░██╗░░░░░░█████╗░
██╔════╝██║░░░░░██║░░░░░██╔══██╗
█████╗░░██║░░░░░██║░░░░░███████║
██╔══╝░░██║░░░░░██║░░░░░██╔══██║
███████╗███████╗███████╗██║░░██║
╚══════╝╚══════╝╚══════╝╚═╝░░╚═╝
ella is a schema compiler that generates Go, TypeScript, and WebAssembly code from a single, human-readable definition language.
Ella takes .ella schema files and generates type-safe client/server code for Go, TypeScript clients and type definitions, and WASM bindings. Think of it like gRPC or Protocol Buffers, but with a much simpler syntax that reads like pseudocode.
You define your models, enums, services, and errors in one place, and ella generates everything you need to call those services from Go backends, TypeScript frontends, or browser WASM modules.
go install ella.to/ella@v0.3.0# Format .ella files in place
ella fmt "./schema/src/*.ella"
# Generate Go code
ella gen schema "./schema/output.gen.go" "./schema/src/*.ella"
# Generate Go code with WASM extensions (for browser clients)
ella gen schema --allow-ext "./schema/output.gen_js.go" "./schema/src/*.ella"
# Generate TypeScript type definitions (.d.ts)
ella gen schema "./web/src/schema.d.ts" "./schema/src/*.ella"
# Generate TypeScript runtime client (.ts)
ella gen schema "./web/src/schema.ts" "./schema/src/*.ella"
# Print AST for debugging
ella gen schema --debug "./schema/output.gen.go" "./schema/src/*.ella"
# Print version
ella verThe output format is determined by the file extension of the output path:
.go— Go structs, interfaces, JSON-RPC client/server code_js.go— Go WASM bindings (use--allow-extflag).d.ts— TypeScript declarations (types/interfaces for WASM usage).ts— TypeScript runtime client (fetch JSON-RPC helper +create<Service>factories + models/enums)
Constants define fixed values. They're useful for event topic names, configuration thresholds, or anything you want shared across generated code.
const TopicUserCreated = "app.user.created"
const TopicUserDeleted = "app.user.deleted"
const MaxUploadSize = 100mb
const RequestTimeout = 30s
Size units: kb, mb, gb, tb, eb
Time units: ms, s, m, h
Enums default to integer values starting at 0. You can also give them explicit string values.
# Integer enum (values: 0, 1, 2)
enum UserStatus {
Pending
Active
Disabled
}
# String enum
enum DeviceStatus {
Init = "init"
Online = "online"
Offline = "offline"
}
Models define data structures. Fields have a name and a type, separated by a colon.
model User {
Id: string
Email: string
Name: string
Status: UserStatus
Created: timestamp
Attributes: map<string, any>
}
Models can extend other models to reuse fields:
model Device {
...User
MachineId: string
DeviceStatus: DeviceStatus
}
| Type | Description |
|---|---|
string |
Text |
bool |
Boolean |
byte |
Single byte |
int8, int16, int32, int64 |
Signed integers |
uint8, uint16, uint32, uint64 |
Unsigned integers |
float32, float64 |
Floating point |
timestamp |
Unix timestamp |
any |
Untyped (maps to interface{} / any) |
[]Type |
Array of Type |
map<K, V> |
Map with key type K and value type V |
String constants with {{ }} placeholders generate functions instead of plain values:
const TopicUserStatus = "app.user.{{userId}}.status"
This generates a function that takes userId as a parameter and returns the interpolated string.
Services define RPC methods. Each method lists its request parameters and response fields.
service UserService {
Create (email: string, name: string) => (user: User)
GetById (id: string) => (user: User)
UpdateStatus (id: string, status: UserStatus) => (user: User)
Delete (id: string)
List () => (users: []User)
}
Methods without a return clause produce no response body.
Named errors with optional HTTP status codes:
error ErrUserNotFound { Msg = "user not found" }
error ErrEmailConflict { Code = 409 Msg = "email already exists" }
These generate typed error values in Go that work with errors.Is().
The Go output includes:
- Struct types with
json:"camelCase"tags for all models - Enum types with
String(),MarshalJSON(), andUnmarshalJSON()methods - A service interface (e.g.
UserServiceHandler) withcontext.Contexton every method - A server constructor that wires up JSON-RPC method routing
- A client constructor that implements the same interface via JSON-RPC calls
- Typed error variables
For .d.ts output:
- Interface definitions for all models
- Enum types as string union types
- Service interfaces with
Promise<T>return types - Support for
AbortSignal, caching, and timeout options
For .ts output:
createFetchJsonRpc(host, options)helper compatible withella.to/jsonrpcrequest/response formatcreate<Service>(conn)factory functions that return async service clients- Runtime constants and enum values
EllaRPCErrorplus typed error guards for schema-defined errors
Example runtime client usage:
import {
createFetchJsonRpc,
createUserService,
isErrUserNotFound,
} from "./schema"
const conn = createFetchJsonRpc("https://api.example.com/rpc")
const users = createUserService(conn)
try {
const user = await users.getById("123")
console.log(user)
} catch (err) {
if (isErrUserNotFound(err)) {
console.error("user not found")
} else {
throw err
}
}The WASM output (with --allow-ext) generates Go code that:
- Creates a JavaScript-callable API object
- Wraps each service method as an async function
- Handles request/response serialization through the WASM bridge
- Supports client-side caching with configurable TTL
ella fmt normalizes your schema files by sorting declarations in a consistent order: constants, then enums, then models, then services, then errors. This keeps things tidy across a team.
ella fmt "./schema/src/*.ella"Ella includes a VS Code syntax extension in tools/syntax.
Install from this repository:
cd tools/syntax
code --install-extension ella-syntax-0.0.1.vsix --forceAfter installation, run Developer: Reload Window from the VS Code command palette.
MIT — see LICENSE for details.