├── .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 | [](https://www.npmjs.com/package/body-parser)
190 | [](https://www.npmjs.com/package/casual)
191 | [](https://www.npmjs.com/package/knex)
192 | [](https://www.npmjs.com/package/objection)
193 | [](https://www.npmjs.com/package/sqlite3)
194 | [](https://www.npmjs.com/package/apollo-boost)
195 | [](https://www.npmjs.com/package/apollo-server-express)
196 | [](https://www.npmjs.com/package/react-apollo)
197 | [](https://www.npmjs.com/package/react)
198 | [](https://www.npmjs.com/package/graphql)
199 | [](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 |
--------------------------------------------------------------------------------