
Go Kit en 2026: cómo crear una API de microservicios paso a paso
Go Kit es un conjunto de paquetes (un toolkit, no un framework) que ayuda a construir microservicios en Go. No impone una estructura rígida, pero sí promueve patrones que llevan a código más estructurado, mantenible y testable, especialmente útil a medida que las aplicaciones crecen.
En este post, vamos a explorar los componentes principales de Go Kit creando una API HTTP muy sencilla que devuelva un saludo.
¿Por qué usar Go Kit?
Antes de empezar, ¿por qué elegir Go Kit?
- Separación de Responsabilidades: Go Kit fomenta dividir la aplicación en capas claras: Transporte, Endpoint y Servicio.
- Componentes Intercambiables: Facilita cambiar detalles de implementación (como pasar de HTTP a gRPC) sin afectar la lógica de negocio.
- Middleware: Proporciona un sistema robusto de middleware para añadir funcionalidades transversales como logging, métricas, tracing, rate limiting, etc.
- Infraestructura para Microservicios: Incluye herramientas para service discovery, circuit breaking y más.
Las Capas Principales de Go Kit
Una aplicación típica en Go Kit se estructura en tres capas principales:
- Servicio (Service): Contiene la lógica de negocio pura. No sabe nada sobre HTTP, JSON, gRPC, etc. Es la capa más interna y testable.
- Endpoint: Adapta las funciones del servicio a un formato específico que Go Kit entiende. Cada método del servicio suele tener un endpoint correspondiente. Actúa como un adaptador.
- Transporte (Transport): Expone los endpoints a través de un medio específico como HTTP, gRPC, NATS, etc. Se encarga de decodificar las peticiones entrantes y codificar las respuestas salientes.
Vamos a construir nuestra API "Hola Mundo" siguiendo estas capas.
1. Definiendo el Servicio (La Lógica de Negocio)
Primero, definimos la interfaz de nuestro servicio y su implementación. Queremos un servicio que pueda saludar a un nombre dado.
Crea un archivo service.go (por ejemplo, en pkg/service/service.go):
~package service import "context" // Service define la interfaz para nuestro servicio de saludo. type Service interface { Saludar(ctx context.Context, nombre string) (string, error) } // Implementación simple del servicio. type simpleService struct{} // NewService crea una nueva instancia del servicio. func NewService() Service { return simpleService{} } // Saludar implementa la lógica de negocio. func (s simpleService) Saludar(ctx context.Context, nombre string) (string, error) { if nombre == "" { return "Hola!", nil // Saludo genérico si no hay nombre } return "Hola, " + nombre + "!", nil }
2. Creando el Endpoint
El endpoint envuelve nuestro método Saludar del servicio. Necesitamos definir las estructuras de petición y respuesta para este endpoint.
Crea un archivo endpoint.go (por ejemplo, en pkg/endpoint/endpoint.go):
~package endpoint import ( "context" "github.com/go-kit/kit/endpoint" "your_module_path/pkg/service" // Reemplaza con tu ruta de módulo ) // SaludarRequest define la estructura de la petición para el endpoint Saludar. type SaludarRequest struct { Nombre string json:"nombre" } // SaludarResponse define la estructura de la respuesta para el endpoint Saludar. type SaludarResponse struct { Saludo string json:"saludo" Err string json:"err,omitempty" // Los errores no se devuelven directamente en JSON usualmente } // MakeSaludarEndpoint crea un endpoint para el método Saludar del servicio. func MakeSaludarEndpoint(svc service.Service) endpoint.Endpoint { return func(ctx context.Context, request interface{}) (interface{}, error) { req := request.(SaludarRequest) // Type assertion saludo, err := svc.Saludar(ctx, req.Nombre) if err != nil { // En una app real, manejarías el error de forma más robusta return SaludarResponse{Saludo: saludo, Err: err.Error()}, nil } return SaludarResponse{Saludo: saludo, Err: ""}, nil } }
Nota: Asegúrate de reemplazar your_module_path con la ruta correcta de tu módulo Go.
3. Configurando el Transporte HTTP
Ahora, exponemos nuestro endpoint a través de HTTP. Necesitamos funciones para decodificar la petición HTTP entrante a nuestra SaludarRequest y codificar la SaludarResponse a una respuesta HTTP.
Crea un archivo transport_http.go (por ejemplo, en pkg/transport/http.go):
~package transport import ( "context" "encoding/json" "net/http" "github.com/go-kit/kit/endpoint" httptransport "github.com/go-kit/kit/transport/http" "your_module_path/pkg/endpoint" // Reemplaza con tu ruta de módulo ) // NewHTTPHandler crea un manejador HTTP para los endpoints del servicio. func NewHTTPHandler(ep endpoint.Endpoint) http.Handler { mux := http.NewServeMux() mux.Handle("/saludar", httptransport.NewServer( ep, decodeSaludarRequest, encodeResponse, )) // Puedes añadir más rutas aquí para otros endpoints return mux } // decodeSaludarRequest decodifica la petición HTTP a SaludarRequest. func decodeSaludarRequest(_ context.Context, r *http.Request) (interface{}, error) { var request endpoint.SaludarRequest // Intentamos decodificar el nombre desde query param o JSON body if name := r.URL.Query().Get("nombre"); name != "" { request.Nombre = name } else if err := json.NewDecoder(r.Body).Decode(&request); err != nil { // Si no hay nombre en query y falla el decode del body (o está vacío) // Devolvemos un request con nombre vacío para el saludo genérico. // En una API real, podrías devolver un error BadRequest aquí. request.Nombre = "" } return request, nil } // encodeResponse codifica la respuesta del endpoint a formato JSON para HTTP. func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error { w.Header().Set("Content-Type", "application/json; charset=utf-8") // Aquí puedes manejar códigos de status basados en la respuesta si es necesario // Por ejemplo, si response contiene un error. // if e, ok := response.(endpoint.Failer); ok && e.Failed() != nil { // encodeErrorResponse(ctx, e.Failed(), w) // Función separada para errores // return nil // } return json.NewEncoder(w).Encode(response) }
4. Uniendo Todo en main.go
Finalmente, en nuestro main.go (por ejemplo, en cmd/server/main.go), instanciamos todas las capas y arrancamos el servidor HTTP.
~package main import ( "log" "net/http" "your_module_path/pkg/service" // Reemplaza con tu ruta de módulo "your_module_path/pkg/endpoint" // Reemplaza con tu ruta de módulo "your_module_path/pkg/transport" // Reemplaza con tu ruta de módulo ) func main() { // 1. Crear el servicio svc := service.NewService() // 2. Crear el endpoint saludarEndpoint := endpoint.MakeSaludarEndpoint(svc) // 3. Crear el manejador HTTP httpHandler := transport.NewHTTPHandler(saludarEndpoint) // 4. Iniciar el servidor HTTP port := ":8080" log.Printf("Servidor escuchando en puerto %s", port) log.Fatal(http.ListenAndServe(port, httpHandler)) }
Estructura de Carpetas Sugerida
Una posible estructura para este proyecto podría ser:
cmd
saludo-api
main.go
internal
config
config.go
service
service.go
instrumentation.go
endpoint
set.go
request_response.go
middleware.go
transport
http
handler.go
encode_decode.go
go.mod
go.sum
Makefile
Dockerfile
Probando la API
- Inicializa tu módulo Go:
go mod init your_module_path - Descarga las dependencias:
go mod tidy - Ejecuta el servidor:
go run ./cmd/server/main.go - Abre tu navegador o usa
curl:curl http://localhost:8080/saludar(Devolverá{"saludo":"Hola!"})curl http://localhost:8080/saludar?nombre=Carlos(Devolverá{"saludo":"Hola, Carlos!"})curl -X POST -H "Content-Type: application/json" -d '{"nombre":"Mundo"}' http://localhost:8080/saludar(Devolverá{"saludo":"Hola, Mundo!"})
Conclusión
Hemos creado una API HTTP muy simple utilizando Go Kit, separando claramente la lógica de negocio (servicio), la adaptación (endpoint) y la comunicación (transporte). Esta estructura, aunque parece verbosa para un ejemplo tan pequeño, escala muy bien para aplicaciones más complejas y facilita la adición de funcionalidades como logging, métricas o diferentes transportes (como gRPC) de forma organizada.
Go Kit ofrece mucho más, te animo a explorar su documentación y ejemplos oficiales para aprender sobre middleware, gRPC, service discovery y otras características avanzadas.