Ejercicio 8.

Ejercicio 8.

8.1 Defininión del esquema

1. Clonar el repositorio

En el directorio c:\sc, clonar el repositorio de inicio de la aplicación:

cd c:\sc
git clone https://github.com/fararoni/curso.graphql.odyssey-lift-off-part1.git

2. Ejecutar la aplicación de lado del servidor

En una ventana CMD, ir al directorio

cd c:\sc\curso.graphql.odyssey-lift-off-part1\server

Ejecute los siguientes comandos para instalar las dependencias y ejecutar la aplicación:

npm install
npm start

únicamente verá un mensaje final “nodemon”

3. Ejecutar la aplicación de lado del cliente

En otra ventana CMD, ir al directorio

cmd
cd C:\sc\curso.graphql.odyssey-lift-off-part1\client

Ejecute los siguientes comandos para instalar las dependencias y ejecutar la aplicación:

npm install
npm start

Abra el navegador e ingrese a la siguiente dirección:

http://127.0.0.1:3000/

4. Analisis de datos

Según la maqueta, parece que necesitaremos la siguiente información para cada Ruta de aprendizaje:

  • Título
  • Imagen en miniatura
  • Duración (duración estimada)
  • Recuento de módulos
  • Nombre del autor
  • Foto del autor

Un tipo se declara usando la palabra reservada type, seguida del nombre del tipo (la mejor práctica es usar un formatpo PascalCase y luego abrimos corchetes para contener los campos:

type SpaceCat {
  # Los campos van aquí
}

Quedando así

type SpaceCat {
  age: Int
  missions: [Mission]
}

Define un objeto tipo SpaceCat con los siguientes campos: name de tipo String(no nulo), age de tipo Int y missions de tipo List de Mission.

type SpaceCat  {
  name: String!
  age: Int
  missions : [Mission]
}

Documenta tu esquema como mejor práctica y para permitir que herramientas como Apollo Studio Explorer orienten a los usuarios a entender lo que hace su código.

type SpaceCat  {
  name: String!
  age: Int
  missions : [Mission]
}

5. Definición del esquema

Abrir una ventana CMD, ir al directorio del server y abrir Visual Studio code

cmd
cd C:\sc\curso.graphql.odyssey-lift-off-part1\server
code .
  • Crear un nuevo archivo en la carpeta “src” con el nombre “schema.js”.
  • Desde visual estudio abrir una consola en el directorio server, asegurarse que sea de tipo CMD, ejecutar lo siguiente:
    npm install @apollo/server graphql graphql-tag
    

    Ahora en el archivo schema.js, cargar la plantilla literal gql de graphql-tag:

    const gql = require("graphql-tag");
    

Declarar una constante typeDefs (abreviatura de “definiciones de tipo”), a la que se le asignará la plantilla gql donde irán nuestras definiciones

const typeDefs = gql`
# Schema definitions go here

`;

module.exports = typeDefs;

Agregar el tipo Track

const typeDefs = gql`
"Un Track es un grupo de módulos que enseñan acerca de un tópico"
type Track {
	id: ID!
	title: String!
	author: Author!
	thumbnail: String
	length: Int
	modulesCount: Int
	}
`;

Agregar el tipo Author

const typeDefs = gql`
"Un Track es un grupo de módulos que enseñan acerca de un tópico"
type Track {
	id: ID!
	title: String!
	author: Author!
	thumbnail: String
	length: Int
	modulesCount: Int
	}
	"Autor de un Track completo o de un Módulo "
	type Author {
		id: ID!
		name: String!
		photo: String
	}
`;

6. Definición de la consulta en el esquema

Obtener la lista de rutas (Tracks) para nuestra página de inicio

type Query {
  " Obtener una lista de rutas para la cuadrícula de la página de inicio"
  tracksForHome: [Track!]!
}

7. Version completa del archivo schema.js

const gql = require("graphql-tag");
const typeDefs = gql`
type Query {
    "Obtener un arreglo de Tracks para la página de inicio"
    tracksForHome: [Track!]!
  }

"Un Track es un grupo de módulos que enseñan acerca de un tópico"
type Track {
	id: ID!
	title: String!
	author: Author!
	thumbnail: String
	length: Int
	modulesCount: Int
	}
	"Autor de un Track completo o de un Módulo "
	type Author {
		id: ID!
		name: String!
		photo: String
	}
`;
module.exports = typeDefs;

8.2 Creación del backend

1. Importar las librerías del servidor

Desde Visual Studio code, en la carpeta server/src/, abra el archivo index.js. Agregar al principio

const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const typeDefs = require("./schema");

Configuremos una función async llamada startApolloServer, para crear una instancia de la clase ApolloServer y le pasaremos nuestro objeto typeDefs en sus parámetros de objetos

async function startApolloServer() {
	const server = new ApolloServer({ typeDefs });
}

Nota: En este código estamos usando la notación de propiedad abreviada con claves implícitas, porque hemos nombrado nuestra constante que coincide con su atributo correspondiente ( typeDefs).

Para iniciar el servidor, usaremos la función startStandaloneServer, pasándola al server que acabamos de inicializar.

async  function  startApolloServer() {
	const  server  =  new  ApolloServer({ typeDefs });
	startStandaloneServer(server);
}

La función startStandaloneServer devuelve una Promise, por lo que esperaremos los await de esa llamada y extraeremos la propiedad url del resultado.

async function startApolloServer() {
	const server = new ApolloServer({ typeDefs });
	const { url } = await startStandaloneServer(server);
}

Agregar un mensaje que indique que ya se levantó el servidor

async function startApolloServer() {
  const server = new ApolloServer({ typeDefs });
  const { url } = await startStandaloneServer(server);
  console.log(`
    🚀  Server is running!
    📭  Query at ${url}
  `);
}

Finalmente, llamar a la startApolloServerfunción al final del archivo.

startApolloServer();

Comparar la versión completa

const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const typeDefs = require("./schema");

async function startApolloServer() {
    const server = new ApolloServer({ typeDefs });
    const { url } = await startStandaloneServer(server);
    console.log(`
      🚀  Server is running!
      📭  Query at ${url}
    `);
  }
  
  startApolloServer();

Levantar el servidor Desde la terminal, iniciaremos nuestro servidor con npm run start desde la carpeta server

npm start

2. Sandbox explorer

  • Navegar en los apartados del explorar de Apollo

3 Consultas

Guardar una operación con el nombre GetTracks que nos va a servir posteriormente.

4 Datos mock

El explorador no puede entregar datos porque no los tiene, vamos a generar datos simulados. En la consola de visual studio, interrumpir la ejecución del servidor y agregar las siguientes paquetes

ctrl + c
npm install @graphql-tools/mock @graphql-tools/schema

Agregar al principio del archivo index.js

const { addMocksToSchema } = require("@graphql-tools/mock"); const { makeExecutableSchema } = require("@graphql-tools/schema");

Modificar la constante server para que reciba los datos mock

const server = new ApolloServer({
  schema: addMocksToSchema({
    schema: makeExecutableSchema({ typeDefs }),
  }),
});

Levanta nuevamente el servidor y revisa los datos en el explorador

Para que no muestre datos tan genéricos, agregar lo siguiente debajo de la línea

const  typeDefs  =  require("./schema");
const mocks = {
  Track: () => ({
    id: () => "track_01",
    title: () => "Astro Kitty, Space Explorer",
    author: () => {
      return {
        name: "Grumpy Cat",
        photo:
          "https://res.cloudinary.com/dety84pbu/image/upload/v1606816219/kitty-veyron-sm_mctf3c.jpg",
      };
    },
    thumbnail: () =>
      "https://res.cloudinary.com/dety84pbu/image/upload/v1598465568/nebula_cat_djkt9r.jpg",
    length: () => 1210,
    modulesCount: () => 6,
  }),
};

Y modificar la invocación del server

const server = new ApolloServer({
  schema: addMocksToSchema({
    schema: makeExecutableSchema({ typeDefs }),
    mocks,
  }),
});

8.3 Frontend

1. Abrir el proyecto frontend react

Abrir una Ventana CMD, ir al directorio C:\sc\curso.graphql.odyssey-lift-off-part1\client y abrir Visual Studio Code:.

	cmd
	cd C:\sc\curso.graphql.odyssey-lift-off-part1\client
	code .

Desde visual studio, abrir una terminal CMD y ejecutar

	npm start

Abrir el archivo raíz de nuestra aplicación React (client/src/index.js) , debe verse de la siguiente forma

import React from 'react';
import { createRoot } from 'react-dom/client'
import GlobalStyles from './styles';
import Pages from './pages';

const root = createRoot(document.getElementById('root'));

root.render(
  <React.StrictMode>
    <GlobalStyles />
    <Pages />
  </React.StrictMode>
);

Interrumpa la ejecución del cliente y ejecute el siguiente comando

C:\sc\curso.graphql.odyssey-lift-off-part1\client>npm install graphql @apollo/client

Imporar ApolloClient en el frontend

Importar en src/index.js los tres símbolos que necesitamos del paquete @apollo/client

import { ApolloClient, InMemoryCache, ApolloProvider } from "@apollo/client";

Crear uns instancia de ApolloClient

const client = new ApolloClient({
  uri: "http://localhost:4000",
  cache: new InMemoryCache(),
});

Hacer que Apollo Client quede disponible para todo el arbol de componentes de React

root.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <GlobalStyles />
      <Pages />
    </ApolloProvider>
  </React.StrictMode>
);

Reiniciar el client

npm start

Se ve igual, porque aun no se le envia una consulta

Enviar query

Abrir C:\sc\curso.graphql.odyssey-lift-off-part1\client\src\pages\tracks.js e importar la plantilla gql al principio del archivo

import { gql } from "@apollo/client";

A continuación definir la consulta TRACKS

const TRACKS = gql`
  # Query goes here
`;

Abrir el explorador en el server y recuperar la consulta guardada TracksForHome. Actualizar con la consulta

const TRACKS = gql`
  query GetTracks {
    tracksForHome {
      id
      title
      thumbnail
      length
      modulesCount
      author {
        id
        name
        photo
      }
    }
  }
`;

hook useQuery

Importar useQuery desde el paquete @apollo/client en src/pages/tracks.js.

import { useQuery, gql } from "@apollo/client";

Y en el componente Tracks las siguientes constantes desestructuradas

 const Tracks = () => {
  const { loading, error, data } = useQuery(TRACKS);
  if (loading) return "Loading...";
  if (error) return `Error! ${error.message}`;
  return <Layout grid>{JSON.stringify(data)}</Layout>;
};

Actualiza el servidor, debe mostrar datos. Aunque no están amigables El código queda de la siguiente forma

import React from 'react';
import { Layout } from '../components';
import { useQuery, gql } from "@apollo/client";

const TRACKS = gql`
  query GetTracks {
    tracksForHome {
      id
      title
      thumbnail
      length
      modulesCount
      author {
        id
        name
        photo
      }
    }
  }
`;

/**
 * Tracks Page is the Catstronauts home page.
 * We display a grid of tracks fetched with useQuery with the TRACKS query
 */
const Tracks = () => {
  const { loading, error, data } = useQuery(TRACKS);
  if (loading) return "Loading...";
  if (error) return `Error! ${error.message}`;
  return <Layout grid>{JSON.stringify(data)}</Layout>;

};
export default Tracks;

Renderizado en las tarjetas

Continuar trabajando en src/pages/tracks.js

import TrackCard from  "../containers/track-card";

Actualizar la siguiente línea

 return <Layout grid>{JSON.stringify(data)}</Layout>;

Por la siguiente

return <Layout grid>
  {data?.tracksForHome?.map((track) => (
    <TrackCard key={track.id} track={track} />
  ))}
</Layout>

Manejo de los resultados

Continuar trabajando en src/pages/tracks.js

import QueryResult from "../components/query-result";

Actualizar la siguiente línea

	if (loading) return "Loading...";
	if (error) return `Error! ${error.message}`;

Por lo siguiente

<QueryResult error={error} loading={loading} data={data}>
  {data?.tracksForHome?.map((track) => (
    <TrackCard key={track.id} track={track} />
  ))}
</QueryResult>

Actualice el navegador y compruebe la salida

	npm start