Code Generation
A backend is mostly code that follows from its API and its database: project and build files, the HTTP layer, models, validation, error handling, the database access and its migrations. Genexy generates that part from your contracts in a fraction of a second. You and your AI write only the business logic, in stub files the generator creates once and never touches again. When a contract changes, you regenerate and the generated part follows.
Code generation is experimental. It works, but the generators, their options and the files they produce can still change between builds. Commit before you regenerate, and review the result in Git Changes.
With AI
The intended way to use the generators is to let your AI call them. Ask for a service as you normally would, for example “Create an orders service in Kotlin from contracts/orders.openapi.yaml”. Through Genexy MCP the agent:
- creates the component with the right generator (
create_component), pointing it at the OpenAPI file, and the code is generated at once, - adds database, client and security modules or ports when the service needs them, and regenerates,
- implements the business logic in the stubs the generator reports, then builds and tests the result.
TURBO
Under the Create component and Regenerate component cards, the chat shows the generator run live. While it runs, the card carries the ⚡ TURBO badge and streams every file the generator writes, the same way the agent’s own file steps appear, only hundreds of them within a second or two.
| In the card | Meaning |
|---|---|
| ⚡ TURBO / Generated / Dry run | The generator is running / finished / reported changes without writing them |
+N green | Files created |
+N blue | New stubs, the files where your code goes (rows say Created stub) |
~N | Files changed by a regeneration (rows say Accepted edits to) |
−N | Files the generator no longer produces and removed |
=N | Files that were already up to date (shown when the run is finished) |
| Footer | Number of files, the time it took, their size and how long an AI would type them |
Click a file to open it. If the run fails, the card turns red and nothing is written.
The typing estimate assumes an agent writing the same code at about 60 tokens per second, without thinking or tool round trips. It is a lower bound: an agent writing a skeleton by hand also reads, plans and fixes its own mistakes. With the generator, those tokens and that time go to the business logic instead, and the skeleton is the same every time.
Guardrails for the agent
- An agent cannot write into files the generator owns, into a component’s
.genexyfolder, or delete a generated folder. Genexy refuses the edit and tells the agent to change the contract or the component spec and regenerate. - An agent that tries to start a Go module by hand (a new
go.mod) is pointed to the generator instead. - After
create_componentthe agent gets the next steps for that generator: which stubs to implement, how to return HTTP errors, and which build and test commands to run.
Code generation and the Genexy MCP tools it uses are PRO. Without PRO, the generator, gnx and the Files actions for
generated components are not available.
How it works
A generator turns two inputs into a project:
- The component spec in
<folder>/.genexy/component.yaml: the generator, the language and framework options, the ports the service listens on, the modules it uses and the run configurations that belong to it. - The contracts the spec points to: OpenAPI files for the HTTP API a component exposes or calls, and SQL schemas for its databases. Usually they lie in the component’s folder, next to the code they describe.
The generators are built into Genexy, so generating needs no JDK, Go, Node.js, .NET or Rust installation and works on every platform, mobile included. You need the toolchain only to build and run the result, for example from a run configuration.
A run first generates everything in memory and compares it with the folder. Only then are the changes written: new and changed files, and removal of files the generator no longer produces. Files that did not change are not touched, so Git, file watchers and language servers see only real changes, and a run that fails or is cancelled leaves the folder as it was. The result is the same for the same spec and contracts, whoever runs it.
<folder>/.genexy/generated.json records which files the generator owns and their content hashes. That is how Genexy
notices an owned file edited by hand: the next run overwrites it and says so in the log, and an edited file that is no
longer generated is kept with a warning instead of being deleted.
Generators
| Generator | Id | Produces | Databases | API clients |
|---|---|---|---|---|
| Go | go-gnx-di | Go workspace with dependency injection and a chi HTTP server | PostgreSQL | not yet |
| Rust | axum | Cargo workspace on axum, tokio, sqlx and reqwest | PostgreSQL | REST |
| Spring Boot | spring-boot | Java or Kotlin service, Gradle or Maven multi-module build | PostgreSQL, MySQL, MariaDB | not yet |
| ASP.NET Core | aspnet-core | Modular C# solution for .NET 10, 9 or 8 | PostgreSQL, MySQL, MariaDB | REST |
| NestJS | nestjs | NestJS 11 service in TypeScript with validated DTOs | not yet | not yet |
| Node.js | nodejs | TypeScript service on Express, Fastify or plain node:http | not yet | not yet |
Every generator implements the HTTP API of the OpenAPI contracts a component exposes (OpenAPI 3.0 and 3.1). The pages of the generators list their options, what they read, what they write and how to build the result.
Every generator also generates everything around the API from the component spec, with the same behaviour in all of them: a web server per port, CORS, rate limits, body limits, security headers, validation options and JWT / OIDC authentication with the scopes and roles of the contract. See Ports, Middleware & Security.
Projects and components
A component is standalone: everything it needs is in its own folder, so a developer can clone one service, open it in Genexy, generate and build it, without the rest of the system. Two files make this work:
| File | What it is | Created |
|---|---|---|
.genexy/project.yaml | The project: the folder opened in Genexy, like .idea. Name, project run configurations, bookmarks, and the list of its components | when a folder is opened for the first time |
.genexy/component.yaml | A component: generator, options, ports, modules, its contracts and run configurations | when a component is created in that folder |
A project that only groups components has just project.yaml; it lists their folders, like <modules> in a Maven
parent pom. A folder that is a project and a component at once (--dir .) has both. A component deeper in the tree
has only component.yaml; opening that folder on its own adds a project.yaml there.
# .genexy/project.yaml
project:
name: shop
version: 1.0.0
components:
- backend
- webAn OpenAPI contract is the API: a component exposes it on a port, or calls it through a REST client module. A SQL schema contract describes a database in YAML: tables and their columns, keys, indexes and constraints, enums and named queries. From it the database module gets the row types, a repository per table, a method per named query, transactions and the migrations.
enums:
order_status: [NEW, PAID, SHIPPED]
tables:
orders:
columns:
id: { type: bigint, primaryKey: true, identity: true }
status: { type: order_status, nullable: false, default: "'NEW'" }
total: { type: 'numeric(12,2)', nullable: false, default: 0 }
placed_at: { type: timestamptz, nullable: false, default: now() }
queries:
byStatus:
sql: SELECT * FROM orders WHERE status = :status::order_status ORDER BY placed_at
params: { status: order_status }
returns: { many: orders }When the project is opened, Genexy scans it for component.yaml files and adds the ones it does not list yet (a
service you cloned into the project, for example). Moving a component folder in Files keeps it a component, because its
spec moves with it.
Parts
A large service can be split into parts (kind: part): an adapter, a client or a database module in its own
folder, often its own repository. A part has its own component.yaml and its own build, so it is generated and built
alone; the service lists it in parts and its build includes it. Every component owns its folder up to the folders of
its parts, so regenerating the service never touches a part. Create one with gnx create <id> --parent <service> or
create_component with parent. Parts are generated by Spring Boot so far.
# backend/.genexy/component.yaml
name: orders
type: spring-boot
parts:
- $ref: adapters/payments # backend/adapters/payments/.genexy/component.yaml, kind: partContracts
A component points to its contracts with { $ref: ... } in exposes (the APIs it serves) and in modules (its
databases and clients). The reference is one of:
| Reference | Meaning |
|---|---|
api/orders.openapi.yaml | A file, relative to the component’s folder. The contract is named after the file (Orders) |
../payments/api/openapi.yaml | A file outside the folder, for example the API of the service a client calls; read, never written |
'#/contracts/OrdersDb' | A contract of the component’s contracts section |
'#/system/contracts/Orders' | The older form: the component’s contracts first, then the project’s |
A file reference can carry name and type when the file name or its content does not tell them. The contracts
section names contracts in one place, with the document in a file or written inline:
# backend/.genexy/component.yaml
exposes:
- name: api
port: 8080
contracts:
- $ref: api/orders.openapi.yaml
- $ref: '#/contracts/Status'
modules:
- name: orders-db
type: postgresql-adapter
contract: { $ref: '#/contracts/OrdersDb' }
contracts:
OrdersDb:
type: sqlschema
spec: { $ref: db/orders.yaml }
Status: # a small API written inline
type: openapi
spec:
openapi: 3.1.0
info: { title: Status, version: 1.0.0 }
paths: {}Keep contracts you want to edit with other tools (linters, Swagger UI, client generators) in their own files; inline documents suit small APIs. When you change a table, the next regeneration writes the next migration; migrations already generated are never rewritten.
Create a component yourself
Right-click a folder in Files and choose New Generated Component….
- Choose the Generator.
- Enter the Component id (lower case letters, digits and dashes, such as
orders) and the Folder, relative to the project. - Fill in the options of that generator; each generator page lists them.
- Select the API contracts the component should implement. Without one, the component is generated without
endpoints. The Folder
.makes the open project folder itself the component. - Press Create & Generate.
The dialog runs gnx create in Genexy Shell, so you see the generator log. The same command works
by hand:
gnx create orders --type spring-boot --dir services/orders --language kotlin --build-tool maven --contract services/orders/api/orders.openapi.yaml --port 8080
gnx regenerate [component...] [--all] [--dry-run] [--force]
gnx components
gnx help--contract takes a file (relative to the project) or the name of a project contract and can be repeated.
--dir . generates into the project folder itself, --parent <id> creates a part of that component.
--no-generate only writes the spec. The options of each generator have their own flags,
listed on its page. Database, client and security modules are added to modules in the component spec, ports and their
middleware to exposes; then you regenerate.
What is generated and what is yours
The Files panel shows who owns what:
- Green: owned by the generator. These files are rewritten on every regeneration, so they open read-only; change the component spec or the contract instead. If you start typing in one anyway, Genexy asks whether to take the file over. A file you take over leaves the generation: regenerations no longer change it, and you maintain it by hand from then on. Delete it to let the generator write it again.
- Normal: stubs and your own files. The generator creates a stub once (the service implementation, your configuration, the README) and never overwrites it. This is where the business logic goes.
| Generator | Your code goes into |
|---|---|
| Go | internal/app/<contract>/<Tag>ServiceImpl.go |
| Rust | src/app/<contract>/<tag>_service_impl.rs |
| Spring Boot | app/src/main/<java|kotlin>/<package>/<contract>/<Tag>ServiceImpl |
| ASP.NET Core | app/<Contract>/<Tag>ServiceImpl.cs |
| NestJS | src/service/<tag>.service.impl.ts |
| Node.js | src/services/<Tag>ServiceImpl.ts |
There is one service per OpenAPI tag. A new stub answers every operation with 501 Not Implemented, so the service builds and runs from the first minute. Each generator also writes an architecture test (Go, Rust, Spring Boot, ASP.NET Core) that fails when your code reaches into generated internals instead of the interfaces meant for it.
Regenerate
Right-click the component folder and choose Regenerate, run gnx regenerate in Genexy Shell, or add a Genexy
Regenerate run configuration. The log lists what was added, updated and removed, and which stubs were kept.
--dry-runreports the changes without writing anything.--forcematters only for the first generation into a folder that already has files: without it the run stops instead of overwriting them.