Archivos de Esquema GraphQL: Diseño de APIs como Sistema de Tipos
Los archivos de esquema GraphQL (.graphql o .gql) definen el contrato de una API GraphQL: los tipos, consultas, mutaciones y suscripciones que los clientes pueden realizar. A diferencia de REST, donde la superficie de la API se dispersa entre rutas de URL y verbos HTTP, un esquema GraphQL centraliza toda la API en un único documento tipado. Saber leer, escribir y validar archivos de esquema es fundamental para desarrolladores backend, consumidores de API y autores de herramientas.
El Lenguaje de Definición de Esquemas (SDL)
Los esquemas GraphQL se escriben en el Schema Definition Language — una sintaxis declarativa y legible por humanos que describe tipos y sus relaciones.
Tipos Escalares
Los cinco escalares integrados:
Boolean # verdadero o falso
Int # entero de 32 bits con signo
Float # doble precisión IEEE 754
String # cadena UTF-8
ID # identificador único, serializado como String
Los escalares personalizados extienden estos para datos específicos del dominio:
scalar Date
scalar URL
scalar JSON
scalar UUID
Tipos Objeto
Los tipos objeto son los bloques de construcción del esquema:
type Usuario {
id: ID!
nombre: String!
email: String!
creadoEn: Date!
publicaciones: [Publicacion!]!
rol: RolUsuario!
}
El sufijo ! significa no nulo — el campo está garantizado a existir. [Publicacion!]! es una lista no nula de objetos Publicacion no nulos.
Tipos Enum
Los enums restringen un campo a un conjunto fijo de valores:
enum RolUsuario {
ADMIN
EDITOR
VISUALIZADOR
INVITADO
}
enum TipoMedia {
IMAGEN
VIDEO
AUDIO
DOCUMENTO
}
Tipos Input
Los tipos input se usan exclusivamente para argumentos de mutaciones — no pueden usarse como tipos de salida:
input CrearUsuarioInput {
nombre: String!
email: String!
contraseña: String!
rol: RolUsuario = VISUALIZADOR
}
input ActualizarPublicacionInput {
titulo: String
contenido: String
publicadoEn: Date
}
Interfaces y Unions
Las interfaces definen campos compartidos entre tipos:
interface Nodo {
id: ID!
}
interface ConMarcasDeTiempo {
creadoEn: Date!
actualizadoEn: Date!
}
type Publicacion implements Nodo & ConMarcasDeTiempo {
id: ID!
titulo: String!
contenido: String!
autor: Usuario!
creadoEn: Date!
actualizadoEn: Date!
}
Las unions permiten que un campo devuelva uno de varios tipos sin requerir campos compartidos:
union ResultadoBusqueda = Usuario | Publicacion | Comentario | Etiqueta
type Query {
buscar(consulta: String!): [ResultadoBusqueda!]!
}
Los Tipos Raíz: Query, Mutation, Subscription
Todo esquema GraphQL requiere un tipo Query. Mutation y Subscription son opcionales:
type Query {
usuario(id: ID!): Usuario
usuarios(limite: Int = 20, desplazamiento: Int = 0): [Usuario!]!
yo: Usuario
publicacion(slug: String!): Publicacion
}
type Mutation {
crearUsuario(input: CrearUsuarioInput!): Usuario!
actualizarPublicacion(id: ID!, input: ActualizarPublicacionInput!): Publicacion!
eliminarPublicacion(id: ID!): Boolean!
}
type Subscription {
publicacionPublicada(autorId: ID): Publicacion!
usuarioConectado: Usuario!
}
Directivas
Las directivas anotan elementos del esquema con comportamiento adicional:
Directivas integradas:
type Usuario {
idLegado: Int @deprecated(reason: "Usa el campo id en su lugar")
email: String!
}
Directivas personalizadas potencian características como autorización, caché, rate limiting y campos calculados:
directive @auth(requiere: RolUsuario = VISUALIZADOR) on FIELD_DEFINITION
directive @cacheControl(maxAge: Int!) on FIELD_DEFINITION | OBJECT
type Publicacion {
id: ID!
titulo: String!
datosSecretos: String @auth(requiere: ADMIN)
miniatura: URL @cacheControl(maxAge: 3600)
}
Patrones de Organización del Esquema
Esquema en Archivo Único
Proyectos pequeños y medianos mantienen todo el esquema en un archivo:
schema.graphql
Esquema Multifichero
Esquemas grandes divididos por dominio:
schema/
usuario.graphql
publicacion.graphql
media.graphql
comentario.graphql
busqueda.graphql
escalares.graphql
directivas.graphql
Herramientas como graphql-tools los fusionan en tiempo de construcción:
const typeDefs = mergeTypeDefs(loadFilesSync('schema/**/*.graphql'));
Schema-First vs. Code-First
Schema-first: Escribe el archivo .graphql y genera resolvers y tipos a partir de él.
Code-first: Escribes los resolvers en tu lenguaje de programación (TypeGraphQL para TypeScript, Strawberry para Python) y el SDL se genera automáticamente.
Ambos producen el mismo esquema en runtime — la diferencia es cuál es la fuente de verdad.
Validación e Introspección
Validación de Esquemas
GraphQL tiene reglas de validación fuertes aplicadas al arranque:
- Todos los tipos referenciados en campos deben estar definidos
- Las implementaciones de interfaz deben incluir todos los campos de la interfaz
- Los miembros de una union deben ser tipos Objeto
Herramientas de validación:
- graphql-inspector — compara esquemas y detecta cambios que rompen compatibilidad
- eslint-plugin-graphql — lint de archivos
.graphqlcontra un esquema - graphql-schema-linter — aplicación de estilo
Introspección
Cualquier servidor GraphQL expone su esquema en runtime mediante consultas de introspección:
{
__schema {
types {
name
kind
fields {
name
type { name kind }
}
}
}
}
Esto impulsa IDEs como GraphiQL y Apollo Sandbox — completan consultas automáticamente leyendo el esquema en vivo.
Generación de Código desde Esquemas GraphQL
Esquema → Tipos TypeScript: graphql-codegen lee el esquema y genera interfaces tipadas:
npx graphql-codegen --config codegen.yml
Esquema → OpenAPI/Swagger: Herramientas como graphql-to-openapi convierten SDL a documentación REST.
Esquema → Servidor mock: addMocksToSchema() de graphql-tools crea un servidor mock funcional solo con el SDL — útil para desarrollo frontend antes de que el backend esté listo.
Base de datos → Esquema: Integraciones con ORMs (Hasura, Prisma) pueden introspeccionar la base de datos y autogenerar un esquema GraphQL desde las definiciones de tablas.
Buenas Prácticas de Diseño
Usa descripciones informativas: SDL soporta descripciones entre triple comillas en tipos, campos y valores de enum:
"""
Un artículo publicado en el sistema.
Los autores pueden crear borradores que no son visibles públicamente.
"""
type Publicacion {
"""Identificador único de la publicación"""
id: ID!
"""Slug amigable para URLs usado en permalinks"""
slug: String!
}
Diseña para los clientes, no para el modelo de datos: El esquema debe reflejar lo que los clientes necesitan, no replicar la estructura de la base de datos.
Pagina las colecciones: Devuelve tipos connection en lugar de listas planas para grandes conjuntos de datos — la especificación de conexión de Relay (edges, node, cursor, pageInfo) es el estándar de facto.
Versiona mediante deprecación: Marca campos con @deprecated y una nota de migración en lugar de crear usuarioV2, usuarioV3.
Consideraciones de Seguridad
- Limitación de profundidad: Las consultas maliciosas pueden anidar profundamente. Usa
graphql-depth-limitpara limitar la profundidad. - Análisis de complejidad: Asigna pesos de coste a los campos y rechaza consultas que superen un umbral.
- Introspección en producción: Deshabilita las consultas
__schemaen producción si la API no es pública. - Autorización por campo: Comprueba permisos en cada resolver, no solo en la capa de gateway.
Los archivos de esquema GraphQL son documentos vivos. A medida que la API evoluciona, sirven como fuente única de verdad para equipos frontend y backend, habilitando herramientas potentes, seguridad de tipos y APIs autodocumentadas que escalan con el producto.
Conversiones relacionadas
Conversiones frecuentes del catálogo: