DEV Community

Cover image for Best GitHub parte 4
Erik Giovani
Erik Giovani

Posted on

Best GitHub parte 4

Hola 👋, en este post terminaremos la parte del backend con Next JS.

Para empezar, dentro de nuestra carpeta api que se encuentra dentro de la carpeta pages, vamos a crear un archivo llamado search-place.ts.

Dentro de este archivo agregaremos el siguiente código:

import type { NextApiRequest, NextApiResponse } from "next";

import messages from "@/utils/messages";

export default async function handler(req: NextApiRequest,res: NextApiResponse) {
  if (req.method !== "POST") {
    return res.status(404).json(messages.notFound);
  }
}
Enter fullscreen mode Exit fullscreen mode

Como te habrás dado cuenta, la estructura es muy parecida a lo que habíamos hecho en el post anterior, pero esta vez vamos de usar el método GET usaremos el método POST.

Antes de continuar, vamos a instalar una dependencia llamada Zod para validar el usuario de GitHub que vamos a resibir.

Esto lo haremos con el siguiente comando:

yarn add zod
Enter fullscreen mode Exit fullscreen mode

Si estás usando NPM:

npm install zod
Enter fullscreen mode Exit fullscreen mode

Ya instalado, crearemos un archivo llamado validations.ts, dentro de nuestra carpeta utils, ahí procederemos a escribir el siguiente código:

import { z } from "zod";

export const userZodSchema = z.object({
  username: z.string().trim().min(1),
});
Enter fullscreen mode Exit fullscreen mode

Lo que estoy haciendo es importar z desde zod para poder acceder a su propiedad object, que nos sirve para poder validar varios datos, pasándole un solo objeto, en lugar de validar dato por dato, también estoy validando un dato como un objeto en caso de que quiera después agregar más datos para validar que vengan del frontend.

La key username va a tener la validación del username, en este caso le estoy diciendo con z.string que quiero que el dato sea de tipo string, con el trim le estoy diciendo que si el string recibido tiene espacios se los quite, y con min(1) le estoy diciendo que el username no puede estar vacío y debe tener minimo un carácter.

Y ese esquema de validación lo estoy guardando en una constante llamada userZodSchema.

Ahora en nuestro archivo search-place.ts, procederemos a importar nuestro esquema y agregar el siguiente código después del if:

const result = userZodSchema.safeParse(req.body);

if (!result.success) {
  return res.status(400).json(messages.error);
}

const { username } = result.data;
Enter fullscreen mode Exit fullscreen mode

Lo que hace ese código, es que estamos accediendo a la propiedad safeParse que nos crea zod en nuestro esquema de validación, que sirve para que si el dato recibido no cumple con las validaciones que le agregamos no nos mande un error, depués va a recibir todo lo que venga del req.body de la petición que nos manden a la ruta /api/search-place con el método POST y guardaremos el resultado en una constante llamada result, si el objeto recibido tiene más propiedades aparte del username, nuestro esquema de validación directamente las ignorará.

En el if lo que estoy haciendo es revisar que la constante result no tenga una propiedad llamada success en true, que significa que el dato recibido no cumple con las validaciones que agregamos,y responda con el mensaje de error de nuestras utilidades que habíamos creado en el post anterior, y si existe la propiedad success, entonces de data desestructuramos el username ya validado.

Ahora en nuestra carpeta util vamos a crear un archivo llamado githubCall.ts y dentro agregaremos el siguiente código:

const githubCall = async(username: string) => {
    const response = await fetch(`https://api.github.com/users/${username}`);
    const user = await response.json();
    return user;
}

export default githubCall;
Enter fullscreen mode Exit fullscreen mode

Como podrás ver, es solo una función que recibe el username y hace una llamada al api pública de GitHub y nos retorna los datos públicos de su perfil de GitHub.

También crearemos un archivo llamado changeUser.ts dentro de nuestra carpeta utils, y agregaremos el siguiente código:

import { PrismaClient } from "@prisma/client";

import type { NewUser } from "./types";

const prisma = new PrismaClient();

const changeUser = async (userID: string, newUser: NewUser) => {
  const updatedUser = await prisma.user.update({
    where: { id: userID },
    data: newUser,
  });

  if (updatedUser) {
    console.log("User Updated");
  } else {
    console.error("Error Updating User");
  }
};

export default changeUser;
Enter fullscreen mode Exit fullscreen mode

Esta utilidad nos va a servir para actualizar a los lugares ganadores, por eso la función esta recibiendo el userID y el newUser, dentro de la función estamos accediendo a la propiedad user de nuestro user en prisma para actualizar los del usuario por los del nuevo usuario identificándolo por su ID.

También crearemos un archivo newUser.ts dentro de nuestra carpeta utils con el siguiente código:

import type { NextApiResponse } from "next";

import changeUser from "./changeUser";
import type { GitHubUser, NewUser } from "./types";
import type { User } from "@prisma/client";

const newUser = async (
  res: NextApiResponse,
  user: GitHubUser,
  users: User[]
) => {
  const newUser: NewUser = {
    username: user.login,
    name: user.name,
    url: user.html_url,
    avatar: user.avatar_url,
    total: user.public_repos + user.followers,
  };

  if (newUser.total >= users[0].total) {
    await changeUser(users[0].id, newUser);
    return res.status(200).json({
      message: "Congratulations, you are in the first place",
    });
  } else if (
    newUser.total < users[0].total &&
    newUser.total >= users[1].total
  ) {
    await changeUser(users[1].id, newUser);
    return res.status(200).json({
      message: "Congratulations, you are in the second place",
    });
  } else if (
    newUser.total < users[1].total &&
    newUser.total >= users[2].total
  ) {
    await changeUser(users[2].id, newUser);
    return res.status(200).json({
      message: "Congratulations, you are in the third place",
    });
  } else {
    return res.status(200).json({
      message: "Sorry, you are not in any of the first places",
    });
  }
};

export default newUser;
Enter fullscreen mode Exit fullscreen mode

Lo que está haciendo ese código es ver si el nuevo usuario tiene más puntos o menos que cualquiera de los tres usuarios en nuestra base de datos, y si tiene más o igual de puntos lo va a sustituir.

Para finalizar, nuestro archivo search-place.ts se tendría que ver de esta forma:

import type { NextApiRequest, NextApiResponse } from "next";

import messages from "@/utils/messages";
import { userZodSchema } from "@/utils/validations";
import githubCall from "@/utils/githubCall";
import usersCall from "@/utils/usersCall";
import newUser from "@/utils/newUser";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method !== "POST") {
    return res.status(404).json(messages.notFound);
  }

  const result = userZodSchema.safeParse(req.body);

  if (!result.success) {
    return res.status(400).json(messages.error);
  }

  const { username } = result.data;

  try {
    const user = await githubCall(username);

    if (user.message) {
      return res.status(400).json({ message: "The username not exist" });
    }

    const users = await usersCall();

    return await newUser(res, user, users);
  } catch (error) {
    return res.status(400).json(messages.error);
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)