GraphQL es un lenguaje de consultas para APIs y un runtime server-side que devuelve exactamente los campos que cada cliente pide en una sola solicitud. La dualidad importa: el lenguaje es una especificación que cualquier cliente puede hablar; el runtime ejecuta esas consultas contra un sistema de tipos que tú defines sobre los datos que ya tienes — cualquier base de datos, cualquier servicio. No es una base de datos, y no reemplaza ni tu almacenamiento ni, necesariamente, tu API REST.
Puntos clave
| Pregunta | Respuesta |
|---|---|
| Qué es | Un lenguaje de consultas gobernado por spec + runtime de ejecución — agnóstico al almacenamiento |
| El movimiento característico | La forma de la respuesta refleja la de la query: pide campos, recibe esos campos |
| Las tres operaciones | query (lectura) · mutation (escritura) · subscription (push en tiempo real) |
| Los bloques de construcción | Schema (contrato SDL) · tipos · resolvers (funciones de obtención por campo) |
| La factura honesta | Estrategia de caché, batching del N+1, límites por costo, hardening de seguridad |
La demo característica: query y respuesta
La demo a la que converge toda explicación, porque es la idea — la respuesta es la query, rellenada:
# Solicitud # Respuesta
{ {
post(id: "8fk2") { "data": {
title "post": {
author { "title": "Hello GraphQL",
username "author": {
} "username": "ada"
comments(first: 2) { },
text "comments": [
} { "text": "Nice." },
} { "text": "Ship it." }
} ]
}
}
}
Una solicitud, tres recursos relacionados, cero campos no pedidos — el par de overfetching y underfetching retirado de un solo golpe. Llamarla desde clientes reales es HTTP plano:
// JavaScript / Node.js — query Back4app's auto-generated GraphQL API
const res = await fetch('https://parseapi.back4app.com/graphql', {
method: 'POST',
headers: {
'X-Parse-Application-Id': APP_ID,
'X-Parse-Client-Key': CLIENT_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
});
const { data } = await res.json(); // shaped exactly like the query // Flutter / Dart — query Back4app's auto-generated GraphQL API
final res = await http.post(
Uri.parse('https://parseapi.back4app.com/graphql'),
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Client-Key': clientKey,
'Content-Type': 'application/json',
},
body: jsonEncode({
'query': '{ posts(first: 20) { edges { node { title author { username } } } } }',
}),
);
final data = jsonDecode(res.body)['data']; // shaped exactly like the query // iOS / Swift — query Back4app's auto-generated GraphQL API
var request = URLRequest(url: URL(string: "https://parseapi.back4app.com/graphql")!)
request.httpMethod = "POST"
request.setValue(appId, forHTTPHeaderField: "X-Parse-Application-Id")
request.setValue(clientKey, forHTTPHeaderField: "X-Parse-Client-Key")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let query = "{ posts(first: 20) { edges { node { title author { username } } } } }"
request.httpBody = try JSONEncoder().encode(["query": query])
let (data, _) = try await URLSession.shared.data(for: request)
// data is shaped exactly like the query // Android / Kotlin — query Back4app's auto-generated GraphQL API
val body = """{ "query": "{ posts(first: 20) { edges { node { title author { username } } } } }" }"""
val request = Request.Builder()
.url("https://parseapi.back4app.com/graphql")
.addHeader("X-Parse-Application-Id", APP_ID)
.addHeader("X-Parse-Client-Key", CLIENT_KEY)
.post(body.toRequestBody("application/json".toMediaType()))
.build()
val data = client.newCall(request).execute().body?.string() // shaped like the query Schema, query, resolver: el trío que hace el trabajo
Los explicadores muestran la query; casi ninguno muestra la maquinaria detrás como un cuadro coherente. El schema es el contrato tipado, escrito en SDL:
type Post {
title: String! # ! = no nulo
author: User!
comments(first: Int): [Comment!]
}
type Query { # los puntos de entrada de lectura
post(id: ID!): Post
}
type Mutation { # los puntos de entrada de escritura
createPost(title: String!): Post!
}
Los resolvers son la otra mitad del runtime — una función por campo, cada una libre de obtener datos de donde sea:
const resolvers = {
Query: {
post: (_, { id }) => db.posts.findById(id),
},
Post: {
author: (post) => db.users.findById(post.authorId), // ¡llamada por post!
},
};
La ejecución es un pipeline: parsear la consulta, validarla contra el esquema (las operaciones inválidas mueren antes de tocar datos) y luego recorrer el selection set llamando resolvers para ensamblar el JSON espejado. Esa llamada de resolver por campo es también el precio de la flexibilidad — nota el // ¡llamada por post!, que se convierte en el problema N+1 más abajo. El nombre, que nadie explica: tus datos forman un grafo de objetos tipados, y las queries lo recorren desde los campos raíz — aunque solo por los caminos que el schema expone, no en recorridos arbitrarios como en un verdadero lenguaje de consultas de grafos.
Procedencia, en breve: creado en Facebook (hoy Meta) en 2012 para sus apps móviles, liberado como open source en 2015, gobernado desde 2018 por la GraphQL Foundation bajo la Linux Foundation, con edición vigente de la spec de octubre de 2021 y un borrador de GraphQL sobre HTTP estandarizando las convenciones de transporte.
Queries, mutations y subscriptions
Las queries leen. Las mutations escriben — y seleccionan campos sobre el resultado, así el cliente recibe el estado posterior a la escritura en el mismo round trip. Las subscriptions mantienen una conexión abierta (en la práctica, WebSockets) y empujan eventos a medida que ocurren; son la historia de tiempo real de GraphQL, con la salvedad de que cada subscription activa es estado retenido en el servidor. Las tres comparten el esquema, el sistema de tipos y el tooling — un contrato, tres tiempos verbales.
GraphQL vs. REST
| GraphQL | REST | |
|---|---|---|
| Endpoints | Uno (/graphql) | Uno por recurso |
| Forma de la respuesta | Compuesta por el cliente en cada query | Fija por endpoint |
| Over/underfetching | Resuelto en la capa HTTP | Mitigado con parámetros |
| Caché HTTP | Perdido por defecto (un solo POST) | Nativo — el superpoder |
| Tipado e introspección | Integrados al contrato | Opcionales vía OpenAPI |
| Versionado | Evolución sin versiones + @deprecated | Convenciones /v1, /v2 |
| Tiempo real | Subscriptions en la spec | Fuera de alcance |
| Su fuerte | Clientes diversos, datos anidados | CRUD de recursos, lecturas cacheables |
El argumento completo — incluido cuándo REST es sencillamente la mejor opción — vive en la entrada dedicada GraphQL vs. REST.
GraphQL en producción: los costos honestos
La sección que los explicadores comerciales suavizan. Caché: un único endpoint POST renuncia al caché HTTP y de CDN indexado por URL; el reemplazo son cachés normalizados del lado del cliente, indexados por id más __typename, y persisted queries (operaciones pre-aprobadas y hasheadas, enviadas como GET) para recuperar parte del caché de transporte. N+1: resolvers ingenuos convierten una lista de 20 posts en 1 + 20 lecturas a la base — el mismo problema que los clientes REST sufren sobre HTTP, reubicado en tu capa de resolvers y corregido ahí con loaders de batching como DataLoader. Errores: GraphQL devuelve 200 OK con un array errors — el monitoreo basado en códigos de estado queda ciego si no se le reenseña. Rate limiting: las solicitudes no son iguales cuando una query puede anidar diez relaciones; las APIs maduras miden el costo de la consulta (análisis de profundidad y complejidad), no el conteo de solicitudes. Seguridad: deshabilita la introspección en producción, impón límites de profundidad y complejidad, y mantén la autorización en la capa de negocio bajo los resolvers — el endpoint único también ciega las reglas de WAF basadas en URL, así que la validación se muda a la propia capa GraphQL.
Casos de uso comunes
- Apps móviles en redes limitadas — el caso de uso fundacional: campos exactos, bytes mínimos, menos round trips.
- Productos multi-cliente — app de reloj, app de teléfono, dashboard web, cada uno moldeando sus propias respuestas contra un único schema.
- Agregación backend-for-frontend — una capa GraphQL componiendo varios servicios internos para el consumo de las UIs.
- Frontends de evolución rápida — pantallas nuevas seleccionan campos nuevos sin esperar endpoints nuevos.
- Contratos tipados de extremo a extremo — la introspección del schema genera clientes tipados, manteniendo honestas la API y la UI en tiempo de compilación.
¿Deberías usar GraphQL? Matriz de decisión
| GraphQL justifica su maquinaria cuando… | Prefiere REST cuando… |
|---|---|
| Los clientes difieren en los datos que necesitan | Un solo tipo de cliente, pantallas estables |
| Las pantallas leen datos anidados y relacionales | Los recursos mapean limpio a endpoints |
| Agregas varias fuentes de backend | Un solo servicio es dueño de los datos |
| El ancho de banda es precioso (mobile-first) | El caché HTTP/CDN puede cargar las lecturas |
| Una plataforma genera el schema por ti | El equipo tendría que construirlo y blindarlo todo a mano |
Limitaciones y trade-offs
- La flexibilidad la paga el servidor. Consultas arbitrarias del cliente exigen que el servidor sea seguro bajo cualquier forma — batching, límites de costo y guardas de profundidad son prerrequisitos, no pulido.
- El caché se vuelve tu proyecto. Lo que HTTP le daba gratis a REST, los equipos de GraphQL lo reimplementan en cachés de cliente y persisted queries.
- La observabilidad hay que reaprenderla. Un solo endpoint, respuestas siempre-200 y timing por campo exigen tooling que entienda GraphQL.
- Las subidas de archivos y los datos binarios son incómodos — suelen delegarse a endpoints de upload separados, junto al grafo.
- La gobernanza del schema es organizacional. Un contrato compartido entre equipos necesita reglas de ownership; la federación (componer subgraphs de cada equipo en un supergraph) es la respuesta de escala — y una disciplina en sí misma.
GraphQL en Back4app
Back4app es una plataforma open-source de Backend as a Service (BaaS) que combina base de datos gestionada, APIs REST y GraphQL generadas automáticamente, autenticación, almacenamiento de archivos y funciones serverless con Cloud Code. La parte distintiva es de dónde sale el schema: define un modelo de datos y la plataforma genera la API GraphQL — tipos de objeto tipados, campos de query y mutation, connections que atraviesan relaciones como el ejemplo posts → author de arriba — sin resolvers que escribir, porque Back4app los implementa contra tu base de datos con permisos aplicados en cada solicitud. Las pestañas de código muestran toda la historia del lado del cliente: un POST a /graphql con las keys de tu app. Una consola GraphQL integrada cubre la exploración, REST sigue disponible sobre los mismos datos para lecturas amigables con el caché, y la lógica personalizada entra al esquema como funciones de Cloud Code — la sección de costos honestos de arriba se vuelve, en gran parte, factura de la plataforma, no tuya.
Preguntas frecuentes
¿Qué es GraphQL en términos simples?
Un lenguaje de consultas que permite a un cliente pedirle a una API exactamente los campos que necesita — relaciones anidadas incluidas — en una sola solicitud, más un runtime de servidor que atiende esas solicitudes desde tus fuentes de datos existentes. Se coloca delante de cualquier base de datos o servicio; no es una base de datos en sí.
¿GraphQL es mejor que REST?
Ninguno es universalmente mejor. GraphQL gana con clientes diversos, restricciones de ancho de banda y datos agregados desde varias fuentes; REST gana en caché HTTP, simplicidad y madurez de tooling para CRUD con forma de recursos. El patrón dominante en producción es pragmático: una capa GraphQL para los frontends sobre interiores REST o RPC.
¿GraphQL es una base de datos o algo como SQL?
No — es un lenguaje de API en la capa de aplicación, agnóstico al almacenamiento por diseño: los resolvers pueden leer de cualquier base de datos, de otra API o de un archivo. Y, a pesar del nombre, no es un lenguaje general de consultas de grafos como SPARQL; recorres el grafo solo por los caminos que el schema expone.
¿Qué son las queries, mutations y subscriptions?
Los tres tipos de operación. Las queries leen datos; las mutations los escriben — y devuelven el nuevo estado en el mismo round trip, así el cliente se actualiza sin una lectura adicional; las subscriptions empujan actualizaciones en tiempo real por una conexión persistente, típicamente WebSockets. Las tres se validan contra el mismo schema.
¿Qué es un schema de GraphQL?
El contrato tipado entre cliente y servidor, escrito en la Schema Definition Language: los tipos de objeto, sus campos y los puntos de entrada raíz Query, Mutation y Subscription. Cada operación entrante se valida contra él antes de ejecutarse, y el tooling lo introspecta para generar documentación y clientes tipados.
¿Qué es un resolver?
Una función del lado del servidor que obtiene el valor de un campo — desde una base de datos, otra API o cualquier lugar. El runtime recorre cada query y llama al resolver de cada campo solicitado, lo que es a la vez la flexibilidad de GraphQL y el origen de su problema N+1 cuando los resolvers de los ítems de una lista consultan por separado.
¿GraphQL solo funciona sobre HTTP POST?
Por especificación, GraphQL es agnóstico al transporte; en la práctica se sirve en un único endpoint HTTP — por convención /graphql — normalmente vía POST con cuerpo JSON, con GET permitido para queries y WebSockets llevando las subscriptions. Una especificación de GraphQL sobre HTTP estandariza hoy estas convenciones.
¿Cuándo NO deberías usar GraphQL?
CRUD simple de recursos con clientes uniformes, tráfico de lectura que el caché HTTP y de CDN podría absorber, transferencia pesada de archivos y equipos pequeños sin apetito por el batching de resolvers, los límites por costo de query y la gobernanza del schema. En esos casos, una API REST bien diseñada es menos maquinaria para el mismo resultado.