From one schema, five languages.
A typed entity in metadata, side-by-side with what every port actually generates. Each output is idiomatic for that language — Drizzle + Zod for TypeScript, JPA + Spring records for Java, Exposed + KotlinPoet data classes for Kotlin, EF Core + ASP.NET records for C#, SQLAlchemy + Pydantic for Python. Same metadata; five idiomatic outputs; conformance-gated to byte-identical canonical form.
The source: one YAML file in metaobjects/
metadata.root:
package: acme
children:
- object.entity:
name: Subscriber
children:
- source.rdb: { "@table": subscribers }
- field.long: { name: id }
- field.string: { name: email, "@maxLength": 320, "@required": true }
- field.string: { name: name }
- field.enum:
name: status
"@values": [active, paused, cancelled]
"@required": true
- field.timestamp: { name: createdAt, "@autoSet": onCreate }
- identity.primary: { "@fields": [id], "@generation": increment }
TypeScript — Drizzle + Zod from codegen-ts
import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core";
import { z } from "zod";
export const subscribers = sqliteTable("subscribers", {
id: integer("id").primaryKey({ autoIncrement: true }),
email: text("email", { length: 320 }).notNull(),
name: text("name"),
status: text("status", { enum: ["active", "paused", "cancelled"] as const }).notNull(),
createdAt: text("created_at").notNull(),
});
export const SubscriberInsertSchema = z.object({
email: z.string().max(320),
name: z.string().optional(),
status: z.enum(["active", "paused", "cancelled"]),
});
export type Subscriber = typeof subscribers.$inferSelect;
export type SubscriberInsert = z.infer<typeof SubscriberInsertSchema>;
Java — Spring + JPA record from codegen-spring
package acme;
public record SubscriberDto(
Long id,
String email,
String name,
SubscriberStatus status,
java.time.Instant createdAt
) {}
public enum SubscriberStatus { ACTIVE, PAUSED, CANCELLED }
@RestController
@RequestMapping("/api/subscribers")
public class SubscriberController {
@GetMapping("/{id}")
public SubscriberDto findById(@PathVariable Long id) { }
@GetMapping
public Page<SubscriberDto> list(@RequestParam Map<String, String> filter,
Pageable pageable) { }
@PostMapping public SubscriberDto create(@RequestBody SubscriberDto dto) { }
@PutMapping("/{id}") public SubscriberDto update(...) { }
@DeleteMapping("/{id}") public void delete(@PathVariable Long id) { }
}
Kotlin — Exposed + KotlinPoet data class from codegen-kotlin
package acme
import java.time.Instant
import kotlinx.serialization.Serializable
@Serializable
public data class Subscriber(
public val id: Long? = null,
public val email: String,
public val name: String? = null,
public val status: SubscriberStatus,
public val createdAt: Instant? = null,
)
@Serializable
public enum class SubscriberStatus { ACTIVE, PAUSED, CANCELLED }
object SubscriberTable : Table("subscribers") {
val id = long("id").autoIncrement()
val email = varchar("email", 320)
val name = varchar("name", 255).nullable()
val status = enumerationByName("status", 20, SubscriberStatus::class)
val createdAt = timestamp("created_at").nullable()
override val primaryKey = PrimaryKey(id)
}
C#.NET — EF Core + ASP.NET from MetaObjects.Codegen
namespace Acme;
public partial class Subscriber {
public long Id { get; set; }
public string Email { get; set; } = null!;
public string? Name { get; set; }
public SubscriberStatus Status { get; set; }
public DateTimeOffset CreatedAt { get; set; }
}
public enum SubscriberStatus { Active, Paused, Cancelled }
public partial class AppDbContext : DbContext {
public DbSet<Subscriber> Subscribers => Set<Subscriber>();
protected override void OnModelCreating(ModelBuilder b) {
b.Entity<Subscriber>(e => {
e.HasKey(s => s.Id);
e.Property(s => s.Email).IsRequired().HasMaxLength(320);
e.Property(s => s.Status).HasConversion<string>();
});
}
}
Python — SQLAlchemy + Pydantic + FastAPI from metaobjects.codegen
from datetime import datetime
from enum import Enum
from typing import Optional
from pydantic import BaseModel, Field
from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column
from .base import Base
class SubscriberStatus(str, Enum):
ACTIVE = "active"
PAUSED = "paused"
CANCELLED = "cancelled"
class Subscriber(Base):
__tablename__ = "subscribers"
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
email: Mapped[str] = mapped_column(String(320))
name: Mapped[Optional[str]]
status: Mapped[SubscriberStatus]
created_at: Mapped[datetime]
class SubscriberInsert(BaseModel):
email: str = Field(max_length=320)
name: Optional[str] = None
status: SubscriberStatus
Plus a migration — dialect-aware (Postgres / SQLite / Cloudflare D1)
CREATE TABLE subscribers (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email VARCHAR(320) NOT NULL,
name TEXT,
status VARCHAR(20) NOT NULL CHECK (status IN ('active', 'paused', 'cancelled')),
created_at TIMESTAMP NOT NULL
);
And the three CLI commands you actually run
$ meta init
$ meta gen
$ meta migrate --db file:./dev.db --slug add-subscriber
Conformance fixtures (JSON form): fixtures/conformance/. YAML and JSON are equivalent — pick whichever your stack prefers.