Migrate from NestJS to GoFr
Summary
NestJS teams moving to GoFr keep the same architectural shape — controllers, services, validation, microservices — but lose the decorator metaphor. Controllers become plain handler functions, modules become Go packages with explicit constructor wiring, DTO classes become Go structs validated via c.Bind, and the @nestjs/microservices transports map onto GoFr's built-in Pub/Sub.
Migrating with an AI assistant?
Hand https://gofr.dev/AGENTS.md to your coding assistant (Claude Code, Cursor, Codex, Aider). It contains the framework conventions, routing/binding/datasource patterns, and per-framework cheat-sheets so the assistant can translate handlers without you re-explaining GoFr.
Mental model
NestJS leans on TypeScript decorators and a runtime DI container assembled from @Module metadata. Go has neither decorators nor a Nest-style DI container, so the structure becomes more explicit:
| NestJS | GoFr |
|---|---|
@Controller('/users') + @Get(':id') | app.GET("/users/{id}", handler) |
@Body() dto: CreateUserDto | var dto CreateUser; c.Bind(&dto) |
@Param('id') | c.PathParam("id") |
@Query('q') | c.Param("q") |
@Module + provider injection | Constructor passing of dependencies |
Pipes (validation, transform) | Struct tags + a validator library |
Interceptors / Guards | GoFr middleware |
@nestjs/microservices (TCP/Redis/NATS/Kafka) | app.Subscribe("topic", handler) over Kafka, NATS, SQS, MQTT, Google Pub/Sub, Azure Event Hub |
@nestjs/swagger | Built-in Swagger UI from your openapi.json |
@nestjs/typeorm, @nestjs/mongoose | SQL auto-initialized from DB_DIALECT/DB_HOST/etc. env vars; app.AddMongo(provider) for Mongo, plus GoFr migrations |
@nestjs/schedule (@Cron) | app.AddCronJob(...) |
@nestjs/terminus health | /.well-known/health (auto) |
Side-by-side: controller ↔ handler
NestJS:
@Controller('users')
export class UsersController {
constructor(private readonly users: UsersService) {}
@Post()
async create(@Body() dto: CreateUserDto) {
return this.users.create(dto);
}
}
GoFr:
type UsersHandler struct {
Users UsersService
}
func (h *UsersHandler) Create(c *gofr.Context) (any, error) {
var dto CreateUser
if err := c.Bind(&dto); err != nil {
return nil, err
}
return h.Users.Create(c, dto)
}
func main() {
app := gofr.New()
h := &UsersHandler{Users: NewUsersService()}
app.POST("/users", h.Create)
app.Run()
}
Validation and DTOs
NestJS pairs class-validator decorators with a ValidationPipe. In GoFr, a DTO is a Go struct with JSON tags; tag-based validation is added by pairing c.Bind with go-playground/validator (or any validator of your choice).
type CreateUser struct {
Name string `json:"name" validate:"required,min=3"`
Email string `json:"email" validate:"required,email"`
}
Auto-CRUD via AddRESTHandlers
If you have a typical "Nest CRUD module" — controller + service + entity + repository — GoFr can generate the full CRUD surface for an entity with AddRESTHandlers. One method registers GET / POST / GET/{id} / PUT/{id} / DELETE/{id} against your model.
Microservices and Pub/Sub
@nestjs/microservices transports map cleanly:
| Nest transport | GoFr equivalent |
|---|---|
| Kafka | Built-in Kafka subscriber/publisher |
| NATS | Built-in NATS subscriber/publisher |
| Redis Pub/Sub | Use Redis client as datasource |
RabbitMQ (Nest's Transport.RMQ) | Not built into GoFr — use Kafka, NATS, SQS, MQTT, Google Pub/Sub, or Azure Event Hub instead, or bridge via a community driver |
| MQTT | Built-in MQTT subscriber |
Subscribe pattern:
app.Subscribe("user.created", func(c *gofr.Context) error {
var msg UserCreated
if err := c.Bind(&msg); err != nil {
return err
}
return process(c, msg)
})
gRPC
For Nest's @GrpcMethod setups, GoFr supports gRPC servers and interceptors directly — see the gRPC guide.
Configuration
@nestjs/config (.env + schema) → GoFr loads configs/.env (with environment overrides) by default. Read at runtime with app.Config.Get(key).
Observability
@nestjs/terminus, @willsoto/nestjs-prometheus, and OpenTelemetry instrumentation are typically wired by hand. GoFr ships OpenTelemetry tracing, Prometheus metrics at /metrics, structured JSON logs, /.well-known/health, and runtime log-level change.
Gradual adoption
Stand up a GoFr microservice that owns one bounded context. From the Nest side, call it via HTTP or share a Pub/Sub topic. From GoFr, call back into Nest with app.AddHTTPService("nest-api", baseURL) — circuit breaker, retries, and rate limiting are configured per service.

