>_JACK INTO YOUR DATABASE. GENERATE THE BACKEND. OWN THE GRID.
api-rest-generator parses a raw CREATE TABLE dump from MySQL, PostgreSQL, SQLite, or Microsoft SQL Server and emits a fully wired REST backend in your stack of choice. Auto-detects primary keys, foreign keys, and column types; maps SQL types to language-native ones; generates entities, controllers, DAOs / repositories, migrations, and module wiring. Two binaries ship from one crate: api-rest-generator for the full multi-target dispatch (Spring Boot Java / Kotlin / Groovy plus Rust/Loco), and loco-gen when you only want the Rust path. You feed it SQL. It feeds you a backend. No boilerplate. No hand-wiring.
Quickstart
Install from crates.io or build the two Rust binaries from source:
# install cargo install api-rest-generator # from source (Rust binaries) git clone https://github.com/MenkeTechnologies/api-rest-generator cd api-rest-generator cargo build --release # binaries land at target/release/api-rest-generator and target/release/loco-gen # original Kotlin/Gradle pipeline still runs in parallel for parity reference ./gradlew build
There are two entry points with two different operating models:
api-rest-generator— config-driven, no CLI flags. It readssrc/main/resources/config.properties(hard-coded relative path), parses the configured dump, and writes the generated tree undertarget.folder. This is the multi-target binary (Java / Kotlin / Groovy / Rust-Loco).loco-gen— flag-driven, two subcommands (new/generate). Rust/Loco only.
The JVM / config-driven path. Edit src/main/resources/config.properties, then run the binary with no arguments:
# config.properties — the only knobs the main binary reads target.folder=build/generated/ target.package=com/example/generated file.name=mysql_dump.sql target.language=java # java | kotlin | groovy | rust-loco (alias: loco) database.type=mysql # mysql | postgresql | sqlite | mssql # then, with the dump dropped under src/main/resources/: cargo run --bin api-rest-generator # or ./gradlew run for the Kotlin pipeline
The Rust/Loco path via the dedicated CLI (no config file needed):
# scaffold a fresh Loco project, generate, wire routes, run migrations
loco-gen new --ddl samples/pagila-postgresql.sql --name myapi \
--out . --db postgresql --loco-db sqlite --wire --migrate
# emit into an EXISTING Loco project (no scaffold)
loco-gen generate --ddl samples/sakila-mysql.sql --out ./myapi --db mysql --wire
cd myapi && cargo loco start # CRUD endpoints live at /api/{table_plural}/{id}
Full reference: README (section-by-section walkthrough of every config key, CLI flag, type-mapping rule, and output convention per target).
Input dialects — one card per parser path
MySQL
Standard CREATE TABLE grammar with backticked identifiers, inline PRIMARY KEY + table-level FOREIGN KEY constraints, MySQL-native types (tinyint, bigint, varchar, datetime, timestamp, bit).
PostgreSQL
pg_dump-compatible: handles CREATE TABLE + post-table ALTER TABLE ... ADD CONSTRAINT for PK/FK, PG-specific types (serial, text, boolean, numeric, real, integer).
SQLite
.dump-compatible: inline PKs, ignores PRAGMA / BEGIN / INSERT / CREATE INDEX noise, SQLite types (INTEGER, TEXT, REAL, NUMERIC, BLOB) with affinity rules.
MSSQL (SQL Server)
SSMS “Generate Scripts” output: [bracket] identifier stripping, ignored SET / GO control statements, MSSQL types (nvarchar, uniqueidentifier, money, datetime2).
Output targets — one card per backend
Java · Spring Boot + JPA
Lombok-powered (@Data, @AllArgsConstructor, @NoArgsConstructor). JPA entities with @Id, @ManyToOne, @JoinColumn. GenericDao service layer + Spring Data JPA repositories. REST controllers covering GET / POST / PUT / DELETE.
Kotlin · Spring Boot + JPA
Constructor injection, var properties with defaults, no Lombok. val / var distinguished by column nullability. Kotlin-native types (Int not Integer, Boolean not String). Same controller / DAO / repository surface as the Java target.
Groovy · Spring Boot + JPA
@Canonical annotation, field injection, no Lombok, no defaults. @Autowired wiring. Matches Java types but emits pure Groovy syntax.
Rust · Loco + Axum + SeaORM
SeaORM entities (#[derive(DeriveEntityModel)], #[sea_orm(primary_key)]). Axum-flavoured Loco controllers. Loco create_table migrations. Module aggregators wired into src/models/_entities/mod.rs and src/controllers/mod.rs. Raw-identifier escaping for Rust keywords (r#type).
4 × 4 matrix — every dialect feeds every target
| Dialect ↓ / Target → | Java | Kotlin | Groovy | Rust/Loco |
|---|---|---|---|---|
| MySQL | OK | OK | OK | OK |
| PostgreSQL | OK | OK | OK | OK |
| SQLite | OK | OK | OK | OK |
| MSSQL | OK | OK | OK | OK |
16 dialect × target combinations, each exercised by the test suite. Sample DDLs ship in samples/: chinook-sqlite.sql (15,902 lines), northwind-mssql.sql (9,350), pagila-postgresql.sql (1,842), sakila-mysql.sql (644) — a 27,738-line aggregate corpus of real-world schemas.
Type mapping — abridged
Full per-dialect tables (every SQL type → every target type) live in the README's TYPE MAPPING section. A short read across the most common cases — note that bit / boolean map to a real Boolean only on the Kotlin target; Java and Groovy emit String for those, mirroring the parser cascade in parser.rs:
| SQL | Java | Kotlin | Groovy | Rust (SeaORM) |
|---|---|---|---|---|
int / integer | Integer | Int | Integer | i32 |
bigint | Long | Long | Long | i64 |
varchar / text / nvarchar | String | String | String | String |
boolean / bit | String | Boolean | String | bool |
float / real | Float | Float | Float | f32 / f64 |
double / numeric / money | Double | Double | Double | f64 |
datetime / date (MSSQL) | LocalDate | LocalDate | LocalDate | DateTimeWithTimeZone |
timestamp / datetime2 | LocalDateTime | LocalDateTime | LocalDateTime | DateTimeWithTimeZone |
time | LocalTime | LocalTime | LocalTime | Time |
uniqueidentifier / image / xml | String | String | String | String |
blob / bytea / binary | String | String | String | Vec<u8> |
snake_case tables → PascalCase entities; column names go camelCase on the JVM, snake_case on Rust. Rust-keyword columns are escaped as raw identifiers (r#type). Primary keys default to Long (JVM) / PkAuto (Loco); foreign keys default to Integer (JVM) / i32 (Loco).
Generated REST shape (per table)
The two backend families wire slightly different route sets. The JVM Spring Boot targets prefix everything with GlobalConstants.CONTEXT_PATH (/api/v1) and add two collection-level verbs the Loco target does not:
# JVM (Spring Boot — Java / Kotlin / Groovy) — 7 routes per entity
GET /api/v1/{entity} # list all
GET /api/v1/{entity}/{id} # fetch one
POST /api/v1/{entity} # create
PUT /api/v1/{entity} # update (collection-level @PutMapping)
DELETE /api/v1/{entity}/{id} # delete one
DELETE /api/v1/{entity} # delete all (collection-level @DeleteMapping)
The Rust/Loco target wires the five id-scoped CRUD routes under a pluralized prefix (.prefix("api/{snake_table_plural}")) — no collection-level PUT or DELETE:
# Rust/Loco (Axum + SeaORM) — 5 routes per entity
GET /api/{table_plural} # list all
POST /api/{table_plural} # create (JSON body, PK omitted)
GET /api/{table_plural}/{id} # fetch one
PUT /api/{table_plural}/{id} # update
DELETE /api/{table_plural}/{id} # remove
JPA repositories on the JVM extend JpaRepository<T, Long> via Spring Data — pagination, sorting, projection — for free; the Loco target exposes its shape through SeaORM entities + Axum handlers (list / get_one / add / update / remove). With --wire, loco-gen patches src/app.rs to register every controller (idempotent // BEGIN loco-gen routes block).
Configuration — config.properties (main binary)
The api-rest-generator binary (and the Kotlin ./gradlew run pipeline) read exactly five keys from src/main/resources/config.properties. There are no command-line flags; the file is the entire interface.
| Key | Values | Default | Meaning |
|---|---|---|---|
target.folder | any path | build/generated/src/main/<lang>/ | Output directory root. When absent, falls back to a language-specific default folder. |
target.package | e.g. com/example/generated | (empty) | Java/Kotlin/Groovy package path, used as both the on-disk subtree and the package declaration. Ignored by the Rust/Loco target. |
file.name | e.g. mysql_dump.sql | (empty) | DDL source file, resolved against the resources dir (src/main/resources/). |
target.language | java / kotlin / groovy / rust-loco (alias loco) | java | Output target. Any unrecognised value (including bare rust) falls through to java. |
database.type | mysql / postgresql / sqlite / mssql | mysql | Selects the dialect-specific normalizer applied before parsing. |
Generating a dump to feed it: mysqldump --no-data (MySQL), pg_dump --schema-only (PostgreSQL), sqlite3 database.db .dump (SQLite), or SSMS “Generate Scripts” / sqlcmd (MSSQL).
loco-gen CLI reference (Rust/Loco only)
loco-gen is flag-driven with two subcommands. new scaffolds a fresh Loco project (shells out to loco new) then generates into it; generate emits into an existing project without scaffolding.
| Subcommand | What it does |
|---|---|
loco-gen new | Runs loco new -n <name> --db <loco-db> --bg none --assets none, parses the DDL, writes all generated files into the new project, and (with --wire) patches src/app.rs to register every controller. With --migrate, runs cargo loco db migrate afterward. |
loco-gen generate | Emits the generated tree into an existing Loco project (no scaffold). --wire still patches src/app.rs. Useful for re-running after schema changes. |
| Flag | Default | Applies to | Meaning |
|---|---|---|---|
--ddl FILE / -d | required | both | Path to the SQL dump. |
--name NAME / -n | required | new | Project / crate name (becomes the new directory name). |
--out DIR / -o | . (new); required for generate | both | Where to create (new) or find (generate) the project. |
--db DIALECT | mysql | both | DDL dialect: mysql / postgresql / sqlite / mssql. |
--loco-db DB | sqlite | new | Backing DB baked into the new Loco project: sqlite or postgres. |
--wire | off | both | Patch src/app.rs to register every generated controller's routes (idempotent — rewrites the same // BEGIN loco-gen routes block in place). |
--migrate | off | new | Run cargo loco db migrate after generation. |
--loco-bin | loco | new | Path to the upstream loco CLI binary. |
Prerequisite for new: the upstream Loco CLI (cargo install loco). The route-merge is surgical — existing modules like controllers::home (shipped by the Loco scaffold) are preserved, and re-running generate --wire after a schema change does not duplicate routes.
Loco target — caveats (not yet implemented)
Drawn verbatim from the README's RUST/LOCO TARGET section — the generated Loco tree is a starting point, not a finished API:
- FK relations are surfaced as plain
i32columns; the SeaORMRelationenum is generated empty. Add#[sea_orm(belongs_to = ...)]arms by hand for eager-loading. - No unique / nullable / default-value detection — every column is non-null in the migration. Tweak
ColType::X→XNull/XUniq/XWithDefault(...)as needed. - No pagination, search, or filter endpoints — only the five basic CRUD verbs.
- Composite primary keys are collapsed to a synthetic
id PkAutocolumn (Loco/SeaORM require a single PK); the original PK columns remain as regular fields.
Architecture in one screen (Rust path)
src/
├── bin/
│ ├── main.rs # api-rest-generator entrypoint — multi-target dispatch
│ └── loco_gen.rs # loco-gen entrypoint — Rust-only path
├── lib.rs # public API surface
├── parser.rs # DDL → Vec<Entity> (port of utils/Util.kt)
├── entity.rs # Entity + ColumnToField structs (port of dto/*.kt)
├── normalize.rs # name normalization (snake_case ↔ PascalCase / camelCase)
├── config.rs # config-file loading + per-language defaults
├── constants.rs # SQL-type → target-type maps (4 dialects × 4 targets)
├── globals.rs # mutable global state (compat with original Kotlin shape)
├── templates.rs # .tmpl loader + {{placeholder}} renderer (JVM targets)
└── loco.rs # Rust/Loco codegen — SeaORM entities, Axum controllers, migrations
src/main/kotlin/com/jakobmenke/bootrestgenerator/ # original Kotlin/Gradle implementation
├── Main.kt
├── dto/ # Entity.kt + ColumnToField.kt
├── utils/ # Util.kt + Configuration.kt + Globals.kt + EntityToRESTConstants.kt
└── templates/Templates.kt
samples/
├── chinook-sqlite.sql # 15,902 lines
├── northwind-mssql.sql # 9,350 lines
├── pagila-postgresql.sql # 1,842 lines
└── sakila-mysql.sql # 644 lines
tests/
├── loco_smoke.rs # Rust/Loco target end-to-end
├── loco_collision_pins.rs # snake-table collisions, duplicate emission, FK-clobber, silent-drop
├── loco_edge_cases.rs # Loco codegen edge cases on boundary DDL inputs
├── loco_keyword_escape.rs # Rust-keyword escaping in generated field names
├── normalize_units.rs # name-normalization invariants
├── parity_smoke.rs # output matches original Kotlin pipeline on shared fixtures
└── sample_ddls.rs # all 4 sample DDLs round-trip cleanly
11 production .rs files (3,956 LOC counting the 2 bin/ entrypoints) currently shadow the Kotlin codebase (9,094 LOC across 36 .kt files — 8 main + 28 test). The Kotlin path is the parity reference; the Rust port mirrors module-for-module to keep the diff readable during transition.
License & usage
MIT licensed. Generated code is yours — no runtime dependency on api-rest-generator itself, just on the target framework you picked (Spring Boot for JVM targets, Loco / Axum / SeaORM for Rust).