├── .gitignore ├── launcher.js ├── src ├── DB │ ├── db.sqlite │ ├── setup.js │ ├── models │ │ ├── index.js │ │ ├── User.js │ │ ├── Profesor.js │ │ ├── Comentario.js │ │ └── Curso.js │ ├── knexfile.js │ ├── migrations │ │ ├── comentarios_table.js │ │ ├── profesores_table.js │ │ └── cursos_table.js │ ├── seeds │ │ ├── seed_comentarios_table.js │ │ ├── seed_cursos_table.js │ │ └── seed_profesores_table.js │ └── db.json ├── schemas │ ├── index.js │ ├── resolvers │ │ ├── index.js │ │ ├── BuildMutation.js │ │ └── resolver.js │ ├── schemas │ │ ├── index.js │ │ ├── Profesor.js │ │ └── Curso.js │ └── schema.js ├── schemaMocks │ ├── index.js │ ├── mockups │ │ ├── Profesores.json │ │ ├── Comentarios.json │ │ ├── index.js │ │ └── Cursos.json │ ├── mockupsCasual │ │ ├── Profesores.js │ │ ├── index.js │ │ ├── Comentarios.js │ │ └── Cursos.js │ └── schema.js ├── app │ ├── index.css │ ├── App.css │ ├── App.test.js │ ├── App.js │ └── registerServiceWorker.js ├── server.js └── index.js ├── diagrama ├── schema.dia ├── schema.png └── query-resuelto.png ├── public ├── favicon.ico ├── manifest.json └── index.html ├── .babelrc ├── LICENSE ├── package.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | yarn.lock -------------------------------------------------------------------------------- /launcher.js: -------------------------------------------------------------------------------- 1 | require("babel-register"); 2 | require("./src/server.js"); -------------------------------------------------------------------------------- /src/DB/db.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrcaPracticas/GQL/HEAD/src/DB/db.sqlite -------------------------------------------------------------------------------- /src/schemas/index.js: -------------------------------------------------------------------------------- 1 | import Schema from "./schema"; 2 | 3 | export default Schema; 4 | -------------------------------------------------------------------------------- /diagrama/schema.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrcaPracticas/GQL/HEAD/diagrama/schema.dia -------------------------------------------------------------------------------- /diagrama/schema.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrcaPracticas/GQL/HEAD/diagrama/schema.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrcaPracticas/GQL/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/schemaMocks/index.js: -------------------------------------------------------------------------------- 1 | import Schema from "./schema"; 2 | 3 | export default Schema; 4 | -------------------------------------------------------------------------------- /src/schemas/resolvers/index.js: -------------------------------------------------------------------------------- 1 | import resolver from "./resolver"; 2 | 3 | export default resolver; 4 | -------------------------------------------------------------------------------- /diagrama/query-resuelto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OrcaPracticas/GQL/HEAD/diagrama/query-resuelto.png -------------------------------------------------------------------------------- /src/app/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: 'Lato', sans-serif; 5 | } 6 | -------------------------------------------------------------------------------- /src/schemaMocks/mockups/Profesores.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1", 3 | "nombre": "Jorge Mendez", 4 | "nacionalidad": "Mexicana" 5 | } -------------------------------------------------------------------------------- /src/schemas/schemas/index.js: -------------------------------------------------------------------------------- 1 | import Curso from "./Curso"; 2 | import Profesor from "./Profesor"; 3 | 4 | export { 5 | Curso, 6 | Profesor, 7 | }; 8 | -------------------------------------------------------------------------------- /src/schemaMocks/mockups/Comentarios.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "nombre": "Jorge Mendez Ortegs", 5 | "cuerpo": "Comentario loco" 6 | } 7 | ] -------------------------------------------------------------------------------- /src/DB/setup.js: -------------------------------------------------------------------------------- 1 | const { Model } = require('objection') 2 | const knexConfig = require('./knexfile') 3 | const Knex = require('knex') 4 | 5 | const knex = Knex(knexConfig.development) 6 | Model.knex(knex) -------------------------------------------------------------------------------- /src/app/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .Curso { 6 | background-color: #AEAEAE; 7 | width: 30em; 8 | margin: 1em auto; 9 | padding: 1em; 10 | border-radius: .3em; 11 | } 12 | -------------------------------------------------------------------------------- /src/schemaMocks/mockupsCasual/Profesores.js: -------------------------------------------------------------------------------- 1 | import Casual from "casual"; 2 | 3 | const SCHEMA = { 4 | id: Casual.uuid, 5 | nombre: Casual.full_name, 6 | nacionalidad: Casual.country, 7 | }; 8 | 9 | export default SCHEMA; 10 | -------------------------------------------------------------------------------- /src/app/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | }); 9 | -------------------------------------------------------------------------------- /src/schemaMocks/mockups/index.js: -------------------------------------------------------------------------------- 1 | import MockCursos from "./Cursos"; 2 | import MockComentarios from "./Comentarios.json"; 3 | import MockProfesores from "./Profesores.json"; 4 | 5 | export { 6 | MockComentarios, 7 | MockCursos, 8 | MockProfesores, 9 | }; 10 | -------------------------------------------------------------------------------- /src/schemaMocks/mockupsCasual/index.js: -------------------------------------------------------------------------------- 1 | import CasualCursos from "./Cursos"; 2 | import CasualComentarios from "./Comentarios"; 3 | import CasualProfesores from "./Profesores"; 4 | 5 | export { 6 | CasualComentarios, 7 | CasualCursos, 8 | CasualProfesores, 9 | }; 10 | -------------------------------------------------------------------------------- /src/DB/models/index.js: -------------------------------------------------------------------------------- 1 | import ModeloComentario from "./Comentario"; 2 | import ModeloCurso from "./Curso"; 3 | import ModeloProfesor from "./Profesor"; 4 | import ModeloUser from "./User"; 5 | 6 | export { 7 | ModeloComentario, 8 | ModeloCurso, 9 | ModeloProfesor, 10 | ModeloUser, 11 | }; 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets" : [ 3 | [ 4 | "env", 5 | { 6 | "targets": { 7 | "node": "current" 8 | } 9 | } 10 | ], 11 | "minify", 12 | "stage-1" 13 | ], 14 | "plugins" : [ 15 | ["inline-json-import", {}] 16 | ] 17 | } -------------------------------------------------------------------------------- /src/DB/knexfile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Archivo de configuracion en el cual 3 | * se le indica el cliente de motor 4 | * de base de datos 5 | */ 6 | module.exports = { 7 | development: { 8 | client: "sqlite3", 9 | connection: { 10 | filename: `${__dirname}/db.sqlite`, 11 | }, 12 | }, 13 | production: {}, 14 | }; 15 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/schemaMocks/mockupsCasual/Comentarios.js: -------------------------------------------------------------------------------- 1 | import Casual from "casual"; 2 | 3 | let contador = 1; 4 | const DATA = []; 5 | 6 | do { 7 | contador += 1; 8 | const SCHEMA = { 9 | id: contador, 10 | cuerpo: Casual.description, 11 | nombre: Casual.full_name, 12 | }; 13 | DATA.push(SCHEMA); 14 | } while (contador < 11); 15 | 16 | export default DATA; 17 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | import Express from "express"; 2 | import Schema from "./schemas"; 3 | import "./DB/setup"; 4 | 5 | const SERVER = Express(); 6 | const PORT = 8080; 7 | 8 | // ruta por defecto graphql 9 | Schema.applyMiddleware({ app: SERVER }); 10 | 11 | /** 12 | * iniciando el servidor 13 | */ 14 | SERVER.listen(PORT, () => { 15 | console.log("El servidor esta corriendo :)"); 16 | }); 17 | -------------------------------------------------------------------------------- /src/schemaMocks/mockupsCasual/Cursos.js: -------------------------------------------------------------------------------- 1 | import Casual from "casual"; 2 | 3 | let contador = 1; 4 | const DATA = []; 5 | 6 | do { 7 | contador += 1; 8 | const SCHEMA = { 9 | id: contador, 10 | titulo: Casual.title, 11 | descripcion: Casual.description, 12 | raiting: Casual.random, 13 | }; 14 | DATA.push(SCHEMA); 15 | } while (contador < 11); 16 | 17 | export default DATA; 18 | -------------------------------------------------------------------------------- /src/schemaMocks/mockups/Cursos.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id" : 1, 4 | "titulo" : "Curso 1", 5 | "descripcion" : "Esto es el cuso 1", 6 | "raiting" : 0.5 7 | }, 8 | { 9 | "id" : 2, 10 | "titulo" : "Curso 2", 11 | "descripcion" : "Esto es el cuso 2", 12 | "raiting" : 0.5 13 | }, 14 | { 15 | "id" : 3, 16 | "titulo" : "Curso 3", 17 | "descripcion" : "Esto es el cuso 3", 18 | "raiting" : 0.5 19 | } 20 | ] -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import ApolloClient from "apollo-boost"; 4 | import { ApolloProvider } from "react-apollo"; 5 | import registerServiceWorker from "./app/registerServiceWorker"; 6 | import App from "./app/App"; 7 | import "./app/index.css"; 8 | 9 | const CLIENT_APOLLO = new ApolloClient({ 10 | uri: "/api", 11 | }); 12 | 13 | 14 | ReactDOM.render( 15 | 16 | 17 | , document.getElementById("root"), 18 | ); 19 | registerServiceWorker(); 20 | -------------------------------------------------------------------------------- /src/DB/models/User.js: -------------------------------------------------------------------------------- 1 | import Axios from "axios"; 2 | 3 | class User { 4 | constructor() { 5 | this.api = Axios.create({ 6 | baseURL: "http://localhost:8081", 7 | }); 8 | } 9 | 10 | list() { 11 | return this 12 | .api 13 | .get("/users") 14 | .then(response => response.data); 15 | } 16 | 17 | find(id) { 18 | return this 19 | .api 20 | .get(`/users/${id}`) 21 | .then(response => response.data); 22 | } 23 | } 24 | 25 | export default new User(); 26 | -------------------------------------------------------------------------------- /src/schemas/schemas/Profesor.js: -------------------------------------------------------------------------------- 1 | const PROFESORES_SCHEMA = ` 2 | # Entidad **Profesor** dentro del sistema 3 | type Profesor { 4 | id: ID! 5 | nombre: String! 6 | nacionalidad: String! 7 | genero: Genero 8 | cursos: [Curso] 9 | } 10 | 11 | input newProfesor { 12 | nombre: String! 13 | nacionalidad: String! 14 | genero: Genero 15 | } 16 | 17 | input editProfesor{ 18 | nombre: String 19 | nacionalidad: String 20 | genero: Genero 21 | } 22 | `; 23 | 24 | const GENERO_SCHEMA = ` 25 | # Enumerador de **Genero** 26 | enum Genero { 27 | MASCULINO 28 | FEMENINO 29 | } 30 | `; 31 | 32 | export default ` 33 | ${PROFESORES_SCHEMA} 34 | ${GENERO_SCHEMA} 35 | `; 36 | -------------------------------------------------------------------------------- /src/DB/models/Profesor.js: -------------------------------------------------------------------------------- 1 | import { Model } from "objection"; 2 | import Path from "path"; 3 | 4 | class Curso extends Model { 5 | /** 6 | * Definicion de la tabla 7 | */ 8 | static get tableName() { 9 | return "profesores"; 10 | } 11 | 12 | /** 13 | * Definicion de las relaciones. 14 | */ 15 | static get relationMappings() { 16 | const RELATION_MAP = { 17 | cursos: { 18 | relation: Model.HasManyRelation, 19 | modelClass: Path.join(__dirname, "/Curso"), 20 | join: { 21 | from: "profesores.id", 22 | to: "cursos.profesor_id", 23 | }, 24 | }, 25 | }; 26 | return RELATION_MAP; 27 | } 28 | } 29 | 30 | export default Curso; 31 | -------------------------------------------------------------------------------- /src/DB/migrations/comentarios_table.js: -------------------------------------------------------------------------------- 1 | const NAME_TABLE = "comentarios"; 2 | 3 | /** 4 | * Inidica que se ejecuta se realizara en la migrcion 5 | * para este casos se crea la tabla. 6 | */ 7 | exports.up = (knex, Promise) => ( 8 | Promise.all([ 9 | knex.schema.createTable( 10 | NAME_TABLE, 11 | (table) => { 12 | table.increments("id").primary().unsigned(); 13 | table.string("nombre"); 14 | table.string("cuerpo"); 15 | table.integer("curso_id").unsigned(); 16 | }, 17 | ), 18 | ]) 19 | ); 20 | 21 | /** 22 | * Al terminar el proceso de migracion se libera la tabla. 23 | */ 24 | exports.down = (knex, Promise) => ( 25 | Promise.all([ 26 | knex.schema.dropTable(NAME_TABLE), 27 | ]) 28 | ); 29 | -------------------------------------------------------------------------------- /src/DB/migrations/profesores_table.js: -------------------------------------------------------------------------------- 1 | 2 | const NAME_TABLE = "profesores"; 3 | 4 | /** 5 | * Inidica que se ejecuta se realizara en la migrcion 6 | * para este casos se crea la tabla. 7 | */ 8 | exports.up = (knex, Promise) => ( 9 | Promise.all([ 10 | knex.schema.createTable( 11 | NAME_TABLE, 12 | (table) => { 13 | table.increments("id").primary().unsigned(); 14 | table.string("nombre"); 15 | table.string("nacionalidad"); 16 | table.string("genero"); 17 | }, 18 | ), 19 | ]) 20 | ); 21 | 22 | /** 23 | * Al terminar el proceso de migracion se libera la tabla. 24 | */ 25 | exports.down = (knex, Promise) => ( 26 | Promise.all([ 27 | knex.schema.dropTable(NAME_TABLE), 28 | ]) 29 | ); 30 | -------------------------------------------------------------------------------- /src/DB/models/Comentario.js: -------------------------------------------------------------------------------- 1 | import { Model } from "objection"; 2 | import Path from "path"; 3 | 4 | class Comentario extends Model { 5 | /** 6 | * Definicion de la tabla 7 | */ 8 | static get tableName() { 9 | return "comentarios"; 10 | } 11 | 12 | /** 13 | * Definicion de las relaciones. 14 | */ 15 | static get relationMappings() { 16 | const RELATION_MAP = { 17 | curso: { 18 | relation: Model.BelongsToOneRelation, 19 | modelClass: Path.join(__dirname, "/Curso"), 20 | join: { 21 | from: "comentarios.curso_id", 22 | to: "cursos.id", 23 | }, 24 | }, 25 | }; 26 | return RELATION_MAP; 27 | } 28 | } 29 | 30 | export default Comentario; 31 | -------------------------------------------------------------------------------- /src/DB/migrations/cursos_table.js: -------------------------------------------------------------------------------- 1 | const NAME_TABLE = "cursos"; 2 | 3 | /** 4 | * Inidica que se ejecuta se realizara en la migrcion 5 | * para este casos se crea la tabla. 6 | */ 7 | exports.up = (knex, Promise) => ( 8 | Promise.all([ 9 | knex.schema.createTable( 10 | NAME_TABLE, 11 | (table) => { 12 | table.increments("id").primary().unsigned(); 13 | table.string("titulo"); 14 | table.string("descripcion"); 15 | table.double("raiting"); 16 | table.integer("profesor_id").unsigned(); 17 | }, 18 | ), 19 | ]) 20 | ); 21 | 22 | /** 23 | * Al terminar el proceso de migracion se libera la tabla. 24 | */ 25 | exports.down = (knex, Promise) => ( 26 | Promise.all([ 27 | knex.schema.dropTable(NAME_TABLE), 28 | ]) 29 | ); 30 | -------------------------------------------------------------------------------- /src/DB/seeds/seed_comentarios_table.js: -------------------------------------------------------------------------------- 1 | const Casual = require("casual"); 2 | 3 | const NAME_TABLE = "comentarios"; 4 | 5 | /** 6 | * Permite el llenado de la base de datos 7 | * los datos con los que estammos llenando la 8 | * base son fake. 9 | */ 10 | exports.seed = (knex, Promise) => ( 11 | knex(NAME_TABLE).del().then( 12 | () => { 13 | const PROMISES = Array(40).fill().map((_, id) => ( 14 | knex(NAME_TABLE).insert( 15 | [ 16 | { 17 | id, 18 | nombre: Casual.name, 19 | cuerpo: Casual.sentences(3), 20 | curso_id: Casual.integer(1, 10), 21 | }, 22 | ], 23 | ) 24 | )); 25 | return Promise.all(PROMISES); 26 | }, 27 | ) 28 | ); 29 | -------------------------------------------------------------------------------- /src/DB/seeds/seed_cursos_table.js: -------------------------------------------------------------------------------- 1 | const Casual = require("casual"); 2 | 3 | const NAME_TABLE = "cursos"; 4 | 5 | /** 6 | * Permite el llenado de la base de datos 7 | * los datos con los que estammos llenando la 8 | * base son fake. 9 | */ 10 | 11 | exports.seed = (knex, Promise) => ( 12 | knex(NAME_TABLE).del().then( 13 | () => { 14 | const PROMISES = Array(10).fill().map((_, index) => ( 15 | knex(NAME_TABLE).insert( 16 | [ 17 | { 18 | id: index + 1, 19 | titulo: Casual.title, 20 | descripcion: Casual.sentences(3), 21 | raiting: Casual.double(1, 10), 22 | profesor_id: Casual.integer(1, 5), 23 | }, 24 | ], 25 | ) 26 | )); 27 | return Promise.all(PROMISES); 28 | }, 29 | ) 30 | ); 31 | -------------------------------------------------------------------------------- /src/DB/models/Curso.js: -------------------------------------------------------------------------------- 1 | import { Model } from "objection"; 2 | import Path from "path"; 3 | 4 | class Curso extends Model { 5 | /** 6 | * Definicion de la tabla 7 | */ 8 | static get tableName() { 9 | return "cursos"; 10 | } 11 | 12 | /** 13 | * Definicion de las relaciones. 14 | */ 15 | static get relationMappings() { 16 | const RELATION_MAP = { 17 | profesor: { 18 | relation: Model.BelongsToOneRelation, 19 | modelClass: Path.join(__dirname, "/Profesor"), 20 | join: { 21 | from: "cursos.profesor_id", 22 | to: "profesores.id", 23 | }, 24 | }, 25 | comentarios: { 26 | relation: Model.HasManyRelation, 27 | modelClass: Path.join(__dirname, "/Comentario"), 28 | join: { 29 | from: "cursos.id", 30 | to: "comentarios.curso_id", 31 | }, 32 | }, 33 | }; 34 | return RELATION_MAP; 35 | } 36 | } 37 | 38 | export default Curso; 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 OrcaPracticas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/DB/seeds/seed_profesores_table.js: -------------------------------------------------------------------------------- 1 | const Casual = require("casual"); 2 | 3 | const NAME_TABLE = "profesores"; 4 | 5 | /** 6 | * Permite el llenado de la base de datos 7 | * los datos con los que estammos llenando la 8 | * base son fake. 9 | */ 10 | 11 | exports.seed = (knex, Promise) => ( 12 | knex(NAME_TABLE).del().then( 13 | () => { 14 | const PROMISES = Array(10).fill().map((_, index) => ( 15 | knex(NAME_TABLE).insert( 16 | [ 17 | { 18 | id: index + 1, 19 | nombre: Casual.name, 20 | nacionalidad: Casual.country, 21 | genero: Casual.random_element( 22 | [ 23 | "MASCULINO", 24 | "FEMENINO", 25 | ], 26 | ), 27 | }, 28 | ], 29 | ) 30 | )); 31 | return Promise.all(PROMISES); 32 | }, 33 | ) 34 | ); 35 | -------------------------------------------------------------------------------- /src/schemas/schemas/Curso.js: -------------------------------------------------------------------------------- 1 | const CURSO_SCHEMA = ` 2 | # Entidad **Curso** dentro del sistema 3 | type Curso { 4 | id: ID! 5 | titulo: String! 6 | descripcion: String! 7 | profesor: Profesor 8 | # Al utilizar @deprecated permite indicar que campos podemos eliminar en un futuro 9 | raiting: Float @deprecated(reason: "No me interesa el **raiting** en el curso") 10 | comentarios: [Comentario] 11 | } 12 | 13 | input newCurso { 14 | titulo: String! 15 | descripcion: String! 16 | raiting: Float 17 | } 18 | input editCurso { 19 | titulo: String 20 | descripcion: String 21 | raiting: Float 22 | } 23 | `; 24 | 25 | const COMENTARIOS_SCHEMA = ` 26 | # Entidad **Comentario** dentro del sistema 27 | type Comentario { 28 | id: ID! 29 | nombre: String! 30 | cuerpo: String! 31 | } 32 | 33 | input newComentario { 34 | nombre: String! 35 | cuerpo: String! 36 | } 37 | 38 | input editComentario { 39 | nombre: String 40 | cuerpo: String 41 | } 42 | `; 43 | 44 | 45 | export default ` 46 | ${CURSO_SCHEMA} 47 | ${COMENTARIOS_SCHEMA} 48 | `; 49 | -------------------------------------------------------------------------------- /src/app/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./App.css"; 3 | import { graphql } from "react-apollo"; 4 | import gql from "graphql-tag"; 5 | 6 | const App = (props) => { 7 | const { data } = props; 8 | let render = ""; 9 | if (data.loading) { 10 | render = ( 11 |
12 |

Cargando

13 |
14 | ); 15 | } else { 16 | render = ( 17 |
18 |

Listado de Cursos

19 | { 20 | data.cursos.map(curso => ( 21 |
22 |

{curso.titulo}

23 |

{curso.descripcion}

24 |
25 |

26 | Profesor: 27 | {curso.profesor.nombre} 28 |

29 |
30 | )) 31 | } 32 |
33 | ); 34 | } 35 | return render; 36 | }; 37 | 38 | const CursosQuery = gql` 39 | query CursosQuery { 40 | cursos { 41 | titulo 42 | descripcion 43 | raiting 44 | profesor { 45 | nombre 46 | } 47 | } 48 | } 49 | `; 50 | 51 | export default graphql(CursosQuery, { 52 | options: { 53 | pollInterval: 2000, 54 | }, 55 | })(App); 56 | -------------------------------------------------------------------------------- /src/schemas/resolvers/BuildMutation.js: -------------------------------------------------------------------------------- 1 | class BuildMutation { 2 | /** 3 | * Configuracion inicial 4 | * 5 | * @param {Object} modelo Modelo de la tabla. 6 | * @param {Object} params Campos. 7 | * 8 | * @return self Fluent interface 9 | */ 10 | static query(modelo, params) { 11 | this.modelo = modelo; 12 | this.params = Object.values(params); 13 | return this; 14 | } 15 | 16 | /** 17 | * Permite borrar un registro. 18 | * 19 | * @return entidad. 20 | */ 21 | static get delete() { 22 | return ( 23 | this.modelo 24 | .query() 25 | .findById(...this.params) 26 | .then(data => this.modelo 27 | .query() 28 | .deleteById(...this.params) 29 | .then((filas) => { 30 | if (filas > 0) return data; 31 | throw new Error(`El registro con el id ${this.params[0]} no se puede elmiar`); 32 | })) 33 | ); 34 | } 35 | 36 | /** 37 | * Permite agregar un nuevo registro. 38 | * 39 | * @return entidad. 40 | */ 41 | static get save() { 42 | return ( 43 | this.modelo 44 | .query() 45 | .insert(...this.params) 46 | ); 47 | } 48 | 49 | /** 50 | * Permite editar un nuevo registro. 51 | * 52 | * @return entidad. 53 | */ 54 | static get edit() { 55 | return ( 56 | this.modelo 57 | .query() 58 | .patchAndFetchById(...this.params) 59 | ); 60 | } 61 | } 62 | 63 | export default BuildMutation; 64 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "", 3 | "dependencies": { 4 | "apollo-boost": "0.1.15", 5 | "apollo-server-express": "2.0.4", 6 | "axios": "^0.18.0", 7 | "body-parser": "1.18.3", 8 | "casual": "1.5.19", 9 | "express": "4.16.3", 10 | "graphql": "^14.0.0", 11 | "graphql-tag": "2.9.2", 12 | "graphql-tools": "3.1.1", 13 | "json-server": "^0.14.0", 14 | "knex": "0.15.2", 15 | "objection": "1.2.3", 16 | "react": "16.4.2", 17 | "react-apollo": "2.1.11", 18 | "react-dom": "16.4.2", 19 | "sqlite3": "4.0.2" 20 | }, 21 | "description": "Practica con GQL", 22 | "devDependencies": { 23 | "babel-cli": "6.26.0", 24 | "babel-minify": "0.4.3", 25 | "babel-plugin-inline-json-import": "0.2.1", 26 | "babel-preset-env": "1.7.0", 27 | "babel-preset-stage-1": "6.24.1", 28 | "babel-register": "6.26.0", 29 | "nodemon": "1.18.3", 30 | "react-scripts": "1.1.5" 31 | }, 32 | "license": "MIT", 33 | "main": "index.js", 34 | "name": "GQL", 35 | "private": false, 36 | "repository": "git@konami12:OrcaPracticas/GQL.git", 37 | "scripts": { 38 | "start:gql": "nodemon ./launcher", 39 | "start:app": "react-scripts start", 40 | "start:json": "json-server --watch ./src/DB/db.json --port 8081", 41 | "db:migrate": "knex migrate:latest --knexfile=src/DB/knexfile.js", 42 | "db:seed": "knex seed:run --knexfile=src/DB/knexfile.js" 43 | }, 44 | "proxy": { 45 | "/api": { 46 | "target": "http://localhost:8080/graphql" 47 | } 48 | }, 49 | "version": "1.0.0" 50 | } 51 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 23 | React App 24 | 25 | 26 | 29 |
30 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/schemas/schema.js: -------------------------------------------------------------------------------- 1 | // Importa ApolloServer que permite integrar los resolvers y typedef 2 | // importa gql el cual permite interpretar el schema. 3 | import { ApolloServer, gql } from "apollo-server-express"; 4 | 5 | import resolvers from "./resolvers"; 6 | import { Curso, Profesor } from "./schemas"; 7 | 8 | 9 | // Creacion del schema, es importante declarar 10 | // el Query root ya que este indica el endpoint 11 | const ROOT_QUERY = gql` 12 | # **Busqueda 13 | union ResultadoBusqueda = Profesor | Curso 14 | 15 | type User { 16 | id: ID! 17 | name: String 18 | age: Int 19 | email: String 20 | } 21 | 22 | # **Root type Query** _endpoint_ principal 23 | type Query { 24 | user: [User] 25 | cursos: [Curso] 26 | profesores: [Profesor] 27 | comentarios: [Comentario] 28 | curso(id: Int): Curso 29 | profesor(id: Int): Profesor 30 | buscar(query: String!): [ResultadoBusqueda] 31 | findUSer(id: Int): User 32 | } 33 | # **Mutaciones disponibles dentro del proyecto 34 | type Mutation { 35 | profesorAdd(profesor: newProfesor): Profesor 36 | profesorEdit(id: Int!, profesor: editProfesor): Profesor 37 | profesorDelete(id: Int!): Profesor 38 | cursoAdd(curso: newCurso): Curso 39 | cursoEdit(id: Int!, curso: editCurso): Curso 40 | cursoDelete(id: Int!): Curso 41 | comentarioAdd(comentario: newComentario): Comentario 42 | comentarioEdit(id: Int!, comentario: editComentario): Comentario 43 | comentarioDelete(id: Int!): Comentario 44 | } 45 | `; 46 | 47 | /** 48 | * Permite que el esquema declarado se tradusca 49 | * a un esquema de GQL 50 | */ 51 | const SCHEMA = new ApolloServer({ 52 | // Referencia al schema creados 53 | typeDefs: [ 54 | ROOT_QUERY, 55 | Curso, 56 | Profesor, 57 | ], 58 | // Referencia a los resolvers. 59 | resolvers, 60 | formatError: error => ({ 61 | errorCode: "JMZ-DB", 62 | name: error.name, 63 | mensaje: error.message, 64 | }), 65 | }); 66 | 67 | export default SCHEMA; 68 | -------------------------------------------------------------------------------- /src/schemas/resolvers/resolver.js: -------------------------------------------------------------------------------- 1 | // Importacion de modelos referentes a la base de datos. 2 | import { 3 | ModeloCurso, ModeloProfesor, 4 | ModeloComentario, ModeloUser, 5 | } from "../../DB/models"; 6 | import BuildMutation from "./BuildMutation"; 7 | 8 | /** 9 | * Declarando la capa de datos que utilizara para resoler los querys. 10 | */ 11 | const RESOLVERS = { 12 | Query: { 13 | // permite extraer los datos de la DB y 14 | // con eager se le indica que relaciones tiene cada tabla. 15 | user: () => ModeloUser.list(), 16 | cursos: () => ModeloCurso.query().eager("[profesor, comentarios]"), 17 | profesores: () => ModeloProfesor.query().eager("cursos"), 18 | comentarios: () => ModeloComentario.query().eager(), 19 | curso: (rootValue, args) => ModeloCurso.query().findById(args.id), 20 | profesor: (rootValue, args) => ModeloProfesor.query().findById(args.id), 21 | buscar: (_, args) => { 22 | return [ 23 | ModeloProfesor.query().findById(3), 24 | ModeloCurso.query().findById(2), 25 | ]; 26 | }, 27 | findUSer: (_, args) => ModeloUser.find(args.id), 28 | }, 29 | ResultadoBusqueda: { 30 | __resolveType: object => ((object.nombre) ? "Profesor" : "Curso"), 31 | }, 32 | Mutation: { 33 | profesorAdd: (_, args) => BuildMutation.query(ModeloProfesor, args).save, 34 | profesorEdit: (_, args) => BuildMutation.query(ModeloProfesor, args).edit, 35 | profesorDelete: (_, args) => BuildMutation.query(ModeloProfesor, args).delete, 36 | cursoAdd: (_, args) => BuildMutation.query(ModeloCurso, args).save, 37 | cursoEdit: (_, args) => BuildMutation.query(ModeloCurso, args).edit, 38 | cursoDelete: (_, args) => BuildMutation.query(ModeloCurso, args).delete, 39 | comentarioAdd: (_, args) => BuildMutation.query(ModeloComentario, args).save, 40 | comentarioEdit: (_, args) => BuildMutation.query(ModeloComentario, args).edit, 41 | comentarioDelete: (_, args) => BuildMutation.query(ModeloComentario, args).delete, 42 | }, 43 | }; 44 | 45 | export default RESOLVERS; 46 | -------------------------------------------------------------------------------- /src/app/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | // In production, we register a service worker to serve assets from local cache. 2 | 3 | // This lets the app load faster on subsequent visits in production, and gives 4 | // it offline capabilities. However, it also means that developers (and users) 5 | // will only see deployed updates on the "N+1" visit to a page, since previously 6 | // cached resources are updated in the background. 7 | 8 | // To learn more about the benefits of this model, read https://goo.gl/KwvDNy. 9 | // This link also includes instructions on opting out of this behavior. 10 | 11 | export default function register() { 12 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 13 | window.addEventListener('load', () => { 14 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 15 | navigator.serviceWorker 16 | .register(swUrl) 17 | .then(registration => { 18 | registration.onupdatefound = () => { 19 | const installingWorker = registration.installing; 20 | installingWorker.onstatechange = () => { 21 | if (installingWorker.state === 'installed') { 22 | if (navigator.serviceWorker.controller) { 23 | // At this point, the old content will have been purged and 24 | // the fresh content will have been added to the cache. 25 | // It's the perfect time to display a "New content is 26 | // available; please refresh." message in your web app. 27 | console.log('New content is available; please refresh.'); 28 | } else { 29 | // At this point, everything has been precached. 30 | // It's the perfect time to display a 31 | // "Content is cached for offline use." message. 32 | console.log('Content is cached for offline use.'); 33 | } 34 | } 35 | }; 36 | }; 37 | }) 38 | .catch(error => { 39 | console.error('Error during service worker registration:', error); 40 | }); 41 | }); 42 | } 43 | } 44 | 45 | export function unregister() { 46 | if ('serviceWorker' in navigator) { 47 | navigator.serviceWorker.ready.then(registration => { 48 | registration.unregister(); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/schemaMocks/schema.js: -------------------------------------------------------------------------------- 1 | // Importa ApolloServer que permite integrar los resolvers y typedef 2 | // importa gql el cual permite interpretar el schema. 3 | import { ApolloServer, gql } from "apollo-server-express"; 4 | 5 | // mockups 6 | import { MockComentarios, MockCursos, MockProfesores } from "./mockups"; 7 | import { CasualComentarios, CasualCursos, CasualProfesores } from "./mockupsCasual"; 8 | 9 | // Creacion del schema, es importante declarar 10 | // el Query root ya que este indica el endpoint 11 | const TYPE_DEF = gql` 12 | # Entidad **Curso** dentro del sistema 13 | type Curso { 14 | id: ID! 15 | titulo: String! 16 | descripcion: String! 17 | profesor: Profesor 18 | # Al utilizar @deprecated permite indicar que campos podemos eliminar en un futuro 19 | raiting: Float @deprecated(reason: "No me interesa el **raiting** en el curso") 20 | comentarios: [Comentario] 21 | } 22 | 23 | # Entidad **Profesor** dentro del sistema 24 | type Profesor { 25 | id: ID! 26 | nombre: String! 27 | nacionalidad: String! 28 | genero: Genero 29 | cursos: [Curso] 30 | } 31 | 32 | # Enumerador de **Genero** 33 | enum Genero { 34 | MASCULINO 35 | FEMENINO 36 | } 37 | 38 | # Entidad **Comentario** dentro del sistema 39 | type Comentario { 40 | id: ID! 41 | nombre: String! 42 | cuerpo: String! 43 | } 44 | 45 | # **Root type Query** _endpoint_ principal 46 | type Query { 47 | cursos: [Curso] 48 | profesores: [Profesor] 49 | curso(id: Int): Curso 50 | profesor(id: Int): Profesor 51 | } 52 | `; 53 | 54 | /** 55 | * Declarando la capa de datos que utilizara resoler. 56 | */ 57 | const RESOLVERS = { 58 | Query: { 59 | cursos: () => MockCursos, 60 | }, 61 | Curso: { 62 | profesor: () => MockProfesores, 63 | comentarios: () => MockComentarios, 64 | }, 65 | Profesor: { 66 | cursos: () => MockCursos, 67 | genero: () => "MASCULINO", 68 | }, 69 | }; 70 | 71 | /** 72 | * Permite que el esquema declarado se tradusca 73 | * a un esquema de GQL 74 | */ 75 | const SCHEMA = new ApolloServer({ 76 | // Referencia al schema creados 77 | typeDefs: TYPE_DEF, 78 | // Referencia a los resolvers. 79 | resolvers: RESOLVERS, 80 | formatError: error => ({ 81 | errorCode: "JMZ-mocks", 82 | name: error.name, 83 | mensaje: error.message, 84 | }), 85 | mocks: { 86 | Curso: () => CasualCursos, 87 | Profesor: () => CasualProfesores, 88 | Comentario: () => CasualComentarios, 89 | }, 90 | }); 91 | 92 | export default SCHEMA; 93 | -------------------------------------------------------------------------------- /src/DB/db.json: -------------------------------------------------------------------------------- 1 | { 2 | "users": [ 3 | { 4 | "id": 0, 5 | "age": 31, 6 | "name": "Peck Montoya", 7 | "email": "peckmontoya@qualitex.com", 8 | "friends": [ 9 | { 10 | "id": 1 11 | }, 12 | { 13 | "id": 2 14 | } 15 | ] 16 | }, 17 | { 18 | "id": 1, 19 | "age": 36, 20 | "name": "Renee Herman", 21 | "email": "reneeherman@qualitex.com", 22 | "friends": [ 23 | { 24 | "id": 0 25 | }, 26 | { 27 | "id": 2 28 | } 29 | ] 30 | }, 31 | { 32 | "id": 2, 33 | "age": 23, 34 | "name": "Sheena Madden", 35 | "email": "sheenamadden@qualitex.com", 36 | "friends": [ 37 | { 38 | "id": 0 39 | }, 40 | { 41 | "id": 1 42 | } 43 | ] 44 | }, 45 | { 46 | "id": 3, 47 | "age": 22, 48 | "name": "Shari Justice", 49 | "email": "sharijustice@qualitex.com", 50 | "friends": [ 51 | { 52 | "id": 4 53 | } 54 | ] 55 | }, 56 | { 57 | "id": 4, 58 | "age": 38, 59 | "name": "Eaton Horton", 60 | "email": "eatonhorton@qualitex.com", 61 | "friends": [ 62 | { 63 | "id": 3 64 | } 65 | ] 66 | }, 67 | { 68 | "id": 5, 69 | "age": 18, 70 | "name": "Lois Best", 71 | "email": "loisbest@qualitex.com", 72 | "friends": [ 73 | { 74 | "id": 6 75 | }, 76 | { 77 | "id": 7 78 | }, 79 | { 80 | "id": 8 81 | }, 82 | { 83 | "id": 9 84 | } 85 | ] 86 | }, 87 | { 88 | "id": 6, 89 | "age": 40, 90 | "name": "Schroeder Stafford", 91 | "email": "schroederstafford@qualitex.com", 92 | "friends": [ 93 | { 94 | "id": 5 95 | } 96 | ] 97 | }, 98 | { 99 | "id": 7, 100 | "age": 33, 101 | "name": "Hanson Collins", 102 | "email": "hansoncollins@qualitex.com", 103 | "friends": [ 104 | { 105 | "id": 5 106 | } 107 | ] 108 | }, 109 | { 110 | "id": 8, 111 | "age": 21, 112 | "name": "Walton York", 113 | "email": "waltonyork@qualitex.com", 114 | "friends": [ 115 | { 116 | "id": 5 117 | } 118 | ] 119 | }, 120 | { 121 | "id": 9, 122 | "age": 38, 123 | "name": "Walter Manning", 124 | "email": "waltermanning@qualitex.com", 125 | "friends": [ 126 | { 127 | "id": 5 128 | }, 129 | { 130 | "id": 10 131 | } 132 | ] 133 | }, 134 | { 135 | "id": 10, 136 | "age": 26, 137 | "name": "Carmen Butler", 138 | "email": "carmenbutler@qualitex.com", 139 | "friends": [ 140 | { 141 | "id": 9 142 | } 143 | ] 144 | }, 145 | { 146 | "id": 13, 147 | "name": "Indigo Montoya", 148 | "age": 31, 149 | "email": "indigomontoya@gmail.com", 150 | "friends": [ 151 | { 152 | "id": 1 153 | }, 154 | { 155 | "id": 2 156 | } 157 | ] 158 | } 159 | ] 160 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cursó de graphql 2 | 3 | _Repositorio del curso sobre **graphql**, el repositorio consta de tags que permite identificar puntos clave sobre el curos._ 4 | 5 | ## Comenzando 🚀 6 | 7 | _Estas son intrucciones que nos permitirán obtener una copia del proyecto y su funcionamiento en tu máquina local para própositos de desarrollo y pruebas._ 8 | 9 | Sigue los pasos que a continuación se indican para poder desplegar el proyecto. 10 | 11 | ### Pre-requisitos 📋 12 | 13 | Para poder iniciar el proyecto necesitamos contar con una instalación. 14 | 15 | - [NodeJS](https://nodejs.org/en/download/): es un entorno en tiempo de ejecución multiplataforma, de código abierto, para la capa del servidor basado en el lenguaje de programación ECMAScript, 16 | 17 | - [Yarn](https://yarnpkg.com/en/docs/install#debian-stable): Yarn es un nuevo tipo de instalador de paquetes JavaScript y gestor de dependencias lanzado por Facebook. 18 | 19 | - [SqlLite](https://www.sqlite.org/download.html): SQLite es un sistema de gestión de bases de datos relacional compatible con ACID, contenida en una relativamente pequeña ​ biblioteca escrita en C 20 | 21 | > 📝 Nota: Se recomienda usar las verciones **LTS** de **nodejs**. 22 | 23 | Al contar con los requisitos solicitados se procederá a la instalación del proyecto realizado durante el cursó. 24 | 25 | ### Instalación 🔧 26 | 27 | #### Paso 1 28 | 29 | Clonar el repositorio. 30 | 31 | ```bash 32 | $ git clone git@github.com:OrcaPracticas/GQL.git 33 | ``` 34 | Al terminar de clonar el repositorio tendremos una carpeta llamada **GQL**. 35 | 36 | #### Paso 2 37 | 38 | Colocarnos en la carpeta que nos genero la clonación del repositorio. 39 | 40 | ```bash 41 | $ cd ./GQL 42 | ``` 43 | 44 | #### Paso 3 45 | 46 | Al estar en el directorio procederemos a ejecutar la instalación de las dependencias. 47 | 48 | ``` 49 | $ yarn install 50 | ``` 51 | también podemos utilizar el comando 52 | 53 | ``` 54 | $ npm i 55 | ``` 56 | 57 | > 📝 Nota: Realmente **yarn** puede ser opcional, pero seria bueno que lo revisaras en el caso de que no lo conoscas. 58 | 59 | al terminar la instalción de las dependencias procederemos a correr la aplicación. 60 | 61 | #### Paso 4 62 | 63 | Antes de poder correr la aplicación es necesario generar la base de datos para esto ejecutaremos le comando siguiente. 64 | 65 | ```bash 66 | $ yarn db:migrate 67 | ``` 68 | o 69 | ```bash 70 | $ npm run db:migrate 71 | ``` 72 | 73 | el comando anterior nos permite crear las tablas para la Base de Datos. 74 | 75 | #### Paso 5 76 | 77 | Llenar las tablas con data **Falsa** para esto ejecutaremos el comando siguiente. 78 | 79 | ```bash 80 | $ yarn db:seed 81 | ``` 82 | o 83 | ```bash 84 | $ npm run db:seed 85 | ``` 86 | 87 | Hasta este punto contamos con nuestra base de datos y sus correspondientes **Tablas** cargadas con data **Falsa** 88 | 89 | #### Paso 6 90 | 91 | Procederemos a levantar nuestro servidor de **graphql** para esto ejecutaremos el comando siguiente. 92 | 93 | ```bash 94 | $ yarn start:gql 95 | ``` 96 | o 97 | 98 | ```bash 99 | $ npm start:gql 100 | ``` 101 | 102 | > 📝 Nota: El servidor se ejecuta en la url http://localhost:8080/graphql. 103 | 104 | podremos ejecutar una pureba en nuestro servidor para verificar que todo corre de manera correcta podemos colocar el siguiente comando. 105 | 106 | ```JavaScript 107 | { 108 | cursos { 109 | id 110 | titulo 111 | descripcion 112 | profesor { 113 | id 114 | nombre 115 | } 116 | comentarios { 117 | nombre 118 | cuerpo 119 | } 120 | } 121 | } 122 | ``` 123 | pulsamo el boton **play** esto ya nos tiene que estar lanzando resultados. 124 | 125 | #### Paso 7 126 | 127 | Procederemos a levantar al **App** que se conectara al servidor de **GQL**, para esto ejecutaremos el comando siguiente, (Es necesario abrir una nueva venta en la terminal). 128 | 129 | ```bash 130 | $ yarn start:app 131 | ``` 132 | o 133 | 134 | ```bash 135 | $ npm run start:app 136 | ``` 137 | > 📝 Nota: El servidor se ejecuta en la url http://localhost:3000, 138 | 139 | Al termino de los pasos anteriores tendremos el proyecto en ejecución. 140 | 141 | #### Paso 8 142 | 143 | También podemos probar la consulta a un **API REST** , para esto ejecutaremos el comando siguiente, (Es necesario abrir una nueva venta en la terminal). 144 | 145 | ```bash 146 | $ yarn start:json 147 | ``` 148 | o 149 | 150 | ```bash 151 | $ npm run start:json 152 | ``` 153 | > 📝 Nota: El servidor se ejecuta en la url http://localhost:8081, 154 | 155 | En el servidor de *GQL* podemos ejecutar el Comenzando 156 | 157 | ```javaScript 158 | { 159 | findUSer(id: 2) { 160 | id 161 | name 162 | age 163 | email 164 | } 165 | } 166 | ``` 167 | 168 | ## Deployment 📦 169 | 170 | Es importante mencionar que el proyecto cuenta con **tags** que facilita el movimiento entre las etapas del desarrollo realizado durante el curso. 171 | 172 | * **[Configuración inicial](https://github.com/OrcaPracticas/GQL/tree/Inicial)** 173 | * **[Creacion del Schema](https://github.com/OrcaPracticas/GQL/tree/Schema)** 174 | * **[Creacion de Resolvers](https://github.com/OrcaPracticas/GQL/tree/Resolvers)** 175 | * **[Creacion de Mocks Dimanicos](https://github.com/OrcaPracticas/GQL/tree/Mocks)** 176 | * **[Integracion de base de datos](https://github.com/OrcaPracticas/GQL/tree/BD)** 177 | * **[Modularización](https://github.com/OrcaPracticas/GQL/tree/Modularizacion)** 178 | * **[Busqueda y manejo de errores](https://github.com/OrcaPracticas/GQL/tree/Busqueda)** 179 | * **[Integración con React](https://github.com/OrcaPracticas/GQL/tree/React)** 180 | * **[Integración con apollo](https://github.com/OrcaPracticas/GQL/tree/Apollo)** 181 | * **[Integración con Api Rest](https://github.com/OrcaPracticas/GQL/tree/api)** 182 | 183 | Cada una de las faces correspondientes al desarrollo generado durante el cursó, cada fase cuenta con su **Release** el cual tiene realción con un **PullRequest** que a su vez tiene relación con un **Milestones** el cuales tienen realación con **issues** y estos tienen relacionados los **commits** generados. 184 | 185 | ## Construido con 🛠️ 186 | 187 | Esta practica se construyo utilizando las siguientes tecnologias. 188 | 189 | [![Body-parser](https://img.shields.io/badge/dependencies-Body--parser-8000ff.svg)](https://www.npmjs.com/package/body-parser) 190 | [![Casual](https://img.shields.io/badge/dependencies-Casual-8000ff.svg)](https://www.npmjs.com/package/casual) 191 | [![Knex](https://img.shields.io/badge/dependencies-Knex-8000ff.svg)](https://www.npmjs.com/package/knex) 192 | [![Objection](https://img.shields.io/badge/dependencies-Objection-8000ff.svg)](https://www.npmjs.com/package/objection) 193 | [![Sqlite3](https://img.shields.io/badge/dependencies-Sqlite3-8000ff.svg)](https://www.npmjs.com/package/sqlite3) 194 | [![Apollo-Boost](https://img.shields.io/badge/dependencies-Apollo--Boost-blue.svg)](https://www.npmjs.com/package/apollo-boost) 195 | [![Apollo-Server-Express](https://img.shields.io/badge/dependencies-Apollo--Server--Express-blue.svg)](https://www.npmjs.com/package/apollo-server-express) 196 | [![React-Apollo](https://img.shields.io/badge/dependencies-React--Apollo-blue.svg)](https://www.npmjs.com/package/react-apollo) 197 | [![React](https://img.shields.io/badge/dependencies-React-blue.svg)](https://www.npmjs.com/package/react) 198 | [![Graphql](https://img.shields.io/badge/dependencies-Graphql-ff69b4.svg)](https://www.npmjs.com/package/graphql) 199 | [![Express](https://img.shields.io/badge/dependencies-Express-009933.svg)](https://www.npmjs.com/package/express) 200 | 201 | ## Autores ✒️ 202 | 203 | * **[Jorge Mendez Ortega](https://github.com/OrcaPracticas)** 204 | 205 | ## Licencia 📄 206 | 207 | Este proyecto está bajo la Licencia MIT mira el archivo [LICENSE.md](LICENSE) para detalles. 208 | 209 | ⌨️ con ❤️ por [Konami12](https://github.com/konami12) 😊 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | --------------------------------------------------------------------------------