Migrate a Go application from Ferret v1
Start with the v1 → v2 migration overview for the project-wide check, preview, and validation workflow.
Moving an embedded Go application to Ferret v2 has three separate parts:
- Use
ferret migratefor supported source and import changes. - Use the v2 compatibility packages temporarily where they cover your application.
- Replace the v1 compiler, runtime, and driver composition with the native v2 Engine, Plan, Session, and module APIs.
The migration command is a compatibility aid, not a general application translator. Plan to review every change and migrate application-specific composition manually.
Start with the mechanical migration
Run the compatibility check from the Go module root before changing files:
ferret migrate check --from v1 .
The check reports final collecting FOR compatibility and supported legacy
stdlib calls, including canonical replacement suggestions and manual-review
findings. Either kind of finding makes the check exit nonzero without modifying
source. See the Migrate command for the
supported mappings and conservative declaration and alias guards.
Preview the supported changes, then apply them:
ferret migrate run --dry-run .ferret migrate run --print .ferret migrate run .
The command can return an implicit final top-level FOR explicitly and migrate
supported encoding, crypto, path, immutable array and object, datetime, scalar
math, range, and zero-argument random calls. Use a CLI release containing these
rules and a runtime containing the corresponding APIs. Calls requiring manual
review remain unchanged while other safe calls can migrate. It can also rewrite
these v1 imports to their v2 compatibility
equivalents:
| Ferret v1 import | Ferret v2 compatibility import |
|---|---|
github.com/MontFerret/ferret |
github.com/MontFerret/ferret/v2/compat |
github.com/MontFerret/ferret/pkg/compiler |
github.com/MontFerret/ferret/v2/compat/compiler |
github.com/MontFerret/ferret/pkg/runtime |
github.com/MontFerret/ferret/v2/compat/runtime |
github.com/MontFerret/ferret/pkg/runtime/core |
github.com/MontFerret/ferret/v2/compat/runtime/core |
github.com/MontFerret/ferret/pkg/runtime/values |
github.com/MontFerret/ferret/v2/compat/runtime/values |
github.com/MontFerret/ferret/pkg/runtime/values/types |
github.com/MontFerret/ferret/v2/compat/runtime/values/types |
Generated Go files and imports without a documented compatibility replacement are left unchanged and reported for manual follow-up. See the Migrate command for discovery rules, transaction behavior, and all available flags.
FQL migration applies to discovered lowercase .fql files. It does not locate FQL embedded in Go string literals, so review and format embedded queries such as the example below manually.
Migrate driver imports manually
The command does not rewrite any import below github.com/MontFerret/ferret/pkg/drivers. In particular, these v1 imports have no compatibility-package replacement:
github.com/MontFerret/ferret/pkg/driversgithub.com/MontFerret/ferret/pkg/drivers/httpgithub.com/MontFerret/ferret/pkg/drivers/cdp
In native v2 code, HTML support is a module from github.com/MontFerret/contrib/modules/web/html. Replace the v1 HTTP driver with the module’s drivers/memory package. Replace the v1 CDP driver with the module’s drivers/cdp package and register it with the same HTML module.
If unsupported v1 imports remain anywhere in a module-wide migration, the command retains the v1 Ferret dependency. Remove it only after all remaining imports and application logic have been migrated.
Treat compatibility packages as temporary
The compatibility packages preserve selected v1 compiler, runtime, function, and value shapes on top of the v2 engine. They are useful for getting core-only code compiling while native composition is migrated in smaller steps.
They are not the preferred long-term embedding API. They also do not make old drivers work with v2. The compatibility compiler has no option for registering native v2 modules, so an application that calls HTML functions cannot combine its migrated compatibility compiler with the contrib HTML module. Such an application must move the engine and module composition to the native API before its HTML path can run on v2.
Use the compatibility stage to stabilize supported code, tests, and FQL changes. Then remove /compat imports rather than building new application features on them.
Compare the application architectures
The following example compiles one query, caches it, registers one host function, and runs it with a URL parameter. Static HTML is the default. Setting FERRET_CDP_ADDRESS selects the optional CDP driver without changing the query or duplicating the application.
Historical Ferret v1.0.0 code
This complete example is historical Ferret v1.0.0 code. Do not copy its APIs into a new application.
This application caches a runtime.Program, attaches drivers to each execution context, and passes values through runtime.WithParam. Program.Run returns raw JSON bytes. The application also owns and closes both driver instances.
Native Ferret v2 code
The memory driver is always registered as the default. When FERRET_CDP_ADDRESS is set, the application registers CDP as an additional named driver and passes cdp as the per-session driver parameter. The URL remains data rather than being interpolated into FQL source.
For a one-off host function, WithFunctionsRegistrar keeps registration close to engine construction. If the application exposes a related set of functions, values, services, or lifecycle hooks, implement an application module and register it with WithModules instead.
Map v1 concepts to native v2
| Ferret v1 | Native Ferret v2 | Migration note |
|---|---|---|
compiler.New() |
ferret.New(...) |
The Engine owns compiler and shared host configuration. Construction can fail, so handle its error. |
drivers.WithContext |
html.New(...) plus ferret.WithModules(...) |
Register capabilities once during Engine construction instead of attaching drivers to every caller context. |
pkg/drivers/http |
modules/web/html/drivers/memory |
Use the memory driver for static HTTP loading, parsing, and in-memory DOM operations. Its registered name is memory, not http. |
pkg/drivers/cdp |
modules/web/html/drivers/cdp |
Register CDP as the default or as an additional named HTML driver. It connects to an existing browser endpoint. |
compiler.RegisterFunction |
WithFunctionsRegistrar, WithFunctions, or an application module |
Host functions are finalized when the Engine is built. Do not mutate the function set after construction. |
runtime.WithParam |
ferret.WithParam or ferret.WithSessionParam |
Engine parameters are shared defaults. Session parameters are per-execution values and override engine defaults. |
compiler.Compile returning *runtime.Program |
engine.Compile returning *ferret.Plan |
A Plan is the reusable compiled query and owns its VM pool. |
program.Run(ctx) |
plan.NewSession(ctx) then session.Run(ctx) |
Create a separate Session for each concurrent execution and close it after use. |
Cached *runtime.Program |
Cached *ferret.Plan |
Plans are safe for concurrent use; Sessions are not. Create one Session per goroutine or request. |
Raw JSON []byte |
*ferret.Output |
Read encoded bytes from Output.Content and the selected MIME type from Output.ContentType. JSON remains the default codec. |
| Manual driver cleanup | Session, Plan, Engine, and module lifecycle | Close directly created Sessions, then Plans, then the Engine. Modules use lifecycle hooks for resources they own, and runtime-owned HTML values are closed as execution results are materialized. Resources retained by the caller remain the caller’s responsibility. |
| Driver registration and cancellation in one context | Engine composition plus caller execution context | Pass the caller context to Compile, NewSession, and Run. Modules and host functions receive a derived context that preserves cancellation and deadlines. |
See Parameters for conversion and override rules, Custom Functions for typed function registration, and Executing Ferret for the complete lifecycle and concurrency contract.
Preserve cancellation and ownership
Registration no longer travels through the execution context, but cancellation still does. Use the request, job, or process context supplied by the caller. Pass it to engine.Compile, plan.NewSession, and session.Run rather than replacing it with context.Background() inside application helpers.
Plan.NewSession observes cancellation while waiting for execution capacity. Session.Run passes the execution context to the VM, module hooks, HTML operations, and host functions. Blocking host code must observe that context while it retains control. Cancellation and deadline errors propagate to the caller.
The host owns every Engine, Plan, and Session it constructs directly. Close children before parents:
- Close each Session to return its VM and run session hooks.
- Close the reusable Plan to release its VM pool and run plan hooks.
- Close the Engine to run module and engine close hooks and release engine-scoped services.
Engine.Run is different: it owns and closes the temporary Plan and Session it creates for a one-shot execution. Use the explicit Plan and Session path when caching compiled work.
Finish the migration
Before removing the v1 dependency:
- replace every
/compatimport with its native v2 package or API - replace every v1 driver import and context registration
- run the same Plan through multiple Sessions with representative parameters
- test caller cancellation, deadlines, and cleanup on both success and failure
- check
Output.ContentTypebefore assuming a result encoding other than the JSON default
Native composition is complete when the application constructs one Engine with its functions and modules, caches reusable Plans, creates Sessions for individual executions, and no longer imports Ferret v1 or v2 compatibility packages.