¿Por qué Go es el lenguaje ideal para API?
\n\nCuando se trata de crear API de alta carga, Go (Golang) ocupa con confianza un lugar en la cima. Su naturaleza compilada, el soporte integrado de concurrencia a través de gorutinas y su sintaxis minimalista permiten escribir microservicios rápidos y confiables. Pero, ¿qué protocolo elegir para tu próximo proyecto: el clásico REST, el flexible GraphQL o el ultrarrápido gRPC?
\n\nEn este artículo no solo enumeraremos las diferencias — escribiremos código real en Go para cada enfoque, analizaremos benchmarks y daremos criterios claros de selección. ¡Allá vamos!
\n\nAPI REST en Go: un clásico probado
\n\nREST (Transferencia de Estado Representacional) sigue siendo el estándar de facto para API públicas. Go ofrece una excelente biblioteca estándar net/http, así como frameworks populares como gin y echo.
Ejemplo de servidor REST en Go puro
\n\nEmpecemos con un servicio CRUD simple para gestionar tareas. Así es como se ve una API REST mínima en Go sin dependencias externas:
\n\npackage main\n\nimport (\n "encoding/json"\n "log"\n "net/http"\n "sync"\n)\n\ntype Task struct {\n ID int `json:"id"`\n Name string `json:"name"`\n}\n\nvar (\n tasks = []Task{{ID: 1, Name: "Aprender Go"}}\n mu sync.Mutex\n nextID = 2\n)\n\nfunc getTasks(w http.ResponseWriter, r *http.Request) {\n mu.Lock()\n defer mu.Unlock()\n w.Header().Set("Content-Type", "application/json")\n json.NewEncoder(w).Encode(tasks)\n}\n\nfunc createTask(w http.ResponseWriter, r *http.Request) {\n var task Task\n if err := json.NewDecoder(r.Body).Decode(&task); err != nil {\n http.Error(w, err.Error(), http.StatusBadRequest)\n return\n }\n mu.Lock()\n task.ID = nextID\n nextID++\n tasks = append(tasks, task)\n mu.Unlock()\n w.WriteHeader(http.StatusCreated)\n json.NewEncoder(w).Encode(task)\n}\n\nfunc main() {\n http.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) {\n switch r.Method {\n case http.MethodGet:\n getTasks(w, r)\n case http.MethodPost:\n createTask(w, r)\n default:\n http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)\n }\n })\n log.Println("API REST iniciada en :8080")\n log.Fatal(http.ListenAndServe(":8080", nil))\n}\n\n\nVentajas de REST en Go:
\n- \n
- Simplicidad y previsibilidad — cada endpoint devuelve una estructura de datos fija \n
- Excelente capacidad de almacenamiento en caché mediante cabeceras HTTP \n
- Amplio soporte de herramientas (Swagger, Postman) \n
Desventajas: Over-fetching y under-fetching de datos. Si el cliente solo necesita el nombre de la tarea, igualmente recibirá el JSON completo.
\n\nGraphQL en Go: flexibilidad en las consultas
\n\nGraphQL resuelve el problema de la redundancia de datos. El cliente decide qué campos necesita. La comunidad Go ofrece la potente biblioteca gqlgen, que genera código a partir del esquema.
Ejemplo de servidor GraphQL con gqlgen
\n\nPrimero definimos el esquema schema.graphql:
type Task {\n id: ID!\n name: String!\n}\n\ntype Query {\n tasks: [Task!]!\n}\n\ntype Mutation {\n createTask(name: String!): Task!\n}\n\n\nAhora ejecutamos la generación y escribimos los resolvers en Go:
\n\npackage main\n\nimport (\n "log"\n "net/http"\n "github.com/99designs/gqlgen/graphql/handler"\n "github.com/99designs/gqlgen/graphql/playground"\n)\n\n// Suponemos que el código generado ya existe\ntype Resolver struct {\n tasks []Task\n mu sync.Mutex\n}\n\nfunc (r *Resolver) Tasks(ctx context.Context) ([]Task, error) {\n r.mu.Lock()\n defer r.mu.Unlock()\n return r.tasks, nil\n}\n\nfunc (r *Resolver) CreateTask(ctx context.Context, name string) (*Task, error) {\n r.mu.Lock()\n defer r.mu.Unlock()\n