Migration Overview
How to move an existing API onto Ginboot — an incremental strategy for Go codebases, and a porting strategy for APIs written in Node.js, Python, Java, .NET, Ruby or PHP.
This section is about moving an API you already have onto Ginboot. It covers two different problems that people often confuse:
Migrating an existing Go app
Gin, net/http, Echo, Fiber, Chi or gorilla/mux. Incremental: the app keeps running the whole way.
Porting from another language
Express, NestJS, FastAPI, Django, Spring Boot, ASP.NET, Rails or Laravel. Contract first, then a cutover.
Playbook for coding agents
The same two migrations as a deterministic procedure, with the exact API surface and verification commands.
Getting Started
If there is nothing to migrate yet, start here instead.
The one thing that decides the strategy
Ginboot is Gin underneath, and it does not hide it. server.Engine() returns the
plain *gin.Engine, and a handler with Gin's own func(c *gin.Context) signature can be
registered on a Ginboot route group unchanged.
That single fact splits the work in two:
| Your API today | Strategy | Downtime | Guide |
|---|---|---|---|
| Go + Gin | Incremental. Swap the entrypoint, keep every route, convert one controller at a time. | None | From Go |
| Go + another router | Incremental, with rewritten handlers. Routing and middleware are rewritten; services, models and tests are kept. | None | From Go |
| Another language | Port + cutover. New service, same contract, traffic moved path by path. | None, if you run both | From other languages |
Do not start with a rewrite
The most common way a migration fails is starting a new project, porting everything, and trying to switch in one step. Both guides here are built so that the application is running and serving traffic at the end of every step, not just the last one.
What Ginboot replaces, and what it leaves alone
Migrating is mostly deleting code you wrote to fill gaps the framework now fills.
Prop
Type
The phases, in the order that keeps you safe
Both guides follow the same five phases. Do them in this order — each one is a verifiable checkpoint, and skipping ahead is what turns a migration into a rewrite.
Take over the entrypoint
ginboot.New() becomes the thing that owns the process. Existing routes are mounted
as-is on server.Engine(). Nothing else changes yet.
Verify: the app builds, starts, and every existing endpoint answers exactly as before.
Move configuration in
Introduce ginboot.yml and read settings through server.Config() instead of scattered
os.Getenv calls. Keep the environment variable names you already deploy with — the
${VAR:default} syntax exists so the values still come from the platform.
Verify: the app starts with your real environment, and with an empty one falls back to the documented defaults.
Convert one endpoint group at a time
Pick the least critical resource. Turn it into a controller with Register(group *ginboot.ControllerGroup),
convert its handlers to (T, error), and register it with server.RegisterController.
Ship it. Then take the next one.
Verify: the converted routes return byte-identical responses. A response diff test against the old implementation is worth writing once and running for every group.
Move the data access layer
Replace hand-written CRUD with a repository. This is where most of the deleted lines come from, and it is deliberately after the routing work: a repository swap under a converted controller is a small, contained change.
Verify: the repository's own tests pass, then the endpoint tests still pass.
Turn on the platform features
Telemetry, caching, the OpenAPI endpoint, background workers, queue consumers, Lambda. These are additive and independent — take them in whatever order your operations need.
Verify: /healthz answers, traces arrive at your collector, and the exported
OpenAPI spec matches the contract your clients hold.
Definition of done
A migration is finished when all of these are true. Until then it is in progress, which is a fine state to be in — but it is not finished.
go build ./...andgo test ./...pass.- Every route is registered inside a controller, not on
server.Engine()directly. - No handler writes a response body and returns a value; it does one or the other.
- Every error path returns a
ginboot.ApiErrorwith the status code the old API used. - Configuration comes from
ginboot.ymland the environment, never from a literal in code. - The exported OpenAPI spec matches the contract the clients were built against.
- The old routing, binding, error-formatting and config-loading code is deleted, not left behind unused.
For AI coding agents
This documentation is published at ginboot.com in an agent-readable form:
- /llms.txt — an index of every documentation page.
- /llms-full.txt — the full text of the entire documentation in one request.
/llms.mdx/docs/<path>/content.md— the Markdown source of any single page. For this page:/llms.mdx/docs/5-migration/content.md.
If you are an agent performing a migration, read the Migration Playbook for Coding Agents first. It states the exact API surface, the step order, the verification command after each step, and the mistakes that are most often made from stale training data.