Esteban Ciudad
Manuel Eguiluz
Gracias, man.
Esteban Ciudad
de nada bro @manolo0189, me alegro que te haya servido.
¿Cómo agregar un nuevo tipo a nuestro directorio?
En el emocionante viaje del desarrollo web, GraphQL se destaca como una herramienta esencial para la gestión eficiente de datos. En este artículo, exploraremos cómo agregar un nuevo tipo a nuestro esquema de GraphQL, junto con los resolvers, queries y mutations necesarios para hacer que todo funcione perfectamente.
¿Qué es un tipo y cómo definirlo?
En GraphQL, un tipo es una definición de cómo deben verse los objetos en nuestra API. Estos determinan qué datos están disponibles para nuestras queries y mutations. Para crear un tipo llamado Student, seguimos estos pasos:
Student con campos obligatorios:
id: tipo ID!name: tipo String!email: tipo String!Esto se vería de la siguiente manera:
type Student { id: ID! name: String! email: String! }
¿Cómo crear resolvers y queries para el nuevo tipo?
Una vez que hemos definido el tipo, es necesario crear resolvers que permitan interactuar con los datos. Para los estudiantes, queremos devolver todos los estudiantes o un estudiante específico:
type Query { students: [Student] student(id: ID!): Student }
Los resolvers para estas queries se implementan así:
const resolvers = { Query: { students: async () => { return await StudentModel.find(); }, student: async (_, { id }) => { return await StudentModel.findById(id); } } }
¿Cómo incluir el input type y las mutations?
El siguiente paso es permitir la creación y edición de estudiantes mediante mutation. Definimos un input type para pasar los datos al resolver:
input StudentInput { name: String! email: String! }
Creamos la mutation para añadir y editar:
type Mutation { createStudent(input: StudentInput): Student updateStudent(id: ID!, input: StudentInput): Student }
Y definimos los resolvers correspondientes:
const resolvers = { Mutation: { createStudent: async (_, { input }) => { const newStudent = new StudentModel(input); return await newStudent.save(); }, updateStudent: async (_, { id, input }) => { return await StudentModel.findByIdAndUpdate(id, input, { new: true }); } } }
¿Cómo realizar tests a las funciones creadas?
Realizar tests es fundamental para asegurarse de que todo funciona conforme a lo esperado. Puedes utilizar herramientas como Apollo Server para ejecutar instantáneamente tus queries y mutations.
Prueba crear un estudiante con la mutation createStudent:
mutation { createStudent(input: { name: "Estudiante Uno", email: "estudiante1@email.com" }) { id name email } }
Y verifica la correcta inserción consultando todos los estudiantes:
query { students { id name email } }
¿Cómo puedes mejorar y extender esta funcionalidad?
Para quienes desean un reto adicional, intenta crear una nueva función de mutation para eliminar tanto estudiantes como cursos. Esto te ayudará a afinar tus habilidades con GraphQL y prepararte para proyectos más complejos.
No olvides seguir explorando e integrando nuevos tipos, resolvers, y tecnologías a tu stack de desarrollo. ¡Continúa expandiendo tus conocimientos y habilidades en desarrollo web con entusiasmo y dedicación!
Esteban Ciudad
Manuel Eguiluz
Gracias, man.
Esteban Ciudad
de nada bro @manolo0189, me alegro que te haya servido.
David Gallego
Para seleccionar multiples definiciones de la misma palabra como el profe le hace:
Referencia: Keyboard shortcuts
David Gallego
Alt + Shift + ↓ : para copiar una linea entera hacia abajo
Favio Náquira
estos son los comentarios que contribuyen =D
Lian Nivin
Me parece super genial que haya repaso en este curso. Algo bueno para imitar en los demas cursos.
César David Ramírez Dimaté
Así es, bastante oportuno esta parte.
Byron Mesias Cueva Cabrera
Reto resuelto:
Noel Zenon Callapiña Angles
excelente aporte! Gracias!
Jasan Azael Hernández Bautista
Creck Brayan Mauri Pastor Paredes
guardado en mis notas
Jhon Manuel Angulo Moncada
de acuerdo con Jecshan Castillo, adicional mente si están haciendo todo en las nuevas versiones mongodb y usan mongo atlas me toco hacerlo así para que retornara la actualización del documento:
editCourse: async (root, { id, input }) => { let db let course try { db = await connectDb() course = await db.collection('courses').findOneAndUpdate( { _id: ObjectID(id) }, { $set: input }, { new : true, returnOriginal: false } ) } catch (error) { console.error(error) } return course.value }
Creck Brayan Mauri Pastor Paredes
buen aporte
Iván Darío Sánchez Jiménez
Fabian Guerrero
papu te quedo nitido el aporte pero te faltaron los awaits después de buscar student y course. Eso puede hacer que te falle en un futuro entregando undefineds a lo loco.
Todo bien
Iván Darío Sánchez Jiménez
Gracias compañero, tiene toda la razón, Me dí cuenta después de haber publicado este aporte y lo corregí en mi equipo pero no acá
Mario Menjívar
Seguí una resolución simple:
Mutation Type: !Mutation Type Screenshot
Mutation Resolver: !Mutation Resolver Screenshot
Result: !Result Screenshot
Juan Jose Vega
no había pensado en usar postman esto da para jugar mucho
César David Ramírez Dimaté
Excelente aporte. Acabé de implementarlo en el repositorio del curso. Github - GraphQL Platzi
Comparto mis soluciones al reto
deleteCourse: async (root, { id }) => { let info try { const db = await connectDB() // MongoDB al eliminar un documento, retorna un objeto con información del resultado del proceso, sin embargo, no retorna el documento eliminado info = await db.collection('courses').deleteOne({ _id: ObjectId(id) }) } catch (error) { console.error(error) } console.log(info) return info.deletedCount ? `Curso con id ${id} eliminado satisfactoriante` : `No existe el curso con el id ${id} indicado` },
Este es otro enfoque
deleteStudent: async (root, { id }) => { let student try { const db = await connectDB() // Consulto primero el documento para devolverlo como resultado de la eliminación, esto puede ser interesante en Clientes que deseén notificar a sus usuarios de que datos se eleminó student = db.collection('students').findOne({ _id: ObjectId(id) }) // Elimino el documento await db.collection('students').deleteOne({ _id: ObjectId(id) }) } catch (error) { console.error(error) } return student }
Oney Ortiz Roman
Andres Roberto Coello Goyes
{ "errors": [ { "message": "Field \"createStudent\" of type \"Student\" must have a selection of subfields. Did you mean \"createStudent { ... }\"?", "locations": [ { "line": 2, "column": 3 } ] } ] }``` si alguien me puede ayudar con este error al crear estudiante
Daniel Esteves
No estás pasando bien los inputs de selección para crear al estudiante
Andres Roberto Coello Goyes
si me di cuenta 5 horas despues :) :D
Carlos S. Aldazosa
Alejandro
Company_adminHola buenas tardes, se podrá agregar en el mutation dos estudiantes en un solo llamado enviando un objeto por ejemplo?, en lugar de hacerlo uno por uno como en el video
Derek Samuel Paúl Peña
¿Alguien sabe cómo se definen los tipos imágenes en GraphQl?
Juan Castro
ProfesorLas imágenes no se guardan directamente en la base de datos. Ahí guardamos es un link a la URL donde las almacenamos en un servidor de sitios estáticos o CDN. Por lo que el tipo en GraphQL sería un string.
Derek Samuel Paúl Peña
Gracias
Octavio Elsacte Palma
Juan David González Rodríguez
Jecsham Castillo
Yo recomiendo usar findOneAndUpdate en lugar de hacer 2 requests a mongodb findOneAndUpdate retorna el documento en la propiedad value, a loq ue quedaría: students.value
Erick Daniel Pérez Mata
Reinaldo Mendoza
Estoy usando WSL y venia funcionando bien mi codigo, incluso lo guardo en git al terminar cada clase con los cambios y comprobando de que funciona, ahora obtengo el error localhost refused to connect, volvi atras con git a un commit que se que funcionaba y obtengo el mismo error, supongo que es un error con los puertos, alguien me podria ayudar?
Mi solución al desafío:
Implemento los mutation deleteCourse y deleteStudent que requieren un ID y devuelven en este caso un string indicando si se realizó o no la operación de borrado de la DB.
schema.graphql
type Mutation { "Crea un nuevo curso" createCourse(input: CourseInput): Course "Edita un curso" editCourse(id: ID!, input: CourseEditInput): Course "Elimina un curso" deleteCourse(id: ID!): String "Crea un nuevo estudiante" createStudent(input: StudentInput): Student "Edita un estudiante" editStudent(id: ID!, input: StudentEditInput): Student "Elimina un estudiante" deleteStudent(id: ID!): String }
mutations.js
deleteCourse: async (root, { id }) => { let db, info; try { db = await connectDb(); info = await db.collection('courses').deleteOne({ _id: ObjectID(id) }); } catch (error) { console.error(error); } return info.deletedCount ? `El curso con id ${id} fue eliminado exitosamente.` : 'No existe el curso con el id indicado'; },
Schema
"Elimina un Estudiante" deleteStudent(_id: ID!): String "Eliminar un Curso" deleteCourse(_id: ID!): String,
Resolvers
deleteStudent: async (root, { _id }) => { const id = await mongoDb.delete('courses', _id); return `Curso con Id: ${id} eliminado`; }
También les comparto la manera en como me conecto a Mongo, pienso que es la manera más limpia que la que hace el profesor.
const { MongoClient, ObjectId } = require('mongodb'); const { config } = require('../config'); const USER = encodeURIComponent(config.dbUser); const PASSWORD = encodeURIComponent(config.dbPassword); const DB_NAME = config.dbName; const MONGO_URI = `mongodb+srv://${USER}:${PASSWORD}@${config.dbHost}:${config.dbPort}/${DB_NAME}?retryWrites=true&w=majority`; class MongoLib { constructor() { this.client = new MongoClient(MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true }); this.dbName = DB_NAME; } connect() { if (!MongoLib.connection) { MongoLib.connection = new Promise((resolve, reject) => { this.client.connect(err => { if (err) { reject(err); } console.log('Connected succesfully to mongo'); resolve(this.client.db(this.dbName)); }); }); } return MongoLib.connection; } getAll(collection) { return this.connect().then(db => { return db .collection(collection) .find() .toArray(); }); } get(collection, id) { return this.connect().then(db => { return db .collection(collection) .findOne({ _id: ObjectId(id) }); }) } create(collection, data) { return this.connect() .then(db => { return db .collection(collection).insertOne(data); }) .then(result => result.insertedId); } updated(collection, id, data) { return this.connect() .then(db => { return db .collection(collection) .updateOne({ _id: ObjectId(id) }, { $set: data }, { upsert: true }); }) .then(result => result.updsertdId || id); } delete(collection, id) { return this.connect() .then(db => { return db .collection(collection) .deleteOne({ _id: ObjectId(id) }) }) .then(() => id); } } module.exports = MongoLib;
schema
"Delete course" deleteCourse(_id: ID!): Course "Delete student" deleteStudent(_id: ID!): Student
mutation
deleteCourse: async (root, { _id }) => { let db; let course; try { db = await connectDB(); course = db.collection('courses').findOne({ _id: ObjectID(_id) }); await db.collection('courses').deleteOne({ _id: ObjectID(_id) }); } catch (error) { console.error(error); } return course; }, deleteStudent: async (root, { _id }) => { let db; let student; try { db = await connectDB(); student = db.collection('students').findOne({ _id: ObjectID(_id) }); await db.collection('students').deleteOne({ _id: ObjectID(_id) }); } catch (error) { console.error(error); } return student; }
Mi approach, yo utilizo el findOneAndDelete() y retorno un custom type con un Boolean que resuelve se se ha borrado o no y la entidad borrada
Schema.graphql
module.exports = { . . . type DeleteStudentOut{ status: Boolean entity: Student } type DeleteCourseOut{ status: Boolean entity: Course } type Mutation { . . . "Eliminar un curso" deleteCourse(_id: ID!): DeleteCourseOut "Elimina un estudiante" deleteStudent(_id: ID!): DeleteStudentOut }
Mutations.js
module.exports = { . . . deleteCourse: async (root, {_id}) => { let db, flag try { db = await connectDB() flag = await db.collection('cursos').findOneAndDelete({_id: ObjectID(_id)}) } catch (error) { console.error(error) } return {status:flag.lastErrorObject.n, entity:flag.value} }, deleteStudent: async (root, {_id}) => { let db, flag try { db = await connectDB() flag = await db.collection('estudiantes').findOneAndDelete({_id: ObjectID(_id)}) } catch (error) { console.error(error) } return {status:flag.lastErrorObject.n, entity:flag.value} } }
Mi solucion!
type Mutation { "Create a Course" createCourse(input: CourseInput!): Course "Edit a Course" editCourse(_id: ID!, input: CourseEditInput!): Course "Delete a Course" deleteCourse(_id: ID!): String "Create a Student" createStudent(input: StudentInput!): Student "Edit a Student" editStudent(_id: ID!, input: StudentEditInput!): Student "Delete a Student" deleteStudent(_id: ID!): String }
deleteCourse: async (root, { _id }) => { let db; let course; try { db = await connectDB(); course = await db.collection('courses').findOne({ _id: ObjectID(_id) }) await db.collection('courses').deleteOne( { _id: ObjectID(_id) } ) } catch (error) { console.error(error); } return course; },
Esta es mi solución. Creé un nuevo Type que corresponde a lo que devuelve la función deleteOne, según la documentación de mongo.
type DeleteResponse { acknowledged: Boolean deletedCount: Int } type Mutation { "Eliminar curso" deleteCourse(_id: ID!): DeleteResponse "Eliminar estudiante" deleteStudent(_id: ID!): DeleteResponse }
Y finalmetne el Resolver:
deleteCourse: async (root, { _id }) => { let db, resp try { db = await connectDB(); resp = await db .collection("courses") .deleteOne({ _id: ObjectId(_id) }); } catch (error) { console.error(error); } return resp; }, deleteStudent: async (root, { _id }) => { let db, resp try { db = await connectDB(); resp= await db .collection("students") .deleteOne({ _id: ObjectId(_id) }); } catch (error) { console.error(error); } return resp; }
queries.js
deleteStudent: async (root, args) => { let db; let student = {}; try { db = await connectDB(); student = await db .collection('students') .deleteOne({ _id: ObjectID(args.id) }); console.log(student); } catch (err) { console.error(err); } return student; },
schema.graphql
type Query { "Return all courses" getCourses: [Course] "Return one course" getCourse(id: ID!): Course "Return all students" getStudents: [Student] "Return one student" getStudent(id: ID!): Student "Delete one course" deleteCourse(id: ID!): Course "Delete one student" deleteStudent(id: ID!): Student }
Reto resuelto: schema.graphql
"Eliminar un curso" deleteCourse(_id: ID!): String "Eliminar un estudiante" deleteStudent(_id: ID!): String
mutations.js
deleteCourse: async (root, { _id }) => { let db; try { db = await connectDb(); await db.collection('courses').deleteOne({ _id: ObjectID(_id) }); } catch (error) { console.error(error); } return `Se elimino el curso ${_id}`; }, deleteStudent: async (root, { _id }) => { let db; try { db = await connectDb(); await db.collection('students').deleteOne({ _id: ObjectID(_id) }); } catch (error) { console.error(error); } return `Se elimino el alumno ${_id}`; },