Skip to Content
Genexy IDE Beta 0.9.1 · Download the Beta build
Genexy FeaturesCode GenerationOverview
Features · Code generation

Code Generation

PROIDEExperimental

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:

  1. creates the component with the right generator (create_component), pointing it at the OpenAPI file, and the code is generated at once,
  2. adds database, client and security modules or ports when the service needs them, and regenerates,
  3. implements the business logic in the stubs the generator reports, then builds and tests the result.
The agent asks the generator for the skeleton instead of writing it. While the generator runs, the card under Create component shows ⚡ TURBO and streams every file it writes; the counters are created files (green) and new stubs (blue), on a regeneration also modified (~) and deleted (−) files. When it is done, the footer shows how long it took and how long an AI would need to type the same code. Then the agent spends its tokens only on the business logic in the stubs.

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 cardMeaning
⚡ TURBO / Generated / Dry runThe generator is running / finished / reported changes without writing them
+N greenFiles created
+N blueNew stubs, the files where your code goes (rows say Created stub)
~NFiles changed by a regeneration (rows say Accepted edits to)
−NFiles the generator no longer produces and removed
=NFiles that were already up to date (shown when the run is finished)
FooterNumber 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 same card after the run. The footer compares the run with an AI agent typing the same code at about 60 tokens per second, without thinking or tool round trips, so the real saving is larger.

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 .genexy folder, 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_component the 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

GeneratorIdProducesDatabasesAPI clients
Gogo-gnx-diGo workspace with dependency injection and a chi HTTP serverPostgreSQLnot yet
RustaxumCargo workspace on axum, tokio, sqlx and reqwestPostgreSQLREST
Spring Bootspring-bootJava or Kotlin service, Gradle or Maven multi-module buildPostgreSQL, MySQL, MariaDBnot yet
ASP.NET Coreaspnet-coreModular C# solution for .NET 10, 9 or 8PostgreSQL, MySQL, MariaDBREST
NestJSnestjsNestJS 11 service in TypeScript with validated DTOsnot yetnot yet
Node.jsnodejsTypeScript service on Express, Fastify or plain node:httpnot yetnot 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:

FileWhat it isCreated
.genexy/project.yamlThe project: the folder opened in Genexy, like .idea. Name, project run configurations, bookmarks, and the list of its componentswhen a folder is opened for the first time
.genexy/component.yamlA component: generator, options, ports, modules, its contracts and run configurationswhen 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 - web

An 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: part

Contracts

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:

ReferenceMeaning
api/orders.openapi.yamlA file, relative to the component’s folder. The contract is named after the file (Orders)
../payments/api/openapi.yamlA 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….

New generated component: choose the generator, name the component and its folder, set the few options that generator has, and pick the OpenAPI contracts it should implement. Create & Generate runs the generator in Genexy Shell, so you see every file it writes.
  1. Choose the Generator.
  2. Enter the Component id (lower case letters, digits and dashes, such as orders) and the Folder, relative to the project.
  3. Fill in the options of that generator; each generator page lists them.
  4. 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.
  5. 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

Green means the generator owns it: those files are rewritten on every regeneration and open read-only; typing in one asks whether to take it over and maintain it by hand. The stubs, here OrdersServiceImpl.kt, config/application.yml and README.md, are yours and are never overwritten. The log on the right is what Create & Generate printed in Genexy Shell.

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.
GeneratorYour code goes into
Gointernal/app/<contract>/<Tag>ServiceImpl.go
Rustsrc/app/<contract>/<tag>_service_impl.rs
Spring Bootapp/src/main/<java|kotlin>/<package>/<contract>/<Tag>ServiceImpl
ASP.NET Coreapp/<Contract>/<Tag>ServiceImpl.cs
NestJSsrc/service/<tag>.service.impl.ts
Node.jssrc/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-run reports the changes without writing anything.
  • --force matters only for the first generation into a folder that already has files: without it the run stops instead of overwriting them.