# 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 } } ```