academia/docs/entregables/02-diseno/esquema-graphql/DI-004-esquema-graphql.md

352 lines
8.7 KiB
Markdown

# DI-004: Esquema GraphQL
**Proyecto:** Sistema de Registro de Estudiantes
**Fecha:** 2026-01-07
---
## 1. Schema Completo
```graphql
# ═══════════════════════════════════════════════════════════════
# TYPES
# ═══════════════════════════════════════════════════════════════
type Student {
id: Int!
name: String!
email: String!
totalCredits: Int!
isActivated: Boolean!
activationExpiresAt: DateTime
enrollments: [Enrollment!]!
}
type User {
id: Int!
username: String!
role: String! # "Admin" | "Student"
studentId: Int
studentName: String
}
type Subject {
id: Int!
name: String!
credits: Int!
professor: Professor!
enrolledStudents: [Student!]!
}
type Professor {
id: Int!
name: String!
subjects: [Subject!]!
}
type Enrollment {
id: Int!
student: Student!
subject: Subject!
enrolledAt: DateTime!
}
type AvailableSubject {
subject: Subject!
isAvailable: Boolean!
unavailableReason: String
}
type Classmate {
subjectName: String!
students: [String!]!
}
# ═══════════════════════════════════════════════════════════════
# QUERIES
# ═══════════════════════════════════════════════════════════════
type Query {
# Autenticación
me: User # Usuario autenticado actual
validateActivationCode(code: String!): ActivationValidation!
# Estudiantes
students: [Student!]!
student(id: Int!): Student
# Materias
subjects: [Subject!]!
subject(id: Int!): Subject
availableSubjects(studentId: Int!): [AvailableSubject!]!
# Profesores
professors: [Professor!]!
professor(id: Int!): Professor
# Compañeros de clase
classmates(studentId: Int!): [Classmate!]!
}
type ActivationValidation {
isValid: Boolean!
studentName: String
error: String
}
# ═══════════════════════════════════════════════════════════════
# MUTATIONS
# ═══════════════════════════════════════════════════════════════
type Mutation {
# Autenticación
login(input: LoginInput!): AuthPayload!
activateAccount(input: ActivateAccountInput!): AuthPayload!
resetPassword(input: ResetPasswordInput!): ResetPayload!
# Estudiantes (Admin crea con código de activación)
createStudent(input: CreateStudentInput!): CreateStudentPayload!
updateStudent(id: Int!, input: UpdateStudentInput!): StudentPayload!
deleteStudent(id: Int!): DeletePayload!
regenerateActivationCode(studentId: Int!): ActivationCodePayload!
# Inscripciones
enrollStudent(input: EnrollInput!): EnrollmentPayload!
unenrollStudent(enrollmentId: Int!): DeletePayload!
}
# ═══════════════════════════════════════════════════════════════
# INPUTS
# ═══════════════════════════════════════════════════════════════
# Autenticación
input LoginInput {
username: String!
password: String!
}
input ActivateAccountInput {
activationCode: String!
username: String!
password: String!
}
input ResetPasswordInput {
username: String!
recoveryCode: String!
newPassword: String!
}
# Estudiantes
input CreateStudentInput {
name: String!
email: String!
}
input UpdateStudentInput {
name: String
email: String
}
input EnrollInput {
studentId: Int!
subjectId: Int!
}
# ═══════════════════════════════════════════════════════════════
# PAYLOADS (Union para errores)
# ═══════════════════════════════════════════════════════════════
# Autenticación
type AuthPayload {
success: Boolean!
token: String
recoveryCode: String # Solo en activación (se muestra una vez)
user: User
error: String
}
type ResetPayload {
success: Boolean!
error: String
}
# Estudiantes
type CreateStudentPayload {
student: Student
activationCode: String # Código para activar cuenta
activationUrl: String # URL completa de activación
expiresAt: DateTime # Cuándo expira el código
errors: [String!]
}
type ActivationCodePayload {
activationCode: String!
activationUrl: String!
expiresAt: DateTime!
}
type StudentPayload {
student: Student
errors: [String!]
}
type EnrollmentPayload {
enrollment: Enrollment
errors: [String!]
}
type DeletePayload {
success: Boolean!
errors: [String!]
}
# ═══════════════════════════════════════════════════════════════
# SCALARS
# ═══════════════════════════════════════════════════════════════
scalar DateTime
```
---
## 2. Ejemplos de Queries
### Listar estudiantes con inscripciones
```graphql
query GetStudents {
students {
id
name
email
totalCredits
enrollments {
subject { name professor { name } }
}
}
}
```
### Materias disponibles para inscripción
```graphql
query GetAvailableSubjects($studentId: Int!) {
availableSubjects(studentId: $studentId) {
subject { id name credits professor { name } }
isAvailable
unavailableReason
}
}
```
### Compañeros de clase
```graphql
query GetClassmates($studentId: Int!) {
classmates(studentId: $studentId) {
subjectName
students
}
}
```
---
## 3. Ejemplos de Mutations
### Crear estudiante
```graphql
mutation CreateStudent {
createStudent(input: { name: "Juan Pérez", email: "juan@email.com" }) {
student { id name email }
errors
}
}
```
### Inscribir en materia
```graphql
mutation Enroll {
enrollStudent(input: { studentId: 1, subjectId: 3 }) {
enrollment {
id
subject { name }
enrolledAt
}
errors
}
}
```
---
## 4. DataLoaders (Evitar N+1)
| DataLoader | Carga Batch |
|------------|-------------|
| `StudentByIdDataLoader` | Estudiantes por IDs |
| `SubjectByIdDataLoader` | Materias por IDs |
| `ProfessorByIdDataLoader` | Profesores por IDs |
| `EnrollmentsByStudentDataLoader` | Inscripciones por estudiante |
| `SubjectsByProfessorDataLoader` | Materias por profesor |
---
## 5. Errores de Negocio
| Código | Mensaje | Contexto |
|--------|---------|----------|
| `MAX_ENROLLMENTS` | "Máximo 3 materias permitidas" | enrollStudent |
| `SAME_PROFESSOR` | "Ya tienes materia con este profesor" | enrollStudent |
| `DUPLICATE_EMAIL` | "Email ya registrado" | createStudent |
| `NOT_FOUND` | "Estudiante no encontrado" | updateStudent |
| `INVALID_CREDENTIALS` | "Usuario o contraseña incorrectos" | login |
| `USERNAME_EXISTS` | "El nombre de usuario ya está en uso" | activateAccount |
| `INVALID_ACTIVATION_CODE` | "Código de activación inválido o expirado" | activateAccount |
| `INVALID_RECOVERY_CODE` | "Código de recuperación inválido" | resetPassword |
| `UNAUTHORIZED` | "No tienes permiso para esta acción" | Operaciones protegidas |
---
## 6. Ejemplos de Autenticación
### Login
```graphql
mutation Login {
login(input: { username: "admin", password: "Admin123!" }) {
success
token
user { id username role studentId studentName }
error
}
}
```
### Activar Cuenta
```graphql
mutation ActivateAccount {
activateAccount(input: {
activationCode: "MSAGDM5DNLAF"
username: "juan.perez"
password: "MiPassword123"
}) {
success
token
recoveryCode # Solo se muestra UNA vez
user { id username role studentId studentName }
error
}
}
```
### Crear Estudiante (Admin)
```graphql
mutation CreateStudent {
createStudent(input: { name: "Juan Pérez", email: "juan@email.com" }) {
student { id name email isActivated }
activationCode # "MSAGDM5DNLAF"
activationUrl # "https://app.com/activate?code=MSAGDM5DNLAF"
expiresAt # "2026-01-11T06:00:00Z"
errors
}
}
```