- Notifications
You must be signed in to change notification settings - Fork0
curso-graphQL/3-academia-online
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
- Creación y configuración de ficheros necesarios
- Instalación de dependencias
- Ficheros JSON con la información de cursos
- Creación del servidor node express
- Schema
- Resolvers
- Configurar Apollo Server
- Obtener la lista de estudiantes
- Obtener un estudiante con el id indicado
- Obtener la lista de cursos
- Obtener un curso con el id indicado
- Lista de estudiantes de los cursos
- Mutations: Primeros pasos
- Definición del input CursoInput
- Añadir un nuevo curso
- Validaciones para no duplicar datos
- Modificar un curso
- Eliminar un curso
Generamos elpackage.json mediante
npm init
Generamos eltsconfig.json mediante
npx tsc --init --rootDir src --outDir build --lib dom,es6 --module commonjs --target es6 --removeComments --resolveJsonModule true
Lista de dependencias que necesitaremos para trabajar en este proyecto:
- express
- apollo-server-express
- lodash
- graphql
- graphql-import-node
- compression
- cors
- typescript
- graphql-tools
- graphql-playground-middleware-express
npm install express graphql ncp http graphql-import-node compression cors lodash typescript graphql-tools graphql-playground-middleware-express apollo-server-express
npm install @types/compression @types/express @types/cors @types/lodash @types/node @types/graphql -D
Creamos la carpetasrc y dentro de ella otra carpetadata donde pondremos los archivos JSON con los datos.
Creamos un archivodata.store.ts para importar los JSON.
importcursosfrom'./courses.json';importestudiantesfrom'./students.json';exportconstdatabase={ cursos, estudiantes}
Configuramos los scripts delpackage.json
"scripts": {"start":"node build/server.js","build":"tsc -p . && ncp src/shchema build/schema","start:dev":"npm run build:dev","build:dev":"nodemon 'src/server.ts' --exec 'ts-node' src/server.ts -e ts,json,graphql" },
Configuramos el servidor node express con los parámetros básicos
importcompressionfrom'compression';importcorsfrom'cors';import{createServer}from'http';constPORT=5200;constapp=express();app.use(cors());app.use(compression());app.get('/',(req,res)=>res.send('Hola a la academia online en GraphQL'));createServer(app).listen({port:PORT},()=>console.log(`Servidor Academia Online listo http://localhost:${PORT}`));
Creamos un directorioschema dentro desrc y en su interior el archivoschema.graphql en el que definiremos los tipos que vamos a necesitar en función de los datos que tenemos:
typeQuery {estudiantes:String}typeEstudiante {id:ID!name:String!email:String!website:Stringcourses: [Curso!]!}typeCurso {id:ID!title:String!description:Stringclases:Int!time:Floatlevel:Nivellogo:String!path:String!teacher:String!students: [Estudiante!]reviews: [Valoracion!]!}enumNivel{ TODOS NOVATOS INTERMEDIO EXPERTO}typeValoracion {id:ID!name:String!points:Float!comment:String}
Creamos una carpetaresolvers ensrc con los siguientes archivos:
- query.ts
import{IResolvers}from'graphql-tools';constquery:IResolvers={Query:{estudiantes():string{return"lista de estudiantes";//sólo para testear}}}exportdefaultquery;
- resolversMap.ts
import{IResolvers}from'graphql-tools';importqueryfrom'./query';constresolversMap:IResolvers={ ...query}exportdefaultresolversMap;
En la carpetasrc/schema creamos un archivoindex.ts que contendrá lo siguiente:
import{GraphQLSchema}from'graphql';import'graphql-import-node';import{makeExecutableSchema}from'graphql-tools';importtypeDefsfrom'./schema.graphql';importresolversfrom'../resolvers/resolversMap';constschema:GraphQLSchema=makeExecutableSchema({ typeDefs, resolvers})exportdefaultschema;
Modificamosserver.ts
...import{ApolloServer}from'apollo-server-express';importschemafrom'./schema/index';importexpressPlayGroundfrom'graphql-playground-middleware-express';......constserver=newApolloServer({ schema,introspection:true});server.applyMiddleware({app});app.get('/',expressPlayGround({endpoint:'/graphql'}));createServer(app).listen({port:PORT},()=>console.log(`Servidor Academia Online listo http://localhost:${PORT}`));...
Modificamos enschema.graphql la query:
typeQuery {"Lista de los estudiantes de la academia"estudiantes: [Estudiante!]!}
Igualmente modificamos enresolvers/query.ts la constantequery:
constquery:IResolvers={Query:{estudiantes():any{returndatabase.estudiantes;}}}
Una vez hecho esto, ya podemos lanzar en apollo server la query a estudiantes estableciendo los parámetros que queremos que nos devuelva.
Para el caso de querer recuperar la información de los cursos de cada estudiante necesitaremos otro resolver.
Creamos en la carpetaresolvers un nuevo archivotypes.ts:
import{IResolvers}from'graphql-tools';import{database}from'../data/data.store';import_from'lodash';consttype:IResolvers={Estudiante:{courses:parent=>{constcursosLista:any[]=[]parent.courses.map((curso:string)=>{cursosLista.push(_.find(database.cursos,['id',curso]))})returncursosLista}}}exportdefaulttype;
Debemos añadir el nuevo resolver aresolvers/resolverMap.ts
...importtypefrom'./type';constresolversMap:IResolvers={ ...query, ...type}...
Primero modificamosschema.graphql para añadir la query de un solo estudiante:
typeQuery {"Lista de los estudiantes de la academia"estudiantes: [Estudiante!]!,"Información del estudiante de la academia seleccionado por ID"estudiante(id:ID!):Estudiante!!,}
Luego añadimos el nuevo resolver para obtener un solo estudiante aresolvers/query.ts:
constquery:IResolvers={Query:{estudiantes():any{returndatabase.estudiantes;},estudiante(__:void,{ id}):any{returndatabase.estudiantes.find(estudiante=>estudiante.id===id);}}}
Con esto ya tenemos disponible la info en la api.
En caso de que el elemento que buscamos no se encuentre, podemos devolver una respuesta que mantenga el formato que se espera, pero que indique de forma clara que no se han encontrado registros. Para conseguirlo, modificamos el resolver:
...estudiante(__:void,{ id}):any{constfound=database.estudiantes.find(estudiante=>estudiante.id===id);returnfound||{id:-1,name:`No se ha encontrado el estudiante con ID${id}`,email:'',courses:[]}}...
Prodedemos de igual forma que hicimos para la lista de estudiantes: Primero modificamos la query en el schema.
typeQuery {"Lista de los estudiantes de la academia"estudiantes: [Estudiante!]!,"Información del estudiante de la academia seleccionado por ID"estudiante(id:ID!):Estudiante!,"Lista de los cursos de la academia"cursos: [Curso!]!}
Luego modificamos el resolver enquery.ts:
constquery:IResolvers={Query:{ ...cursos():any{ returndatabase.cursos;}, ...
Procedemos de la misma forma, modificando primero el schema:
typeQuery { ..."Información del curso de la academia seleccionado por ID"curso(id:ID!):Curso!,}
Luego modificamos el resolver:
constquery:IResolvers={Query:{ ...curso(__:void,{ id}):any{const found=database.cursos.find(curso=>curso.id===id);returnfound||{id:-1,title:`No se ha encontrado el curso con ID${id}`,clases:-1,logo:'',path:'',teacher:'',reviews:[]}},}}
Vamos también a añadir un nuevo resolver para poder obtener los estudiantes que están inscritos a un curso. Para ello modificamosresolvers/type.ts:
consttype:IResolvers={ ...Curso:{students:parent=>{constestudiantesLista:any[]=[]database.estudiantes.map((estudiante:any)=>{if(!!estudiante.courses.find((course:string)=>course===parent.id)){estudiantesLista.push(estudiante)}});returnestudiantesLista},path:parent=>`http://www.udemy.com${parent.path}`}
Como en este caso, los estudiantes de un curso no es un parámetro obligatorio, contemplamos el caso en el que parent.students sea undefined.
Adicionalmente hemos modificado cómo se entrega la información del path del curso para incluir la url completa:
Una vez hemos terminado de configurar lasqueries para obtener datos, vamos a empezar a configurar losmutations para poder añadir nuevos datos.
Enschema-graphql añadimos un nuevo tipo:
typeMutation {"Añadir nuevo curso del al academia"cursoNuevo(curso:CursoInput!):Curso!"Modificar curso del al academia existente"modificarCurso(curso:CursoInput!):Curso!"Eliminar curso del al academia seleccionado por su ID"eliminarCurso(id:ID!):Curso!}
En el directorioresolvers creamos un nuevo ficheromutation.ts donde definiremos las distintas acciones más adelante.
import{IResolvers}from'graphql-tools';import_from'lodash';constmutation:IResolvers={}exportdefaultmutation;
Debemos añadir este archivo alresolversMap.ts
constresolversMap:IResolvers={ ...query, ...type, ...mutation,}
En el punto anterior indicamos que el parámetor curso para añadir/modificar un curso era del tipo CursoInput.
Definimos este tipo enschema.graphql tomando como referencia las propiedades del typeCurso:
inputCursoInput {id:IDtitle:String!description:Stringclases:Int!time:Floatlevel:Nivellogo:String!path:String!teacher:String!}
En este caso ID no puede ser required porque al crear el curso no tenemos su valor y va como null. Del mismo modo, no incluimos los atributos reviews ni students porque al crear un nuevo curso estas propiedades no existen.
En el archivomutation.ts añadimos el mutation para añadir un nuevo curso:
Mutation: {cursoNuevo(__: void, { curso }):any {constnuevoCurso = {id:String(database.cursos.length + 1),title:curso.title,description:curso.description,clases:curso.clases,time:curso.time,level:curso.level,logo:curso.logo,path:curso.path,teacher:curso.teacher,reviews: [],students: [] };database.cursos.push(nuevoCurso);returnnuevoCurso; } }
Una vez hecho esto, ya podemos añadir un nuevo curso a través de apollo server:
Para realizar esta validación, en el mutation ponemos una condición que compruebe que el nuevo curso no existe antes de formar el objeto nuevoCurso:
if(database.cursos.find(item=>curso.title===item.title)){return{id:-1,title:'El curso ya existe con este título',clases:0,logo:'',path:'',teacher:'',reviews:[],}}
Creamos el nuevo mutation:
modificarCurso(__:void,{ curso}):any{constfoundIndex=_.findIndex(database.cursos,item=>curso.id===item.id)if(foundIndex>0){database.cursos[foundIndex]={ ...database.cursos[0], ...curso};returndatabase.cursos[foundIndex];}return{id:-1,title:'No existe curso con este id',clases:0,logo:'',path:'',teacher:'',reviews:[],}}
Creamos el nuevo mutation:
eliminarCurso(__:void,{ id}):any{constfound=_.remove(database.cursos,item=>id===item.id)returnfound[0]||{id:-1,title:'No existe curso con este id',clases:0,logo:'',path:'',teacher:'',reviews:[],}}
About
Resources
Uh oh!
There was an error while loading.Please reload this page.