Cómo obtener metadatos de repositorio con JavaScript a través de la API de Github

Cómo usar la API de recuperación de JavaScript para recuperar metadatos (p. ej., un recuento de estrellas) para un repositorio de la API de Github.

Primeros pasos

Para este tutorial, usaremos CheatCode Next.js Boilerplate como punto de partida para nuestro trabajo. Para comenzar, clonemos una copia del repositorio para ese proyecto:

Terminal

git clone https://github.com/cheatcode/nextjs-boilerplate.git

A continuación, cd en el repositorio e instalar sus dependencias:

Terminal

cd nextjs-boilerplate && npm install

Finalmente, continúe e inicie el servidor de desarrollo:

Terminal

npm run dev

Con eso, estamos listos para comenzar.

Creación de un componente React.js para representar datos de repositorio

Para comenzar, vamos a crear un componente React donde buscaremos y representaremos nuestros datos desde la API de Github. Para nuestro componente, usaremos el patrón de componente de función:

/páginas/index.js

import React from "react";
import StyledIndex from "./index.css";

const Index = () => {
  return (
    <StyledIndex>
      // We'll build out the core of our component here...
    </StyledIndex>
  );
};

Index.propTypes = {};

export default Index;

Debido a que el modelo con el que estamos trabajando se basa en Next.js, para definir nuestro componente, debemos agregarlo al /pages directorio en la raíz del proyecto. Detrás de escena, Next.js creará automáticamente una ruta en nuestra aplicación, en el navegador, que representará el componente que creamos (en este caso, en http://localhost:5000/ mientras estamos creando /pages/index.js ).

Arriba, estamos configurando nuestro componente base <Index /> y exportarlo como default de nuestro archivo (requerido por Next.js). Mirando el código, comenzamos con la creación de un componente con estilo:una forma de agregar estilo a un componente de React mediante la generación dinámica de un componente contenedor, aquí, <StyledIndex />—that has CSS attached to it. Real quick, let's open up that /pages/index.css.js` que se está importando aquí y agregue los estilos que necesitaremos más adelante:

/páginas/index.css.js

import styled from "styled-components";

export default styled.div`
  display: inline-block;
  border: 1px solid #eeeeee;
  padding: 40px;
  margin: 0 auto;
  border-radius: 3px;
  box-shadow: 0px 2px 2px 2px rgba(0, 0, 0, 0.02);

  > h4 {
    font-size: 16px;
    text-transform: uppercase;
    color: #aaaaaa;
  }

  > h4 .fa-star {
    color: #ffcc00;
  }

  > h1,
  > h2,
  > h3,
  > h4,
  > h5,
  > h6 {
    margin: 0;
  }

  > h2 {
    font-size: 48px;
    font-weight: bold;
    margin: 10px 0 5px;
  }

  > p {
    font-size: 20px;
    color: #888888;
    margin: 0;
  }

  > p > a {
    display: inline-block;
    font-size: 16px;
    color: #0099ff;
    margin-top: 10px;
  }

  > div {
    margin-top: 20px;
  }

  > div {
    display: flex;
    align-items: center;
  }

  > div img {
    width: 50px;
    height: 50px;
    border-radius: 50px;
    margin-right: 15px;
  }

  > div a {
    color: #aaaaaa;
  }

  > div h5 {
    margin: 0;
  }

  > div p {
    margin: 0;
  }
`;

Aquí, importamos un objeto styled que contiene una serie de funciones, cada una de las cuales representa un elemento HTML estándar. Cuando se llama a la función, espera una única cadena de JavaScript como argumento que contiene estilos CSS para adjuntar al elemento devuelto por la función.

Usamos el styled.div función, llamándola usando el método abreviado en JavaScript para llamar a una función donde el único argumento pasado es una cadena:llamando a la función, pero en lugar de paréntesis, siga inmediatamente con un par de acentos graves. Entre esos acentos graves, hemos pasado algo de CSS como una cadena (nuevamente, los acentos graves son solo otra forma de crear una cadena en JavaScript) para ayudarnos a diseñar el marcado que agregaremos a nuestro componente a continuación, donde representaremos nuestro repositorio de Github datos.

/páginas/index.js

import React, { useState, useEffect } from "react";
import StyledIndex from "./index.css";

const Index = () => {
  const [loading, setLoading] = useState(true);
  const [repoName, setRepoName] = useState("");
  const [repoDescription, setRepoDescription] = useState("");
  const [repoOwner, setRepoOwner] = useState({
    username: "",
    url: "",
    avatar: "",
  });
  const [repoURL, setRepoURL] = useState("");
  const [repoStars, setRepoStars] = useState(0);

  if (loading) {
    return <div></div>;
  }

  return (
    <StyledIndex>
      <h4>
        <i className="fas fa-star" /> {repoStars} Stars
      </h4>
      <h2>{repoName}</h2>
      <p>{repoDescription}</p>
      <p>
        <a href={repoURL}>{repoURL}</a>
      </p>
      {repoOwner && (
        <div className="owner">
          {repoOwner?.avatar && (
            <img src={repoOwner.avatar} alt={repoOwner.username} />
          )}
          <div>
            <h5>{repoOwner.username}</h5>
            <p>
              <a href={repoOwner.url}>{repoOwner.url}</a>
            </p>
          </div>
        </div>
      )}
    </StyledIndex>
  );
};

Index.propTypes = {};

export default Index;

Desarrollar nuestro componente, porque vamos a depender de una solicitud de API potencialmente lenta, en el sentido general; en condiciones normales, la API es rápida; vamos a poner en estado los datos que queremos mostrar en nuestro componente.

Aquí, debido a que estamos usando el patrón de componente de función (a diferencia del patrón basado en clases), vamos a usar el useState() gancho para definir, establecer y recuperar nuestros diferentes valores de estado.

Para nuestras necesidades, tenemos seis valores que queremos establecer en el estado:

  1. Un loading state para informarnos si completamos la solicitud de API.
  2. El repoName para la URL que solicitamos.
  3. El repoDescription para la URL que solicitamos.
  4. El repoOwner para la URL que solicitamos (compuesta por su nombre de usuario, URL y avatar).
  5. El repoURL para la URL que solicitamos (diferente de la URL de la API que usaremos).
  6. El repoStars para la URL que solicitamos.

Para cada uno de los anteriores, hacemos una llamada a useState() , pasando un valor predeterminado para cada uno y anticipando una matriz de JavaScript a cambio. En esas matrices, esperamos el valor actual de nuestros valores de estado en la primera posición (0 index) y una función setter para nuestros valores de estado en la segunda posición (1 índice).

Para que nuestro código sea más fácil de leer, para acceder a estos valores utilizamos la desestructuración de matrices de JavaScript. Esto nos permite acceder y asignar valores de matriz a las variables simultáneamente. Aquí, por ejemplo, const [repoName, setRepoName] = useState(""); , podría reescribirse como:

const state = useState("");
const repoName = state[0];
const setRepoName = state[1];

Debido a que esto es más detallado, y posiblemente más confuso, usamos el patrón de desestructuración de matriz de JavaScript en su lugar.

Debajo de nuestras llamadas a useState() , nos aseguramos de devolver un <div></div> vacío en caso de que nuestro loading el valor del estado es actualmente true (esto evitará un flasheo innecesario del HTML donde representamos la información de nuestro repositorio antes hemos recibido datos de la API).

Asumiendo loading es falso, pasamos a nuestro return value, aquí tenemos nuestro <StyledIndex /> componente que aprendimos anteriormente envuelto alrededor del marcado que usaremos para representar nuestro componente (para lo que configuramos los estilos antes). No hay nada demasiado especial aquí, solo una interfaz simple estilo tarjeta que muestra nuestros datos.

Ahora viene la parte divertida. A continuación, debemos agregar la capacidad de obtener datos de la API de Github. La buena noticia:no necesitaremos realizar ningún flujo de trabajo de autenticación especial como la URL que queremos usar https://api.github.com/repos/<user>/<repo> es un punto final de API de cara al público.

Uso de la API Fetch para obtener datos de repositorio de la API de Github

Ahora, para que todo esto funcione, queremos implementar nuestra llamada a la API de Github. Para hacerlo, utilizaremos la API de recuperación de JavaScript para realizar nuestras solicitudes HTTP. Esta API está integrada en los navegadores web modernos, por lo que no necesitamos instalar nada adicional.

/páginas/index.js

import React, { useState, useEffect } from "react";
import StyledIndex from "./index.css";

const Index = () => {
  const [loading, setLoading] = useState(true);
  const [repoName, setRepoName] = useState("");
  const [repoDescription, setRepoDescription] = useState("");
  const [repoOwner, setRepoOwner] = useState({
    username: "",
    url: "",
    avatar: "",
  });
  const [repoURL, setRepoURL] = useState("");
  const [repoStars, setRepoStars] = useState(0);

  useEffect(() => {
    fetch(
      `https://api.github.com/repos/cheatcode/nodejs-server-boilerplate`
    ).then(async (response) => {
      // We'll handle the response from the Github API here...
    });
  }, []);

  if (loading) {
    return <div></div>;
  }

  return (
    <StyledIndex>
      ...
    </StyledIndex>
  );
};

Index.propTypes = {};

export default Index;

Entre nuestras llamadas a useState() y nuestro if (loading) condicional, hemos agregado una llamada al useEffect() función gancho, importada desde el react dependencia en la parte superior de nuestro archivo. Esta función nos permite ejecutar código arbitrario cuando nuestro componente se renderiza, así como cuando cambia cualquier dependencia que le indiquemos que observe.

Aquí, nuestro objetivo es ejecutar nuestra solicitud de API tan pronto como se carga nuestra página y luego copiar los datos que obtenemos de la respuesta a esa solicitud en los valores de estado de nuestro componente. Antes de hacer eso, dentro de la función de devolución de llamada que pasamos a useEffect() (el segundo argumento [] puede contener opcionalmente una lista de valores, por ejemplo, accesorios, que obligan a que se active la función de devolución de llamada cuando cambian), estamos configurando nuestra llamada a la API usando el fetch() integrado método que insinuamos anteriormente.

A él, le pasamos el punto final de la API, indicado por el api.github.com part—que queremos que busque para hacer una solicitud y manejar la respuesta. Aquí, https://api.github.com/repos/cheatcode/nodejs-boilerplate nos devolverá los metadatos para CheatCode Node.js Boilerplate. Para ver una vista previa de los datos que esperamos a cambio, visite la URL en su navegador.

En nuestro código, para obtener acceso a esa respuesta, porque estamos usando fetch() , necesitamos especificar cómo queremos obtener nuestros datos a cambio:

/páginas/index.js

import React, { useState, useEffect } from "react";
import StyledIndex from "./index.css";

const Index = () => {
  const [loading, setLoading] = useState(true);
  const [repoName, setRepoName] = useState("");
  const [repoDescription, setRepoDescription] = useState("");
  const [repoOwner, setRepoOwner] = useState({
    username: "",
    url: "",
    avatar: "",
  });
  const [repoURL, setRepoURL] = useState("");
  const [repoStars, setRepoStars] = useState(0);

  useEffect(() => {
    fetch(
      `https://api.github.com/repos/cheatcode/nodejs-server-boilerplate`
    ).then(async (response) => {
      const data = await response.json();

      // We'll copy our data over to state here...
    });
  }, []);

  if (loading) {
    return <div></div>;
  }

  return (
    <StyledIndex>
      ...
    </StyledIndex>
  );
};

Index.propTypes = {};

export default Index;

Dentro del .then() devolución de llamada que hemos encadenado a fetch() (hacemos esto instintivamente como esperamos fetch() para devolver una promesa de JavaScript), para recuperar nuestro cuerpo de respuesta como datos JSON (en este punto, un objeto de JavaScript), llamamos al .json() función que anticipamos en el response objeto pasado a nuestro .then() devolución de llamada.

Porque esperamos esto función—response.json() —para devolver una Promesa también, usamos el patrón async/await para decirle a JavaScript "en este contexto, esperamos usar el await declaración". Declaramos que anteponiendo la palabra clave async al ámbito principal externo (en este caso, la función pasó a .then() ) donde nuestro await se utilizará la declaración. Esto es obligatorio ya que JavaScript arrojará un error de tiempo de ejecución si no lo hacemos .

Con esto, ahora, en nuestro const data variable, deberíamos recuperar los datos para nuestro repositorio. Vamos a agregarlo al estado:

/páginas/index.js

import React, { useState, useEffect } from "react";
import StyledIndex from "./index.css";

const Index = () => {
  const [loading, setLoading] = useState(true);
  const [repoName, setRepoName] = useState("");
  const [repoDescription, setRepoDescription] = useState("");
  const [repoOwner, setRepoOwner] = useState({
    username: "",
    url: "",
    avatar: "",
  });
  const [repoURL, setRepoURL] = useState("");
  const [repoStars, setRepoStars] = useState(0);

  useEffect(() => {
    fetch(
      `https://api.github.com/repos/cheatcode/nodejs-server-boilerplate`
    ).then(async (response) => {
      const data = await response.json();

      if (data && data.name) {
        setRepoName(data.name);
      }

      if (data && data.description) {
        setRepoDescription(data.description);
      }

      if (data && data.owner) {
        setRepoOwner({
          username: data?.owner?.login,
          url: data?.owner?.url,
          avatar: data?.owner?.avatar_url,
        });
      }

      if (data && data.html_url) {
        setRepoURL(data.html_url);
      }

      if (data && data.stargazers_count) {
        setRepoStars(data.stargazers_count);
      }

      setLoading(false);
    });
  }, []);

  if (loading) {
    return <div></div>;
  }

  return (
    <StyledIndex>
      ...
    </StyledIndex>
  );
};

Index.propTypes = {};

export default Index;

Aquí, utilizando nuestro set funciones que recibimos cuando definimos nuestros valores de estado anteriormente, escribimos una serie de if declaraciones, cada una asegurando que hicimos de hecho recuperar data de Github y que cada uno de los valores que necesitamos estén presentes en esa respuesta. Si lo son, llamamos al set correspondiente función, por ejemplo, setRepoName() —pasando el valor correspondiente del objeto de respuesta que queremos mostrar en pantalla.

Hacemos esto para cada variable de estado que establecemos, con el hombre impar siendo el setRepoOwner al que se le pasa un objeto que contiene tres propiedades (a diferencia de un único valor que se pasa directamente).

Con eso, suponiendo que todo funcione bien y que la API de Github esté disponible, deberíamos ver los datos de nuestro repositorio representados en el navegador:

¡Intente intercambiar la URL que le pasamos con un repositorio público propio y véalo en el navegador!

Terminando

En este tutorial, aprendimos cómo acceder a los metadatos públicos para un repositorio de Github usando la API de JavaScript de Github. Aprendimos a definir un componente React.js en Next.js, lo que nos brinda una forma de obtener datos de la API de Github, así como representarlos en la pantalla (y diseñarlos con componentes con estilo).

Finalmente, aprendimos cómo realizar una solicitud HTTP usando JavaScript fetch() API y cómo copiar datos de una solicitud HTTP al estado de nuestro componente para representarlo dinámicamente en la pantalla.