<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ api - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Descubre miles de cursos de programación escritos por expertos. Aprende Desarrollo Web, Ciencia de Datos, DevOps, Seguridad y obtén asesoramiento profesional para desarrolladores. ]]>
        </description>
        <link>https://www.freecodecamp.org/espanol/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ api - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/espanol/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 18 Jun 2026 04:56:07 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/espanol/news/tag/api/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Como ocultar las llaves API en aplicaciones frontend usando Netlify Functions ]]>
                </title>
                <description>
                    <![CDATA[ Netlify es una plataforma muy popular para desarrollo web que facilita la compilación, despliegue y administración de sitios web. Puedes usar Netlify como hosting para tus sitios y te ayuda a editarlos y publicar los cambios que realices. También te brinda características adicionales como seguridad, servicios de autenticación y autorización ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-ocultar-las-claves-api-en-aplicaciones-frontend-usando-netlify-functions/</link>
                <guid isPermaLink="false">64b5b77c363a37071b881189</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ frontend ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lambda ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Israel Palma ]]>
                </dc:creator>
                <pubDate>Fri, 24 Nov 2023 18:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/07/FCC-hide-API-keys-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/hide-api-keys-in-frontend-apps-using-netlify-functions/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Hide API Keys in Frontend Apps using Netlify Functions</a>
      </p><p>Netlify es una plataforma muy popular para desarrollo web que facilita la compilación, despliegue y administración de sitios web.</p><p>Puedes usar Netlify como hosting para tus sitios y te ayuda a editarlos y publicar los cambios que realices. También te brinda características adicionales como seguridad, servicios de autenticación y autorización de usuarios y más.</p><p>Esta guía se enfoca en mostrarte como configurar una función serverless en Netlify para ocultar las API keys de la aplicación del lado del cliente.</p><p>Para esta lección crearás un motor de búsqueda de imágenes y lo desplegarás en Netlify, que además realizará llamadas a la API de Pixabay usando funciones serverless.</p><p>Este es el mismo proceso para cualquier otra aplicación front-end (cliente) elaborada con JavaScript, con librerías JavaScript o frameworks JS como ReactJS, NextJS, VueJS, Angular, etc.</p><h3 id="prerrequisitos"><strong>Prerrequisitos</strong></h3><p>Para seguir los pasos en este tutorial deberás contar con lo siguiente:</p><ul><li>Una cuenta en Netlify (<a href="https://app.netlify.com/signup">regístrate aquí</a>)</li><li>Entendimiento básico de APIs RESTFUL, funciones Lambda y conceptos de asincronía (async/await).</li></ul><p>El demo terminado lo puedes encontrar en la rama principal de GitHub o en este enlace: <code>https://netlify-func-demo.netlify.app</code>.</p><h2 id="-qu-es-una-funci-n-netlify"><strong>¿Qué es una función Netlify?</strong></h2><p>Una función Netlify es una función serverless o lambda cuyo acceso es provisto por Netlify. Se usan para desplegar código serverless o lógica de "servidor" (back-end) pero sin la necesidad de realmente administrar un servidor dedicado.</p><p>El propósito de una función Netlify es manejar código con base en eventos (event-driven) y enviar solicitudes HTTP que devuelvan una respuesta en formato JSON.</p><blockquote>"Las funciones serverless, llamadas comercialmente "funciones Netlify" por ejecutarse en la plataforma de Netlify, son una manera de desplegar código del lado del servidor en forma de API endpoints" - Documentación Netlify </blockquote><p>La función accede de manera segura el entorno de las variables en segundo plano ("tras bamablinas") mediante una función lambda de Amazon Web Services (AWS).</p><p>Las credenciales secretas como los tokens de acceso o claves API que se ocultan sólo usando variables de entorno son menos seguras. Esto se debe a que pueden ser fácilmente obtenidas usando las "Developer Tools" (herramientas de desarrollo) a traves de la API "fetch request" del navegador.</p><p>Las claves API, si son interceptadas pueden ser mal usadas por actores maliciosos lo que podría afectar a tu aplicación o incrementar tus costos si esta depende de un servicio API de paga.</p><p>Otras funciones serverless utilizadas para ejecutar código sin tener que manejar servidores incluyen: las AWS Lambda Functions, Azure Functions y Google Cloud Functions. </p><h2 id="c-mo-configurar-una-aplicaci-n-cliente"><strong>Cómo configurar una aplicación cliente</strong></h2><h3 id="c-mo-clonar-la-aplicaci-n-demo"><strong>Cómo clonar la aplicación demo</strong></h3><p>Para empezar con este tutorial puedes clonar la "aplicación motor de búsqueda de fotos de stock" desde su &nbsp;<a href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/main">repositorio GitHub</a>. Mira la previsualización en Netlify en <a href="https://netlify-func-demo.netlify.app/">https://netlify-func-demo.netlify.app</a>.</p><p>El primer paso es clonar el repositorio:</p><pre><code class="language-bash">git clone https://github.com/frankiefab100/netlify-serverless-functions-demo.git</code></pre><p>Paso siguiente, cambia (ingresa) al directorio <strong><strong>netlify-serverless-functions-demo</strong>.</strong></p><pre><code class="language-bash"> cd netlify-serverless-functions-demo</code></pre><p>Luego tendrás que instalar las dependencias.</p><pre><code class="language-bash">npm install
#OR 
yarn add</code></pre><p>Ahora ejecuta el servidor de desarrollo. Ejecuta el siguiente comando para este propósito:</p><pre><code class="language-bash">netlify dev</code></pre><p>La aplicación estará disponible en: <code>https://localhost:8888</code>.</p><p>Alternativamente puedes omitir los pasos anteriores si quieres crear la aplicación desde cero. En el próximo paso crearás una aplicación JavaScript para la búsqueda de fotos stock. </p><h3 id="c-mo-construir-la-aplicaci-n-demo-usando-javascript"><strong>Cómo construir la aplicación demo usando JavaScript</strong></h3><p>El primer paso es configurar la aplicación front-end (cliente). Abre tu editor de código favorito como, por ejemplo: VS Code.</p><p>A continuación, crea la carpeta <strong>dist</strong> y dentro crea el archivo index.html. Copia y pega ahí el siguiente código:</p><pre><code class="language-html">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
  &lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta http-equiv="X-UA-Compatible" content="IE=edge" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;link rel="stylesheet" href="style.css" /&gt;
    &lt;title&gt;Stock Photos Search Engine&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;div class="container"&gt;
      &lt;header&gt;
        &lt;h1&gt;Search For Stock Photos&lt;/h1&gt;
        &lt;div class="search-section"&gt;
          &lt;input
            type="text"
            name="search"
            class="search"
            placeholder="Enter a keyword"
          /&gt;
          &lt;input
            id="searchBtn"
            class="search-btn"
            type="submit"
            value="Search"
          /&gt;
        &lt;/div&gt;
      &lt;/header&gt;

      &lt;div class="photo-wrapper"&gt;
        &lt;img src="" alt="" class="photo" /&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;script src="./script.js" type="module"&gt;&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;</code></pre><p>En el mismo directorio <code>dist</code> agrega la siguiente hoja de estilos en un archivo llamado style.css:</p><pre><code class="language-css">/* dist/style.css */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  color: #222;
  font-family: "Roboto", sans-serif;
  font-size: 1rem;
  margin: 0 auto;
  width: 100vw;
  height: 100vh;
}

.container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  text-align: center;
  min-height: 100vh;
  min-width: 100vw;
  width: 100%;
  height: 100%;
}

h1 {
  padding-bottom: 20px;
}

.search-section {
  display: inline;
  text-align: center;
  min-width: 310px;
}

.search,
.search-btn {
  border: none;
  border-radius: 5px;
  font-size: 1rem;
  padding: 15px;
  height: 50px;
}

.search {
  background-color: #d1f3bf;
  color: #222;
  min-width: 225px;
}

.search-btn {
  background-color: #04ab04;
  color: #f6f6f6;
  cursor: pointer;
  margin-left: 5px;
  min-width: 80px;
  transition: all 0.3s ease-in-out;
}

.search-btn:hover {
  background-color: #2dc22d;
}

.photo-wrapper {
  display: flex;
  gap: 15px;
  margin: 30px;
}

.photo-wrapper img {
  width: 200px;
}</code></pre><h2 id="crea-una-cuenta-gratuita-en-pixabay"><strong>Crea una cuenta gratuita en Pixabay </strong></h2><p>El primer paso para utilizar la <a href="https://pixabay.com/api/docs/">Pixabay API</a> es darse de alta una cuenta con tu correo electrónico.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Pixabay-API-Documentation.png" class="kg-image" alt="Pixabay-API-Documentation" width="600" height="400" loading="lazy"><figcaption>Pixabay API key section</figcaption></figure><p>Como se muestra en la imagen de arriba, tus llaves API las puedes encontrar debajo de la seccion de parámetros en la <a href="https://pixabay.com/api/docs/">página oficial de documentación de Pixabay API</a>.</p><h3 id="crea-una-variable-de-entorno"><strong>Crea una variable de entorno</strong></h3><p>Las variables de entorno (comúnmente abreviadas como "env") son combinaciones pares de clave/valor que pueden afectar el comportamiento y los procesos de un sistema operativo o aplicación.</p><p>Usar variables de entorno se recomienda para configurar servicios de terceros y sus credenciales durante el desarrollo.</p><h3 id="instala-dotenv"><strong>Instala dotenv</strong></h3><p>Una vez que haya completado la creación de tu cuenta en Pixabay, abre tu terminal e instala el paquete <strong>dotenv</strong>. Esto permitirá que tu aplicación pueda leer las variables de entorno del archivo <strong>.env</strong>.</p><figure class="kg-card kg-code-card"><pre><code class="language-javascript">npm install dotenv
#O bien
yarn add -D dotenv</code></pre><figcaption>Usando tu terminal o línea de comandos</figcaption></figure><p>En el próximo paso podrás guardar la clave API en el archivo <strong>.env</strong>.</p><h3 id="crea-el-archivo-env"><strong>Crea el archivo .env</strong></h3><p>En la raíz del directorio de tu aplicación crea el archivo <strong>.env</strong> y almacena las llaves (keys) API copiadas de tu perfil Pixabay.</p><pre><code class="language-plaintext">PIXABAY_API_KEY=123456-7890</code></pre><p>Donde <code>PIXABAY_API_KEY=123456-7890</code> representa la llave y su valor asociado.</p><p><strong><strong>Not</strong>a: </strong>Reemplaza este par clave/valor con el valor apropiado.</p><h3 id="crea-el-archivo-gitignore"><strong>Crea </strong>el archivo<strong> .gitignore</strong></h3><p>Para evitar incluir archivos y valores sensibles como <code>node_modules</code> y <code>secret keys</code> &nbsp;en cada commit en repositorios públicos, crea el archivo <strong>.gitignore</strong> en la misma carpeta raíz del proyecto y agrega lo siguiente:</p><figure class="kg-card kg-code-card"><pre><code class="language-plaintext">node_modules
.env
.netlify</code></pre><figcaption>.gitignore</figcaption></figure><p>La carpeta <strong>.netlify</strong> que contiene funciones serverless compiladas y otros archivos serán excluidos cuando el proyecto se empuje a GitHub o a cualquier otro sistema de control de versiones.</p><h3 id="crea-una-funci-n-get-request"><strong>Crea una función get request</strong></h3><p>Ahora deberás añadir la lógica para realizar la petición fetch en el archivo <strong>script.js</strong>. Ajustaremos la lógica más adelante usando funciones Netlify.</p><pre><code class="language-javascript">/* dist/script.js */
const dotenv = require("dotenv").config();

const searchbar = document.querySelector(".search");
const submitBtn = document.querySelector(".search-btn");
const photoWrapper = document.querySelector(".photo-wrapper");

submitBtn.addEventListener("click", () =&gt; {
  getPhoto(searchbar.value);
  searchbar.value = "";
});

window.addEventListener("keydown", (e) =&gt; {
  if (e.keyCode === 13) {
    getPhoto(searchbar.value);
    searchbar.value = "";
  }
});

async function getPhoto(keyword) {
  const apiKey = PIXABAY_API_KEY;
  let apiURL = `https://pixabay.com/api/?key=${apiKey}&amp;q=${keyword}&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`;

  try {
    const response = await fetch(apiURL, {
      method: "GET",
      headers: { accept: "application/json" },
    });
    const data = await response.json();

    let imageURL = data.hits;

    imageURL.forEach((result) =&gt; {
      let imageElement = document.createElement("img");
      imageElement.setAttribute("src", `${result.webformatURL}`);
      photoWrapper.appendChild(imageElement);
    });
  } catch (error) {
    alert(error);
  }
}</code></pre><p><strong>Nota: </strong>Como he mencionado anteriormente, si se publica este repositorio en GitHub, podremos acceder a la clave API en el "lado del cliente" desde un navegador, aunque hayamos excluido el archivo <code>.env</code>, mas adelante veremos como. </p><p>Para ilustrar esto, selecciona la rama <code>testing</code> <a href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/testing">del repositorio de esta aplicación</a>. La <a href="https://testing--netlify-func-demo.netlify.app/">previsualización del sitio </a>mostrara los siguientes errores de referencia (Reference Errors) en la consola del navegador: </p><pre><code class="language-bash">Uncaught ReferenceError: require is not defined
Uncaught ReferenceError: require is not defined at getPhotos
Uncaught ReferenceError: process is not defined at getPhotos</code></pre><p>Esto se debe a que no hay manera de vincular con las variables de entorno especificadas en el archivo <strong>.env, </strong>ya que este no se envió en el commit al repositorio público en GitHub.</p><p>En el siguiente paso, selecciona y clona la rama <code>testing</code> en tu máquina de forma local con los siguientes comandos:</p><pre><code class="language-bash">git clone https://github.com/frankiefab100/netlify-serverless-functions-demo.git
cd netlify-serveless-functions-demo
npm install
netlify dev</code></pre><p>La aplicación estará disponible en tu navegador en la dirección <code>localhost:8888</code>.</p><p>Ahora abre las herramientas para desarrolladores (<strong>Developer Tools) </strong>o<strong> </strong>haz clic derecho y selecciona inspeccionar. O puedes presionar la tecla <strong>F12</strong>. Luego vea la pestaña <strong>Network</strong> y haz clic en la URL de la petición del archivo <code>getPhotos.js</code>. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--124----Copy.png" class="kg-image" alt="Screenshot--124----Copy" width="600" height="400" loading="lazy"><figcaption>Clave API mostrada en la pestaña Red (Network) en las herramientas de Desarrollo</figcaption></figure><p>Ahora deberías poder ver la clave API expuesta públicamente en la sección de encabezados de la pestaña <strong>Network</strong> donde fue devuelta con los datos de la respuesta en tu navegador.</p><p>Esto es un problema de seguridad ya que la pestaña Network en las herramientas de desarrollo típicamente permite mostrar información tal como la URL de la petición, el estado y los datos de la respuesta.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--127----Copy.png" class="kg-image" alt="Screenshot--127----Copy" width="600" height="400" loading="lazy"><figcaption>Clave de la API devuelta como dato de respuesta y expuesto en el URL de la petición</figcaption></figure><p>En la siguiente sección encontraras la manera segura de ocultas la clave API utilizando las funciones Netlify. </p><h2 id="c-mo-empezar-a-usar-netlify-functions"><strong>Cómo empezar a usar Netlify Functions</strong></h2><p>Primero, necesitarás iniciar sesión en tu terminal e instalar la <strong>Netlify CLI</strong> y <strong>Lambda</strong> como dependencias de desarrollo. Puedes hacerlo al ejecutar este comando:</p><pre><code class="language-bash">npm install -g netlify-cli netlify-lambda
#OR 
yarn add -D netlify-cli netlify-lambda --save-dev</code></pre><h3 id="agrega-los-comandos-personalizados-build-y-dev-en-package-json"><strong>Agrega los comandos personalizados build y dev en package.json</strong></h3><p>Estos comandos, crearán e iniciarán la aplicación en el servidor y también la ejecutarán en el nevegador. &nbsp;Aquí puedes ver un ejemplo de como podrías agregar este script con comandos en <strong>el archivo package.json</strong>:</p><pre><code class="language-bash">"scripts": {
   "build": "npm run-script",
   "dev": "netlify dev"
 }</code></pre><h3 id="instala-axios"><strong>Instala Axios</strong></h3><p>Usaremos el método <code>axios.get</code> porque esta función de Node difiere del método fetch en que este último está pensado para correr en el navegador.</p><p>Para instalar Axios, abre tu terminal e ingresa el comando:</p><pre><code class="language-bash">npm install axios
#OR
yarn add -D axios</code></pre><p>En este caso vamos a trabajar nuestra aplicación solo con JavaScript puro (vanilla JS), es decir sin usar librerías o marcos de trabajo para nuestro frontend, por lo que deberás importar axios de la siguiente manera: </p><pre><code class="language-javascript">const axios = require("axios");</code></pre><p>Para cuando se usan librerias como React, debes importar asi: </p><pre><code class="language-javascript">import axios from "axios";</code></pre><h3 id="crea-una-funci-n-serverless"><strong>Crea una función serverless </strong></h3><p>En la raíz del proyecto, crea una carpeta llamada <code>netlify</code> y a su vez dentro de este crea otra carpeta <code>functions</code>. En esta carpeta <code>functions</code> crea un archivo llamado <code>getPhotos.js</code>.</p><p>Crearás una función serverless en el archivo <code>getPhotos</code>. Esto ocultará efectivamente las llaves API cuando traigamos las imágenes desde la <a href="https://pixabay.com/api">API de Pixabay</a>.</p><pre><code class="language-javascript">//netlify/functions/getPhotos.js 
require("dotenv").config();

const axios = require("axios");

exports.handler = async (event, context) =&gt; {
  try {
    const { keyword } = event.queryStringParameters;
    let response = await axios.get(
      `https://pixabay.com/api/?key=${process.env.PIXABAY_API_KEY}&amp;q=${keyword}&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`,
      {
        headers: { Accept: "application/json", "Accept-Encoding": "identity" },
        params: { trophies: true },
      }
    );

    let imageURL = response.data.hits;

    return {
      statusCode: 200,
      body: JSON.stringify({ imageURL }),
    };
  } catch (error) {
    return {
      statusCode: 500,
      body: JSON.stringify({ error }),
    };
  }
};</code></pre><p>Aquí la clave <code>process.env.PIXABAY_API_KEY</code> hace referencia al entorno de configuración de la clave API especificado en el archivo <code>.env</code> para el modo de desarrollo.</p><p>El parámetro <code>keyword</code> acepta una cadena de texto (string) que es accesible en la propiedad <code>queryStringParameters</code> y devuelve datos commo respuesta almacenados en la variable <code>imageURL</code>. Esta será enviada al archivo <code>script.js</code> como respuesta a la petición (veremos esto más adelante).</p><p>Si la petición GET es exitosa, devolverá una respuesta con el código (<code>statusCode</code>) 200 y el objeto JSON correspondiente. En caso de errores, recibiremos una alerta con el mensaje y código de estado.</p><p>Debido a cambios de versión, Axios podría devolver un Buffer como respuesta en tu terminal que se vera algo como esto:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-data.JPG" class="kg-image" alt="netlify-data" width="600" height="400" loading="lazy"><figcaption>Buffer de respuesta de Axios en la terminal</figcaption></figure><p>Para evitar esto deberás adjuntar lo siguiente a la petición GET:</p><pre><code class="language-javascript"> let response = await axios.get(
      `https://pixabay.com/api/?key=${process.env.PIXABAY_API_KEY}&amp;q=${keyword}&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`,
      {
        headers: { Accept: "application/json", "Accept-Encoding": "identity" },
        params: { trophies: true },
      }
    );</code></pre><h3 id="crea-un-archivo-de-configuracion-netlify"><strong>Crea un archivo de configuracion Netlify</strong></h3><p>En la raíz del proyecto, crea el archivo <code>netlify.toml</code>. Este archivo especifica como es que Netlify construirá y desplegará tu aplicación.</p><p>Ahora, agrega la siguiente configuración en el archivo <code>netlify.toml</code>:</p><pre><code class="language-bash">[build]
  command = "npm run build"
  functions = "netlify/functions"
  publish = "dist"</code></pre><p><strong><strong>Not</strong>a<strong>:</strong></strong></p><ul><li><code>command = "npm run build"</code> ejecuta la Netlify CLI (Interfaz de Línea de Comandos) para que realice la construcción de la aplicación a partir de las funciones. &nbsp;</li><li><code>functions = "netlify/functions"</code> indica que las funciones en <code>getPhotos</code> existen en ña carpeta <code>netlify/functions</code>. </li><li><code>publish = "dist"</code> identifica <code>dist</code> como la carpeta desde donde será "servido" este archivo. </li></ul><h3 id="edita-el-archivo-script-js-con-la-url-para-las-peticiones-a-netlify-functions"><strong>Edita el archivo script.js con la URL para las peticiones a Netlify functions</strong></h3><p>Siguiente paso, edita la <code>apiURL</code> de esto:</p><pre><code class="language-javascript">let apiURL = `https://pixabay.com/api/?key=${apiKey}&amp;q=${keyword}&amp;image_type=photo&amp;safesearch=true&amp;per_page=3`;</code></pre><p>A esto (el endpoint para la petición HTTP a las Netlify Functions):</p><pre><code class="language-javascript">  let apiURL = `/.netlify/functions/getPhotos?keyword=${keyword}`;</code></pre><p>Esta función "serverless" será consultada desde el lado del cliente de tu aplicación mediante el endpoint: <code>/.netlify/functions/getPhotos</code>. Una vez que la solicitud es enviada, la función <code>getPhotos</code> será invocada desde <code>script.js</code>. </p><p>La respuesta <code>imageURL</code> obtenida de la Netlify functions <code>getPhotos</code> será pasada o enviada como el valor asignado al parámetro <code>keyword</code> en el "query string" (cadena de texto de consulta) de la función. Su contenido será recorrido mediante un ciclo for para devolver las 3 imágenes desde la API de Pixabay. </p><p>El archivo <strong>script.js</strong> debe verse así:</p><pre><code class="language-javascript">/* dist/script.js */
const searchbar = document.querySelector(".search");
const submitBtn = document.querySelector(".search-btn");
const photoWrapper = document.querySelector(".photo-wrapper");

submitBtn.addEventListener("click", () =&gt; {
  getPhoto(searchbar.value);
  searchbar.value = "";
  photoWrapper.innerHTML = "";
});

window.addEventListener("keydown", (e) =&gt; {
  if (e.keyCode === 13) {
    getPhoto(searchbar.value);
    searchbar.value = "";
    photoWrapper.innerHTML = "";
  }
});

async function getPhoto(keyword) {
  let apiURL = `/.netlify/functions/getPhotos?keyword=${keyword}`;

  try {
    const response = await fetch(apiURL, {
      method: "GET",
      headers: { accept: "application/json" },
    });
    const data = await response.json();

    for (let i = 0; i &lt; data.imageURL.length; i++) {
      let imageElement = document.createElement("img");
      imageElement.setAttribute("src", `${data.imageURL[i].webformatURL}`);
      photoWrapper.appendChild(imageElement);
    }
  } catch (error) {
    alert(error);
  }
}</code></pre><p><strong>Nota: </strong>En el código de arriba mantiene tu variable de entorno segura ya que se accede a esta desde una función serverless.</p><p><strong>Ejecuta el servidor de desarrollo</strong></p><p>Ahora, ejecuta tu aplicación con el siguiente comando: </p><pre><code class="language-bash">netlify dev</code></pre><p>Esto hará que se cargue la función <strong>getPhotos</strong> mediante <code>https://localhost:8888/.netlify/functions/getPhotos.</code></p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-dev-terminal.JPG" class="kg-image" alt="netlify-dev-terminal" width="600" height="400" loading="lazy"><figcaption>Resultado en terminal de netlify build</figcaption></figure><p>Luego, inicia el servidor de desarrollo y lanza la aplicación en tu navegador por defecto en &nbsp;<code>localhost:8888</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/netlify-success2.JPG" class="kg-image" alt="netlify-success2" width="600" height="400" loading="lazy"><figcaption>La función Netlify está ahora lista en https://localhost:8888</figcaption></figure><p>En este punto, la configuración de la función Netlify está completa y ya es posible realizar solicitudes <code>GET</code> HTTP.</p><h3 id="c-mo-realizar-solicitudes-para-traer-datos"><strong>Cómo realizar solicitudes para traer datos</strong></h3><p>Ahora que ya tienes la aplicación servida, hagamos una solicitud (fetch) para traer datos. Ingresa algún texto en el campo input y dale <strong>Enter o </strong>haz clic en el botón para traer las imágenes desde la API de Pixabay. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Stock-Photos-Search-Engine--1-.png" class="kg-image" alt="Stock-Photos-Search-Engine--1-" width="600" height="400" loading="lazy"><figcaption>Imágenes de flores buscados dese la API de Pixabay</figcaption></figure><p>Para mayor información acerca del API de Pixabay, ve a su <a href="https://pixabay.com/api/docs">sitio oficial de documentación</a>. </p><p>El comando anterior enviará una solicitud a la función serverless y luego devolverá seis respuestas. Aquí cómo se verá la respuesta en la ventana de tu terminal:</p><blockquote>Request from ::1: GET /.netlify/functions/getPhotos?keyword=flower<br>Response with status 200 in 4895 ms<strong>.</strong></blockquote><p>También puedes utilizar las herramientas de desarrollador (<strong>Developer Tools)</strong> en la pestaña <strong>Network </strong>(Red) para validar la solicitud.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--118----Copy.png" class="kg-image" alt="Screenshot--118----Copy" width="600" height="400" loading="lazy"><figcaption>Datos de respuesta de la función serverless de la API</figcaption></figure><p>La solicitud devuelve la URL de nuestra aplicación, la función Netlify <strong>getPhotos</strong>, el script.js y las imágenes de Pixabay.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--120-.png" class="kg-image" alt="Screenshot--120-" width="600" height="400" loading="lazy"><figcaption>Respuesta de la petición realizada a la función Netlify vista en la sección de encabezado en la pestaña de Red (Network) del navegador.</figcaption></figure><h2 id="c-mo-hospedar-la-aplicaci-n-en-el-repositorio-remoto"><strong>Cómo hospedar la aplicación en el repositorio remoto</strong></h2><p>Ahora, deberías empujar tu proyecto al hosting de control de versiones GitHub.</p><pre><code class="language-javascript">git add .
git commit -m"initial commit"
git push origin -u main</code></pre><h2 id="c-mo-desplegar-la-aplicaci-n-y-la-funci-n-serverless-en-netlify"><strong>Cómo desplegar la aplicación y la función serverless en Netlify</strong></h2><p>Una vez que hayas publicado el proyecto en GitHub, inicia sesión en <a href="https://app.netlify.com/">Netlify</a> y conecta tu cuenta GitHub para dar autorización.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--130-.png" class="kg-image" alt="Screenshot--130-" width="600" height="400" loading="lazy"><figcaption>Importar un proyecto para despliegue en Netlify</figcaption></figure><p>Como se muestra en la imagen de arriba, haz clic en <code>Add New Project</code>y luego busca el repositorio de tu aplicación en la lista desplegable. Enseguida, haz clic en <code>Build Your Site</code>. Esto tomará algunos minutos en completarse. </p><p>Acabas de desplegar la aplicación usando la UI de Netlify. Ahora tu aplicación estará disponible en: <code>https://netlify-func-demo.netlify.app.</code></p><p>La URL para peticiones "fetch" (del inglés traer) debería verse como ésta: <code>https://netlify-func-demo.netlify.app/.netlify/functions/getPhotos</code>.</p><h2 id="opcional-c-mo-configurar-la-aplicaci-n-desde-el-tablero-netlify"><strong>Opcional - Cómo configurar la aplicación desde el tablero Netlify </strong></h2><p>Alternativamente, puedes configurar el comando Netlify desde el tablero (o panel de control, del inglés, Dashboard). Si estas configuraciones ya han sido especificadas en el archivo <strong>netlify.toml</strong>, se sobreescribirán cualquier configiracion correspondiente.</p><p>Primero, selecciona la configuración de la carpeta del proyecto en "Site settings". </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Site-overview-netlify-func-demo1.png" class="kg-image" alt="Site-overview-netlify-func-demo1" width="600" height="400" loading="lazy"><figcaption>Opciones de Sitio para la carpeta del proyecto en Netlify</figcaption></figure><h3 id="configura-el-comando-build-y-el-directorio-p-blico"><strong>Configura el comando Build y el directorio público </strong></h3><p>Ve a <code>Site settings</code>&gt; <code>Deploy</code> &gt; <code>Build &amp; Deploy</code>, edita los siguientes comandos y luego guarda los cambios:</p><ul><li>Commando build: <strong><strong>npm run build</strong></strong></li><li>Directorio público: <strong><strong>dist</strong></strong></li></ul><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--132-.png" class="kg-image" alt="Screenshot--132-" width="600" height="400" loading="lazy"><figcaption>Sección de Construcción y despliegue en Netlify</figcaption></figure><h3 id="configura-la-carpeta-de-las-funciones"><strong>Configura </strong>la carpeta de las funciones</h3><p>Por defecto, Netlify utiliza <code>netlify/functions</code> como el directorio en el cual encontrar las funciones a desplegar. En este caso, nuestra función serverless <code>getPhotos</code> se encuentra en la carpeta <code>netlify/funcions</code>.</p><p>Ahora configuraremos una carpeta personalizada donde Netlify puede encontrar tus funciones compiladas. Ve a <code>Site settings</code> &gt; <code>Functions</code> e ingresa la ruta la carpeta en donde se encuentran almacenadas las funciones de tu repositorio.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--131-.png" class="kg-image" alt="Screenshot--131-" width="600" height="400" loading="lazy"><figcaption>Sección de Carpeta de Funciones en Netlify</figcaption></figure><h3 id="c-mo-configurar-variables-de-entorno-para-producci-n"><strong>Cómo configurar variables de entorno para producción </strong></h3><p>En el panel de control de Netlify (Dashboard) haz clic en <code>Site settings</code> &gt; <code>Build &amp; deploy</code> &gt; <code>Environment</code>&gt; <code>Environment varibales</code> y luego configura el par clave/valor de la siguiente manera:</p><pre><code class="language-plaintext">PIXABAY_API_KEY=your-api-key-here</code></pre><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--133-.png" class="kg-image" alt="Screenshot--133-" width="600" height="400" loading="lazy"><figcaption>Sección de Variables de Entorno en Netlify</figcaption></figure><p>Haz clic en guardar, y enseguida redespliega la aplicación para que los cambios se apliquen. </p><p>Para redesplegar la aplicación en Netlify, ve a <code>Deploys</code> &gt; <code>Trigger deploy</code>. A continuación, selecciona <code>Clear cache and redeploy site</code>.</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--134-.png" class="kg-image" alt="Screenshot--134-" width="600" height="400" loading="lazy"><figcaption>Ejecuta el redespliegue en la pestaña "Deploys" en Netlify</figcaption></figure><p><strong>Nota: </strong>El nombre de la variable de entorno (PIXABAY_API_KEY) debe coincidir con el que se referencia en el código en <code>getPhotos</code>.</p><p>Para una aplicación React, usa el prefijo <code>REACT_APP_</code> para que puedan ser leídas desde el archivo <code>.env</code>.</p><pre><code class="language-plaintext"> REACT_APP_PIXABAY_API_KEY=your-api-key-here</code></pre><h2 id="c-mo-inicializar-la-funci-n-serverless-desde-la-aplicaci-n-remota"><strong>Cómo inicializar la función serverless desde la aplicación remota</strong></h2><p>Para conectar la carpeta de tu proyecto a la aplicación web ya desplegada, inicia sesión en tu cuenta Netlify desde la terminal: </p><pre><code class="language-bash">netlify login</code></pre><p>A continuación, inicializa la aplicación en Netlify ejecutando el siguiente comando en tu terminal:</p><pre><code class="language-bash">netlify init</code></pre><p>Tu aplicación ahora está configurada para despliegues continuos via Netlify.</p><h2 id="c-mo-compilar-la-funci-n-serverless-de-netlify"><strong>Cómo compilar la función serverless de Netlify</strong></h2><p>Necesitas enlazar tu aplicación con el ID del sitio en Netlify antes de poder ejecutar el comando build en tu terminal. Para conectar la carpeta local de tu proyecto con el ID de tu sitio en Netlify, ingresa el siguiente comando en tu terminal: </p><pre><code class="language-bash">netlify link</code></pre><p>Luego de esto, se te pedirá que enlaces la carpeta mediante cualquiera de las siguientes formas:</p><ul><li>Búsqueda por nombre completo o parcial del sitio.</li><li>Elegir de una lista de tus sitios recientemente editados.</li><li>Ingresa el ID de tu sitio.</li></ul><p>Una vez que hayas seleccionado tu opción preferida, se habrá enlazado la carpeta de tu proyecto con el sitio hospedado en Netlify. Esto te permite ejecutar comandos con la <strong>CLI de Netlify </strong> y automáticamente desplegar el proyecto desde el repositorio cada que haya cambios en el código.</p><p>En el siguiente paso, compilarás una función serverless mientras se ejecuta en el servidor. Para correr el comando build definido en el archivo <code>netlify.toml</code>, ejecuta el siguiente comando:</p><pre><code class="language-bash">netlify build</code></pre><p>Esto a su vez, ejecutará el comando <code>npm run-script</code> como fue especificado en el <code>package.json</code>. Ahora, tu función serverless que está en la carpeta <code>netlify/functions</code> será empaquetado ¡exitosamente!</p><h2 id="c-mo-poner-a-prueba-la-aplicaci-n"><strong>Cómo poner a prueba la aplicación </strong></h2><p>Para realizar pruebas y confirmar que la función Netlify funciona correctamente, ejecuta el comando siguiente en tu terminal:</p><pre><code class="language-bash">netlify functions:serve</code></pre><p>Esto inyecta las variables de entorno de tu proyecto del archivo <strong>.env</strong> y ejecuta la función serverless..</p><h3 id="c-mo-confirmar-la-seguridad-de-la-clave-api-keys"><strong>Cómo confirmar la seguridad de la clave API Keys</strong></h3><p>Para inspeccionar tu aplicación y confirmar que las claves API se encuentran ocultas sigue los pasos a continuación:</p><p>Haz clic en la URL de tu aplicación hospedada y una vez abierta, navega a las herramientas de desarrollador (<strong>Developer Tools</strong>) presionando la tecla <strong>F12 </strong>o haciendo clic en cualquier lugar y seleccionando <strong>Inspeccionar</strong>. Navega a la pestaña de Red, donde deberías ver los datos traídos desde la API Pixabay. </p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/news/content/images/2023/02/Screenshot--128-.png" class="kg-image" alt="Screenshot--128-" width="600" height="400" loading="lazy"><figcaption>Clave API ahora oculta de la vista pública usando las herramientas de desarrollador (<strong>Developer Tools).</strong>&nbsp;</figcaption></figure><p>Ahora ya has confirmado que has configurado exitosamente una función serverless y la has desplegado en Netlify.</p><h2 id="conclusi-n"><strong><strong><strong>Conclusi</strong></strong>ó<strong><strong>n</strong></strong></strong></h2><p>Este tutorial introdujo las funciones serverless, JavaScript asíncrono y conceptos de APIs RESTful.</p><p>Espero que ahora sepas cómo crear funciones serverless/lambda y mantener de manera segura cualquier valor sensible como las llaves/claves API que utiliza tu aplicación del lado del cliente. </p><p>Si te atoraste con algo, puedes tener acceso al código completo en el <a href="https://github.com/frankiefab100/netlify-serverless-functions-demo/tree/main">Repositorio en GitHub</a>.</p><p>¡Gracias por leer el artículo! Sígueme en <a href="https://twitter.com/frankiefab100">Twitter</a>.</p><h2 id="enlaces-relevantes"><strong>Enlaces relevantes</strong></h2><ul><li><a href="https://www.freecodecamp.org/espanol/news/como-alojar-repositorio-git-en-subdominio-con-netlify/">Cómo alojar un repositorio de GIT en un subdominio con Netlify</a></li><li><a href="https://www.freecodecamp.org/espanol/news/como-implementar-una-aplicacion-basada-en-react-router-en-netlify/">Cómo implementar una aplicación basada en React Router en Netlify</a></li><li><a href="https://www.freecodecamp.org/espanol/news/como-agregar-un-formulario-netlify-a-una-aplicacion-react-creada-con-create-react-app/">Cómo agregar un formulario Netlify a una aplicación React construida con create-react-app</a></li></ul> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Consumiendo APIs con ReactJS: Aprende useEffect y useState ]]>
                </title>
                <description>
                    <![CDATA[ Tutorial completo sobre cómo consumir APIs con ReactJS utilizando useEffect y useState! Esta es una guía paso a paso que te enseñará cómo interactuar con APIs externas en tus aplicaciones React. ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/consumiendo-una-api-rest-con-react-js/</link>
                <guid isPermaLink="false">654c3467208bc703b414b36a</guid>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ useState ]]>
                    </category>
                
                    <category>
                        <![CDATA[ useEffect ]]>
                    </category>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Leonardo José Castillo Lacruz ]]>
                </dc:creator>
                <pubDate>Mon, 13 Nov 2023 02:43:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/11/4ujLXaujWZj3_2560_1440-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <h2 id="introducci-n-a-las-apis">Introducción a las APIs</h2><p>Empecemos por conocer lo que es una API. Estas letras son el acrónimo de <strong>application programming interface</strong>, en español lo podemos traducir como Interfaz de programación de aplicaciones, su objetivo fundamental es permitir que la comunicación entre 2 o más elementos o sistemas. </p><p>Con la definición anterior, también indicar que existen diferentes tipos de API, una de las más conocidas es la API que ofrece <a href="https://learn.microsoft.com/es-es/windows/win32/api/">Windows</a> para poder acceder a los recursos del sistema operativo y construir aplicaciones sobre él. Otro tipo de API y es el foco de nuestra atención, es el tipo encargado del transporte de datos entre sistemas, siendo ampliamente utilizado actualmente.</p><h2 id="arquitecturas-de-api-para-transporte-de-datos">Arquitecturas de API para transporte de datos</h2><p>Dado su amplio uso, las APIs de transporte de datos se pueden organizar o implementar de diferentes formas, teniendo como aspectos diferenciales, el formato de datos y la forma como se definen las reglas de comunicación entre los principales puntos a definir, veamos algunos tipos de arquitectura de API para transporte de datos:</p><ol><li><em>REST (Representational State Transfer o Transferencia del estado representacional)</em>, es actualmente la arquitectura más usada, utiliza el protocolo http como base para el transporte de datos, cada petición u operación es independiente y realiza una tarea completa, es decir no hay dependencia entre operaciones.</li><li><em>GraphQL</em>, al igual que REST, esta basado en el protocolo http, pero se diferencia en la forma como realiza las operaciones, ya que estas se basan en un lenguaje de consulta y manipulación de datos, lo cual hace posible manejarlas de manera más detallada y óptima pero incrementando un poco la complejidad. </li><li><em>Websocket</em>. Es una arquitectura basada en el concepto de socket, el cual mediante el protocolo TCP, crea una conexión directa entre 2 puntos y la mantiene abierta, de forma tal que cualquier operación se ejecuta en el menor tiempo posible. Es indicada para casos en que la comunicación debe realizarse en tiempo real.</li><li><em>Webhook. </em>Es un tipo de arquitectura un poco diferente a las anteriores, el inicio del proceso se atribuye a eventos, es decir, es a partir de una acción, que el proceso se inicia, como por ejemplo, cuando un registro en el sistema está en la condición X, se ejecuta la petición a la URL y en general son respuestas a procesos, por lo que esta arquitectura se denomina API reversa o inversa.</li><li><em>RPC y gRPC</em>. Esta arquitectura actua como un proceso remoto ejecutado en ambiente local, trabaja bajo http, tcp, o udp. Se base en la definición de un servicio que es invocado por el cliente y resuelto por el servidor, a diferencia de REST, tanto como cliente como servidor comparten definiciones del servicio, es decir debe haber correspondencia en sus implementaciones, en REST cada lado implementa de su forma y es solo en el transporte de datos que debe correspondencia.</li><li><em>SOAP. </em>Mas que una arquitectura es protocolo de comunicación, basado en http pero con transporte de datos hecho en XML (eXtensible Markup Language, o en español 'Lenguaje de Marcado Extensible'), su forma de trabajo es basada en acceso a objetos y tiene un conjunto de reglas y restricciones que la hacen un poco más compleja de manejar pero posee mayor seguridad. </li></ol><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/1698457900830.gif" class="kg-image" alt="1698457900830" width="800" height="1120" loading="lazy"><figcaption>Tipos de arquitecturas más conocidos. Tomado de: https://www.linkedin.com/in/brijpandeyji/</figcaption></figure><h2 id="detallando-una-api-rest">Detallando una API REST</h2><p>Como comentamos anteriormente, una API REST es una herramienta o mecanismo que permite interconectar componentes o sistemas. Su uso actualmente se ha enfocado en ser la forma ideal para implementar procesos de gestión de datos, conocidos como CRUD (C-reate, R-ead, U-pdate y D-elete), de uso común para cualquier aplicación. </p><p>El formato común de transporte de datos este tipo de APIs es JSON (Javascript Object Notation), el cual por ser liviano y muy bien estructurado permite que podamos representar de forma simple y entendible los modelos que serán procesados.</p><p>La comunicación entre el cliente y el servicio se realiza a través de protocolo http como indicamos anteriormente y cada tipo de operación está relacionada o identificada por un verbo, veamos que significa ello.</p><p>Para saber que operación se va a realizar existen 2 elementos fundamentales que permiten definirla, la ruta o endpoint y el verbo. Cuandon realizamos operaciones sobre una API REST, estas las denominamos peticiones, solicitudes o requisiciones, recordando que quien inicia la operación es el cliente, conversando con el servicio que dispone o habilita la API. </p><h2 id="ruta-o-endpoint-de-una-petici-n-en-api-rest">Ruta o endpoint de una petición en API REST</h2><p>Es la dirección o url que se va a ejecutar cuando realicemos la petición, solicitud o requisición, de esta forma el protocolo http, identificará que servidor va a responder y que modelo o entidad es la que se va a procesar. Sigue a continuación lo que se denomina la anatomía de una ruta o endpoint.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image.png" class="kg-image" alt="image" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image.png 600w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image.png 711w" width="711" height="266" loading="lazy"><figcaption>Elementos o partes de un endpoint</figcaption></figure><h2 id="verbos-de-una-api-rest">Verbos de una API REST</h2><p>Para que esta conversación sea posible, existen lo que se denominan verbos, que son los que definen que tipo de operación se va a realizar, los verbos más comunes en una API REST son:</p><ul><li>GET: se define o identifica como el verbo que permite realizar consultas, es importante indicar que este verbo trabaja de la misma forma que en el protocolo http, donde los datos que enviamos quedan expuestos en la url. Esta operación es la R de R-ead.</li><li>POST: es el verbo que nos permite definir operaciones de creación de registros, los datos al igual que en el protocolo http, viajan por un mecanismo interno o cuerpo de la petición, no son visibles. En este caso enviamos los datos en lo que denominamos el cuerpo o <em>body</em> de la petición, generalmente se envian en formato JSON</li><li>PUT: mediante este verbo hacemos actualizaciones completas de registros, es decir, lo que enviamos va a sustituir lo que existe, generalmente en la url o endpoint de la petición se envía un identificador que permite saber sobre que registro se hace la actualización, importante tener claro este punto, ya que en REST tenemos 2 formas de actualizar registros, los datos al igual que en POST, no son visibles en el envío.</li><li>PATCH: con el uso de este verbo, podemos realizar actualizaciones de forma incremental, es decir podemos cambiar partes de registro sin afectarlo por completo, es decir son actualizaciones parciales. Los datos en este caso de igual forma viajan mediante el cuerpo o body de la petición, es decir no son visibles en el envío. En la ruta o endpoint generalmente se indica un parámetro que pemite identificar el registro a ser actualizado.</li><li>DELETE: a través de este verbo podemos indicar la operación de borrado de registros. No es necesario enviar body, ya que en la ruta o endpoint se indica un parámetro que permite identificar que registro se va a eliminar.</li></ul><p>En las definiciones anteriores se indica que para identificar un registro, la buena práctica o común implementación es indicarlo en la ruta, pero no hay una restricción técnica que sea de obligatorio cumplimiento.</p><h2 id="reactjs-y-los-hooks">ReactJS y los hooks</h2><p>Iniciamos recordando que ReactJS es una libreria que permite implementar y construir el front-end para aplicaciones web, esta libreria es de amplio uso, por estar basada en Javascript tiene una comunidad fuerte que apoya su evolución continua. </p><p>React trabaja de la forma en que la aplicación sólo comunica al navegador la página inicial, esto se denomina SPA o Single Page Application, y toda la lógica es controlada e implementada por la libreria, esto hace que el performance y la usabilidad de cara al usuario sea mucho mejor que las aplicaciones web tradicionales (basadas en html+css+JS y renderizado total).</p><p>Uno de los puntos fuertes que surgió a partir de la versión 16.8, es la implementación en base a componentes funcionales, lo cual facilitó de forma enorme la construcción e implementación, de igual manera en esa versión nacieron <em>los hooks</em>, pero que son y como funcionan?, en las siguientes lineas lo revisamos en detalle.</p><h2 id="entiendo-los-hooks-de-reactjs">Entiendo los hooks de ReactJS</h2><p>Empecemos por definir y entender que son los <em><em>Hooks</em></em>, expresémoslo en los términos más simples, <em><em>un hook es una función</em></em>, partiendo de esa premisa entonces podemos indicar que no es cualquier función, está claro ello, en ese caso podemos decir:</p><blockquote><em><em>Un hook es una función especial con un objetivo especifico, simplificar algún proceso que hasta el momento se realizaba de otra forma.</em></em></blockquote><figure class="kg-card kg-image-card"><img src="https://miro.medium.com/v2/resize:fit:700/0*H-KrMzFt_kMk2m_X.png" class="kg-image" alt="0*H-KrMzFt_kMk2m_X" width="700" height="301" loading="lazy"></figure><p>Con este concepto más amplio, podemos decir que los hooks, son funciones que simplifican procesos relacionados al ciclo de vida de componente funcionales, porque hasta la versión 16.7 de React, los componentes funcionales sólo recibían <em><em>props </em></em>y no era posible gestionar su estado, solo los componentes de clases (antigua forma de construir componentes, basada en POO ) soportaban está gestión.</p><p>Un detalle interesante, es que dada la usabilidad y potencia de los Hooks, no solo la librería base React actualmente tiene hooks, ahora otros paquetes de amplio uso también implementan hooks, tal como es el caso de <code><a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow">react-router-dom</a></code> por ejemplo.</p><h2 id="gesti-n-de-estado-usestate-en-reactjs">Gestión de estado - useState en ReactJS</h2><p>Uno de los puntos críticos y de suma importancia cuando construimos aplicaciones de una sóla página (SPA - Single Page Application) es la gestión de estado, dado que en el enfoque tradicional (html + css + js + server), la gestión es realizada mediante la gestión de sesiones en el lado del server, en el enfoque de ReactJS, la gestión es hecha a través de hooks, siendo el hook encargado de esta operación el <em>useState.</em></p><h2 id="conociendo-a-usestate">Conociendo a useState</h2><p>Este hook permite mantener el control de los estados de un componente, entiendo a los estados como las características o propiedades que definen el comportamiento y presentación del componente. Toda vez que un estado cambia, significa que el componente ha cambiado y por tanto debe actualizarse o renderizarse (es decir construirse nuevamente).</p><p>Tomemos un ejemplo práctico, una aplicación que presenta productos, en ese caso hablamos de un componente tipo "Galeria", que muestra datos y fotos de estos productos. Al iniciar no tiene productos, es decir, tenemos un estado tipo lista o arreglo, pero que está vacía al inicio, por lo tanto se presenta un mensaje de "Sin productos disponibles". Ahora mediante algún proceso (que vamos a entender más adelante), esa lista se llena con algunos datos, que debe suceder? que al recibirlos estos datos disparan un proceso de actualización del componente "Galeria".</p><p>Entendido el concepto teórico de funcionamiento de la gestión de estado, pasemos a colocar las manos en el código. Vamos a crear una aplicación con Vite y vamos a crear un componente funcional llamado <em>Galeria. </em>En este material puedes verificar como se realiza la creación de componentes en React. </p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/lPTvvi41frE?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="🚀 Aprende ReactJS: Componentes y Primeros Pasos | Guía para Principiantes 🚀" name="fitvid0"></iframe>
          </div>
        </div>
      </figure><p>Para definir un estado se sigue la siguiente anatomia:</p><pre><code>//Definición de un estado
const [estado, setEstado] = useState(&lt;valor inicial&gt;);</code></pre><p>Observa que siempre que se define un estado, se invoca al hook <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>useState</code></a> el cual nos retorna un arreglo que desestructuramos (recordar que la desestructuración está disponible en Javascript desde la versión ES2015) en 2 variables, una se refiere al estado en sí y otra es la función que permite asignar o atribuirle valor a ese estado. </p><p>Por ser de manejo especial, a los estados no le podemos asignar valor, como tradicionalmente lo hacemos en Javascript, entonces lo hacemos siempre a través de la función <em>setteadora</em>.</p><p>Por nuestra aplicación necesitar presentar múltiples productos, también va a ser necesario crear un componente para esta gestión. Es buena práctica en ReactJS, granular o despiezar de la manera más detallada la gestión de componentes, para que podamos aprovechar las características de la libreria y obtener un comportamiento óptimo de nuestras aplicaciones.</p><p>Veamos el código del &nbsp;componente <em>Galeria</em>:</p><pre><code class="language-Javascript">import React, { useState } from 'react'
import Product from '../Product/Product';

const productos = [
    {
        "_id": "652803510a598cf5573918f3",
        "nombre": "The Complete Common Core: State Standards Kit, Grade 5",
        "sku": "D82015FFBF",
        "precio": 12.88,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b006",
        "url": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f4",
        "nombre": "Flash Furniture 25''W x 45''L Trapezoid Red HP Laminate Activity Table - Height Adjustable Short Legs",
        "sku": "39F1B8A212",
        "precio": 117.26,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b00b",
        "url": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f3",
        "nombre": "The Complete Common Core: State Standards Kit, Grade 5",
        "sku": "D82015FFBF",
        "precio": 12.88,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b006",
        "url": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f4",
        "nombre": "Flash Furniture 25''W x 45''L Trapezoid Red HP Laminate Activity Table - Height Adjustable Short Legs",
        "sku": "39F1B8A212",
        "precio": 117.26,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b00b",
        "url": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f3",
        "nombre": "The Complete Common Core: State Standards Kit, Grade 5",
        "sku": "D82015FFBF",
        "precio": 12.88,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b006",
        "url": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/41gxkFaSFfL.jpg"
        },
        ]
    },
    {
        "_id": "652803510a598cf5573918f4",
        "nombre": "Flash Furniture 25''W x 45''L Trapezoid Red HP Laminate Activity Table - Height Adjustable Short Legs",
        "sku": "39F1B8A212",
        "precio": 117.26,
        "descripcion": "...",
        "imagenes": [
        {
        "_id": "654f7cae84c15e3104b1b00b",
        "url": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg",
        "nombre": "https://images-na.ssl-images-amazon.com/images/I/31WxfYlS8XL.jpg"
        },
        ]
    },
];


const Gallery = () =&gt; {
    //Definición del estado
    const [listaProductos, setListaProductos] = useState([]);

    const llenarProductos = () =&gt; {
        setListaProductos([...productos]);
    }

    //Si no hay producto emitimos un mensaje
    if (listaProductos.length === 0) {
        return &lt;&gt;
        &lt;h1&gt;No hay productos disponibles&lt;/h1&gt;
        &lt;button onClick={llenarProductos}&gt;Cargar productos&lt;/button&gt;
        &lt;/&gt;
    }
    
    //Mostramos los productos
  return (
    &lt;div style={{display:'flex', width:'100%', flexWrap:'wrap'}}&gt;
    {
        listaProductos.map((p) =&gt; {
            return &lt;Product product={p}&gt;&lt;/Product&gt;
        })
    }
    &lt;/div&gt;
    
  )
}

export default Gallery</code></pre><p>Expliquemos un poco el código anterior, empecemos por definir el estado, esto se encuentra en la siguiente línea:</p><pre><code class="language-Javascript">	//Definición del estado
    const [listaProductos, setListaProductos] = useState([]);</code></pre><p>Como indicamos anteriormente, realizamos la llamada al hook <code>useState</code> el cual nos retorna 2 variables, nuestro estado <code>listaProductos</code> y su función setteadora <code>setListaProductos</code>, notese que le pasamos a useState un arreglo vacío como valor inicial, de esta forma estamos indicando que el estado va a ser de tipo arreglo.</p><p>Luego tenemos una expresión de función que nos va a servir para llenar el arreglo a partir de un evento, este evento será el click de un botón que hemos agregado al mostrar el mensaje de "No hay productos disponibles".</p><p>Observa que en esta función <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>llenarProductos</code></a> se realiza la asignación del estado, de un modo que para quien viene de Javascript puro o vanilla, puede en principio no ser entendida, vamos explicar un poco porque se hace así:</p><pre><code>const llenarProductos = () =&gt; {
	setListaProductos([...productos]);
}</code></pre><p>Observa que se usa el <em>spread operator</em>, el cual es una característica disponible de ES2015, este operador básicamente lo que hace es crear un nuevo arreglo y lo que pasamos como: ...&lt;variable&gt;, se copia inmediamente a este nuevo arreglo, de esta forma lo que estamos realizando es la creación de un arreglo con los datos de productos, al ser un nuevo arreglo hay cambio de estado y ReactJS reacciona, actualizando el componente. Y por qué no usar el método <code>push</code> por ejemplo, porque en ese caso estariamos agregando un valor, solo que el arreglo para React no estaria modificado, por ser un tipo de dato estructurado y manejado por referencia, en esencia por ser inmutable, en este caso, para React el arreglo sigue igual, no hay cambio de estado por lo tanto no se renderiza.</p><p>Avanzando en el código nos encontramos la implementación del componente, donde lo primero que realizamos es la verificación de existencia o no de productos, si no existen se muestra un mensaje y un botón para cargar productos. En caso de existir productos, se hace un recorrido, recordando que por ser un arreglo, tenemos disponible sus métodos nativos de Javascript, entre ellos el método <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>map</code></a>.</p><p>Recorremos el arreglo y para cada producto construimos un componente de tipo <code>Product</code>. Este componente recibe los datos de cada producto y hace una presentación en pantalla de las informaciones. Sigue a continuación el código del componente <a href="https://reactrouter.com/en/main" rel="noopener ugc nofollow"><code>Product</code></a></p><pre><code>import React from 'react'

const Product = ({product}) =&gt; {
  return (
    &lt;div style={{display:'flex', width:'30%', border: '1px solid white', justifyContent:'center', flexDirection:'column', margin: '1rem',}}&gt;
        &lt;img src={product.imagenes[0]['url'] ?? './sinimagen.jpg'} style={{width:'100%', height:'20vh', objectFit:'contain'}}&gt;&lt;/img&gt;
        &lt;hr&gt;&lt;/hr&gt;
        &lt;div style={{height: '30vh'}}&gt;
            &lt;div&gt;
                &lt;h3 style={{overflowX:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap', maxHeight:'100%', }}&gt;{product.nombre}&lt;/h3&gt;
            &lt;/div&gt;
            &lt;div style={{height: '20vh'}}&gt;
                &lt;p style={{overflow:'hidden', maxHeight:'15vh', textOverflow:'ellipsis'}}&gt;{product.descripcion}&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;div style={{display:'flex'}}&gt;
            &lt;div&gt;
                Precio: {product.precio}
            &lt;/div&gt;
            &lt;div&gt;
                SKU: {product.sku}
            &lt;/div&gt;
        &lt;/div&gt;        
    &lt;/div&gt;
  )
}

export default Product</code></pre><p>Este código es simple, lo que retorna es el HTML necesario para presentar los datos de productos, le hemos hecho algunos ajustes en cuanto a CSS para presentar la información de una forma agradable. Nota que valimos si hay imágenes en el producto, de lo contrario presentamos una imagen por defecto.</p><p>Ya hemos visto como funciona la gestión de estado, solo que en este caso nuestros datos están en memoria o en el código, luego hemos tenido que hacer un clic para poder cargarlos. Avancemos en el uso de otro hook que nos va a permitir, consultar una API externa y luego cargar mediante la actualización del estado.</p><h2 id="efectos-secundarios-useeffect-en-reactjs">Efectos secundarios - useEffect en ReactJS</h2><p>Los efectos secundarios, son acciones que podemos ejecutar durante el ciclo de vida de un componente ReactJS, para ello detallemos &nbsp;cual es el ciclo de vida que ReactJS implementa:</p><ul><li>Montaje, es cuando el componente es creado.</li><li>Actualización, proceso ejecutado toda vez que un estado del componente cambia, por tanto es necesario reconstruir el componente</li><li>Desmontaje, cuando se destruye el componente, por ejemplo si nos cambiamos de vista.</li></ul><p>Analizando este ciclo, entendemos que nuestro componente es creado, solo se actualiza por un cambio de estado, el cual es generado a partir de un evento, generalmente ejecutado o invocado por el usuario y luego se destruye. Pensemos ahora que deseamos presentar nuestros productos al momento de que se cargue el componente, sólo que estos productos seran consultados a una API externa, por lo cual no se van a mostrar de inmediato, en el mejor de los casos puede que sean algunos segundos de tiempo, pero puede pasar que sea necesario un tiempo mayor, o peor aún que se genere algún error, en ese caso nuestro usuario va a estar esperando con una pantalla blanca, esto no es correcto.</p><p>Es allí en ese punto anterior, que <code>useEffect</code> y los efectos secundario entran en escena. Ahora lo anterior con <code>useEffect</code>, queda de la siguiente forma: </p><ul><li>Creamos el componente, sabiendo que no tiene datos, por lo tanto podemos informar al usuario de esto, o mejor aún podemos decirle que estamos buscando los datos.</li><li>Inmediatamente luego de creado el componente, podemos invocar a <code>useEffect</code>, el cual ejecutará un callback, donde nosotros tenemos la libertad de poder consumir la API indicada (recordando que esto se hace con promesas o async/await de Javascript).</li><li>Al llegar los datos podemos actualizar nuestro estado <code>listaProductos</code>, luego esto va a disparar la actualización del componente. Nuestro usuario va a estar atento y siempre informado de lo que sucede (usabilidad)</li></ul><p>Y como se implementan los pasos anteriormente descritos?, vayamos al código, tomando en cuenta que vamos a consumir una API que se encuentra en la siguiente URL: <a href="https://apiexpress-x7sl.onrender.com/productos">https://apiexpress-x7sl.onrender.com/productos</a>, esta API fue desarrollada por mi y es pública, así que puedes usarla para que realices tus ejercicios.</p><h2 id="implementando-el-consumo-de-una-api-con-reactjs">Implementando el consumo de una API con ReactJS</h2><p>En nuestro caso, el consumo de datos que queremos realizar es a nivel del componente <em>Galeria</em>, el cual actualmente tiene los datos en memoria, como debemos ajustar su código, primero empecemos por conocer la anatomía del useEffect:</p><pre><code>useEffect(() =&gt; {
      //Callback a ejecutar en el proceso de montaje y/o actualización
    
      return () =&gt; {
        //Callback a ejecutar en el proceso de desmontaje
      }
    }, [Arreglo de dependencias para saber cuando ejecutar el useEffect])</code></pre><p>Con lo anterior podemos percibir que el <code>useEffect</code> cumple a cabalidad el ciclo de vida de componentes, de acuerdo a lo indicado anteriormente:</p><ul><li>Ejecuta un callback (bloque de código o función) al hacer el montaje (Paso 1 del ciclo de vida) y al hacer actualizaciones (Paso 2 del ciclo de vida, esta ejecución es opcional y va a depender del arreglo de dependencias).</li><li>Puede ejecutar un callback al hacer el desmontaje (Paso 3 del ciclo de vida, este es opcional)</li></ul><p>Sólo hay una restricción en el <code>useEffect</code>, se ejecuta siempre en el proceso de montaje de acuerdo a lo anterior.</p><p>Expliquemos ahora el arreglo de dependencias:</p><ul><li>Si el arreglo no se indica, el <code>useEffect</code> se ejecuta tanto en el montaje como en cualquier cambio de estado.</li><li>Si el arreglo se indica vacio [], el <code>useEffect</code>se ejecuta solo en el proceso de montaje.</li><li>Si dentro del arreglo, indicamos algunos estados, el <code>useEffect</code> se ejecuta tanto en el montaje, como cuando esos estados indicados cambien. </li></ul><p>Con ello tenemos flexibilidad para indicar, como queremos que sea ejecutado este efecto secundario.</p><p>Veamos como queda nuestro código luego de implementado el <code>useEffect</code>:</p><pre><code>import React, { useState, useEffect } from 'react'
import Product from '../Product/Product';



const Gallery = () =&gt; {
    //Definición del estado
    const [listaProductos, setListaProductos] = useState([]);

    useEffect(() =&gt; {
      const obtenerDatos = async () =&gt; {
        const res = await fetch('https://apiexpress-x7sl.onrender.com/productos');
        const data = await res.json();
        setListaProductos([...data]);
      }

      obtenerDatos();

    }, []);
    

    //Si no hay producto emitimos un mensaje
    if (listaProductos.length === 0) {
        return &lt;&gt;
        &lt;h1&gt;Cargando productos&lt;/h1&gt;
        &lt;/&gt;
    }
    
    //Mostramos los productos
  return (
    &lt;div style={{display:'flex', width:'100%', flexWrap:'wrap'}}&gt;
    {
        listaProductos.map((p, i) =&gt; {
            return &lt;Product key={i} product={p}&gt;&lt;/Product&gt;
        })
    }
    &lt;/div&gt;
    
  )
}

export default Gallery</code></pre><p>Expliquemos los cambios que hemos realizado:</p><ul><li>Ya no es necesario tener el arreglo de productos, ni el botón para cargar datos, ni el handler para el clic <code>llenarProductos</code>, con esto nuestro código ha quedado más simple.</li><li>Luego hemos implementado el <code>useEffect</code> de la siguiente forma:</li></ul><pre><code>useEffect(() =&gt; {
      const obtenerDatos = async () =&gt; {
        const res = await fetch('https://apiexpress-x7sl.onrender.com/productos');
        const data = await res.json();
        setListaProductos([...data]);
      }

      obtenerDatos();

    }, []);</code></pre><p>Tenemos una función de callBack llamada <code>obtenerDatos</code>, la cual de forma asincronica, se conecta con la API (<a href="https://apiexpress-x7sl.onrender.com/productos">https://apiexpress-x7sl.onrender.com/productos</a>) y obtiene una respuesta, luego pedimos solo los datos en formato JSON, tal como indicamos que la API los retornaba, un ejemplo de los datos se puede ver en la siguiente imagen:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-1.png" class="kg-image" alt="image-1" srcset="https://www.freecodecamp.org/espanol/news/content/images/size/w600/2023/11/image-1.png 600w, https://www.freecodecamp.org/espanol/news/content/images/size/w1000/2023/11/image-1.png 1000w, https://www.freecodecamp.org/espanol/news/content/images/2023/11/image-1.png 1074w" sizes="(min-width: 720px) 720px" width="1074" height="699" loading="lazy"><figcaption>Ejemplo de retorno de la API que estamos usando</figcaption></figure><p>Estos datos nos han quedado disponibles en la variable: <code>data</code>, ahora esta variable que es un arreglo de objetos, se lo asignamos al estado <code>listadoProductos</code>, con lo que explicamos anteriormente, este cambio de estado dispara la actualización del componente <em>Galeria</em>, y con ello hemos podido lograr el objetivo planteado, consumir datos via API y usando <code>useState</code> y <code>useEffect.</code></p><p>En el siguiente video podrás apreciar la construcción de todo lo explicado anteriormente.</p><figure class="kg-card kg-embed-card" data-test-label="fitted">
        <div class="fluid-width-video-container">
          <div style="padding-top: 56.49999999999999%;" class="fluid-width-video-wrapper">
            <iframe width="200" height="113" src="https://www.youtube.com/embed/TTEb_AkGMEc?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="" title="🌐 Consume APIs con ReactJS: Aprende useEffect y useState | Tutorial Completo 🌐" name="fitvid1"></iframe>
          </div>
        </div>
      </figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Cómo crear APIs seguras con Flask y Auth0 ]]>
                </title>
                <description>
                    <![CDATA[ Las APIs son el corazón del desarrollo moderno. Soportan todo tipo de sistemas, desde móviles, web, y aplicaciones de escritorio, hasta dispositivos IoT y autos autónomos. Son un puente entre tus clientes y la lógica y almacenamiento de tu aplicación.  Este punto central de acceso a la información de ]]>
                </description>
                <link>https://www.freecodecamp.org/espanol/news/como-crear-apis-seguras-con-flask-y-auth0/</link>
                <guid isPermaLink="false">6402bea42154fe0736d6379e</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Josué Leiva ]]>
                </dc:creator>
                <pubDate>Mon, 17 Apr 2023 02:48:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/espanol/news/content/images/2023/04/secureApis.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artículo original:</strong> <a href="https://www.freecodecamp.org/news/build-secure-apis-with-flask-and-auth0/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Build Secure APIs with Flask and Auth0</a>
      </p><p>Las APIs son el corazón del desarrollo moderno. Soportan todo tipo de sistemas, desde móviles, web, y aplicaciones de escritorio, hasta dispositivos IoT y autos autónomos. Son un puente entre tus clientes y la lógica y almacenamiento de tu aplicación. </p><p>Este punto central de acceso a la información de tu aplicación plantea la pregunta: ¿Cómo puedes proveer acceso a la información a quienes lo necesitan mientras deniegas el acceso a peticiones no autorizadas?</p><p>La industria ha provisto de diversos protocolos y buenas prácticas para asegurar APIs. Hoy nos enfocaremos en <a href="https://auth0.com/docs/authorization/protocols/protocol-oauth2?utm_source=freecodecamp?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">OAuth2</a> <em>(artículo en inglés)</em>, una de las opciones más populares para autorizar clientes en nuestras APIs.</p><p>Pero ¿cómo implementamos OAuth2? hay dos formas de hacerlo:</p><ol><li>La forma DIY (hacerlo tu mismo)</li><li>Trabajar con un tercero seguro como <a href="https://auth0.com/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">Auth0</a></li></ol><p>En este artículo, te guiaré a través de una implementación de OAuth2 para Python y <a href="https://flask.palletsprojects.com/">Flask</a> <em>(documentación en inglés)</em> usando Autho como nuestro proveedor de identidad. Pero primero, debemos discutir la forma DIY.</p><h2 id="-por-qu-no-construir-tu-propia-autenticaci-n-y-autorizaci-n">¿Por qué no construir tu propia autenticación y autorización?</h2><p>Por algunos años ahora, he querido devolver a la comunidad que tanto me ha ayudado al enseñarme programación y ayudarme a progresar en mi búsqueda de conocimiento. Siempre he pensado que una &nbsp;gran forma de contribuir era al tener mi propio blog, algo que he intentado más de algunas veces y fallado. </p><p>Pero ¿dónde fallé? En vez de enfocarme en escribir, intenté construir mi propio motor de blog, ya que está en mi naturaleza. Es lo que los desarrolladores hacen. Les encanta construir.</p><p>Pero ¿por qué menciono esto aquí? Ya que muchos caen en la misma trampa al momento de construir APIs. Permíteme explicarlo con un ejemplo.</p><p>Bob es un gran desarrollador, y él tiene esta gran idea de hacer una app de ToDo <em>(lista de tareas pendientes)</em> que podría ser la siguiente gran app. Bob está en conocimiento de que para una implementación exitosa, los usuarios pueden acceder sólo su propia información.</p><p>Esta es la línea de tiempo de la app de Bob:</p><ul><li>Sprint 0: Investigar ideas y crear prototipos.</li><li>Sprint 1: Construir la tabla de usuarios y vista de inicio de sesión con una API.</li><li>Sprint 2: Añadir vistas de reset de contraseñas y construir las plantillas de email</li><li>Sprint 3: Construir, crear y listar las vistas de ToDos</li><li>Sprint 4: MVP se publica</li><li>Retroalimentación de usuarios:</li><li>Algunos usuarios no pueden ingresar debido a un bug.</li><li>Algunos usuarios se sienten inseguros sin autenticación de 2 factores.</li><li>Algunos usuarios no quieren otra contraseña más. Prefieren ingresar usando Google o Facebook.</li></ul><p>Hablemos sobre lo que ha pasado. Bob pasó los primeros sprints no construyendo su app pero construyendo los bloques básicos, como las funcionalidades de login, notificaciones de email y así. Este valioso tiempo lo podría haber usado de forma distinta, pero lo que sucede a continuación es más preocupante.</p><p>El backlog de Bob se comienza a llenar. Ahora él necesita improvisar un método de autenticación de 2do factor, añadir un ingreso rápido (google) y algunas funciones no relacionadas con el producto que podrían potencialmente retrasar su producto.</p><p>Y aún hay una gran pregunta para ser respondida: ¿Implementó Bob todos los mecanismos de seguridad correctamente? Un error crítico podría exponer toda la información de los usuarios a terceros.</p><p>Lo que Bob hizo es lo que yo hice en mi blog muchas ocasiones. Algunas veces, es de gran ayuda descansar en terceros si queremos hacer las cosas bien.</p><p>Hoy en día, los hackers y ataques se han hecho tan sofisticados que la seguridad ya no es un factor trivial. Es un sistema complicado en sí, y es usualmente mejor dejar esa implementación a expertos - no solo para que se haga bien, sino para que también nos podamos enfocar en lo que importa: construir nuestra aplicación y sus APIs.</p><h2 id="c-mo-configurar-una-cuenta-gratuita-de-administraci-n-de-identidades-en-auth0"><strong>Cómo Configurar una Cuenta Gratuita de Administración de Identidades en Auth0 </strong></h2><p><a href="https://auth0.com/?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">Auth0</a> es un proveedor líder en autorización y autenticación, pero veamos cómo puede ayudar a Bob (o a tí) a construir una app mejor.</p><ol><li>Ahorra tiempo</li><li>Es segura</li><li>Tiene un plan gratis</li></ol><p>Es tiempo de ponerse prácticos. Primero, asegúrate de tener una cuenta Auth0. Sino, puedes crear una <a href="https://auth0.com/es/signup?place=header&amp;type=button&amp;text=registrarse">aquí gratuitamente</a>.</p><h3 id="crear-una-nueva-api-auth0"><strong>Crear una nueva API Auth0</strong></h3><p>Aún hay una cosa que debemos hacer antes de comenzar a programar. Ve a la sección de <a href="https://manage.auth0.com/#/apis?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">APIs</a> de tu dashboard en Auth0 y haz click en el botón "Crear API" <em>(Create API).</em> Después de ello, &nbsp;llena el formulario con tus detalles. Sin embargo, asegúrate que selecciones &nbsp;<code>RS256</code> como <code>Signing Algorithm</code>.</p><p>Tu formulario debiera verse así:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://lh5.googleusercontent.com/XccGez21ClEDsCECuKwiF_1AF5gj2OXXaJKEXVUOBFmxQ7Ci11a1g1O3cu_io185YbdnSJkAlu3dmP0pt6Ww-N6cPqQLTIeweSi2hNv4ototIkuSZhfiprjqcMrFhcMLaGkKfedkm8D0PR2IcjdLPGUChKS27wsiPMvqCsysQRJyGANVYc5Q5EbFdaFo" class="kg-image" alt="XccGez21ClEDsCECuKwiF_1AF5gj2OXXaJKEXVUOBFmxQ7Ci11a1g1O3cu_io185YbdnSJkAlu3dmP0pt6Ww-N6cPqQLTIeweSi2hNv4ototIkuSZhfiprjqcMrFhcMLaGkKfedkm8D0PR2IcjdLPGUChKS27wsiPMvqCsysQRJyGANVYc5Q5EbFdaFo" width="1298" height="1252" loading="lazy"><figcaption>Creating the API – image showing fields to fill out</figcaption></figure><p>Después de crear un API exitosamente, se abre la página de detalles. Mantén esa pestaña abierta, ya que contiene información que necesitaremos para configurar nuestra aplicación. Si la cierras, no te preocupes, siempre puedes acceder a ella nuevamente.</p><h2 id="c-mo-impulsar-nuestra-aplicaci-n"><strong>Cómo impulsar nuestra aplicación</strong></h2><p>Ya que nos enfocaremos sólo en aspectos de seguridad, tomaremos algunos atajos al construir nuestra demo de API. Sin embargo, cuando desarrolles <a href="https://livecodestream.dev/post/python-flask-api-starter-kit-and-project-layout/">APIs reales</a> <em>(artículo en inglés)</em>, por favor sigue las <a href="https://auth0.com/blog/best-practices-for-flask-api-development/">mejores prácticas para APIs Flask</a> <em>(artículo en inglés).</em> </p><h3 id="instalar-las-dependencias"><strong>Instalar las dependencias</strong></h3><p>Primero, instala las siguientes dependencias para establecer Flask y autenticar usuarios.</p><pre><code class="language-shell">pipenv install flask python-dotenv python-jose flask-cors six</code></pre><h3 id="construir-los-endpoints"><strong>Construir los endpoints</strong></h3><p>Nuestra API será directa. Consistirá sólo de tres endpoints, las cuales todas serán, por ahora, accesibles públicamente. Sin embargo, arreglaremos eso pronto. Acá están nuestros endpoints:</p><ul><li><code>/</code> (endpoint público)</li><li><code>/user</code> (requiere un usuario autenticado)</li><li><code>/admin</code> (sólo los usuarios de rol admin)</li></ul><p>Vamos a ello:</p><pre><code class="language-python">from flask import Flask

app = Flask(__name__)

@app.route("/")
def index_view():
    """
    Endpoint por defecto, es publico y puede ser accedido por cualquiera
    """
    return jsonify(msg="Hello world!")

@app.route("/user")
def user_view():
    """
    Endpoint Usuario, solo puede ser accedido por un usuario autorizado
    """
    return jsonify(msg="Hello user!")

@app.route("/admin")
def admin_view():
    """
    Endpoint Admin, solo puede ser accedido por un admin
    """
    return jsonify(msg="Hello admin!")
</code></pre><p>Bastante simple, no? Vamos a ejecutarlo:</p><pre><code class="language-shell">~ pipenv run flask run
* Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)</code></pre><p>Y si accedemos nuestro endpoint:</p><pre><code class="language-shell">~ curl -i http://localhost:5000
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:24:57 GMT

{"msg":"Hello world!"}

~ curl -i http://localhost:5000/user
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 22
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:25:42 GMT

{"msg":"Hello user!"}

~ curl -i http://localhost:5000/admin
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:26:18 GMT

{"msg":"Hello admin!"}</code></pre><h2 id="c-mo-asegurar-los-endpoints"><strong>Cómo asegurar los endpoints</strong></h2><p>Ya que estamos usando OAuth, vamos a autenticar las peticiones al validar un token de acceso en <a href="https://auth0.com/es/learn/json-web-tokens">formato JWT</a>. Lo enviaremos a la API en cada petición como parte de las cabeceras HTTP.</p><h3 id="variables-de-configuraci-n-auth0"><strong>Variables de Configuración Auth0</strong></h3><p>Como mencionado en la sección previa, nuestra API necesita ser consciente y requerirá información de nuestro dashboard Auth0. Así que ve devuelta a tu <a href="https://manage.auth0.com/#/apis?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">página detallada de API</a> y agarra dos valores distintos.</p><p><strong>Primero, el identificador de API: &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </strong></p><p>Este es el valor requerido cuando la API es creada. También puedes obtenerla desde tu página detallada de API:</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://lh5.googleusercontent.com/UffKcasZXNZmXldeB8nhDEjzmOPVao3PR6EUVPbtWzXStuDzcCw2kr5ztEnr0VlWCkBLbhleAM-D11Cv5Cv8fcII8m24D6TfEe4XfxWe8HXN1aNrF-dHeN05zeVeoNfQISWh-VPf0__x8uVfJPL3GGHYIC87utfrr6734Z9Wdk-9eJUApslcdUKOyoSh" class="kg-image" alt="UffKcasZXNZmXldeB8nhDEjzmOPVao3PR6EUVPbtWzXStuDzcCw2kr5ztEnr0VlWCkBLbhleAM-D11Cv5Cv8fcII8m24D6TfEe4XfxWe8HXN1aNrF-dHeN05zeVeoNfQISWh-VPf0__x8uVfJPL3GGHYIC87utfrr6734Z9Wdk-9eJUApslcdUKOyoSh" width="1600" height="699" loading="lazy"><figcaption>How to find the API identifier on the API details page</figcaption></figure><p><strong>Siguiente, dominio Auth0:</strong> </p><p>A menos que estés utilizando un dominio customizado, este valor será <a href="about:blank"><code>[NOMBRE_TENANT].auth0.com</code></a>, y puedes agarrarlo desde la pestaña <code>Test</code> (asegúrate no incluir <code>https://</code> &nbsp;y la última barra diagonal <code>/</code>).</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://lh4.googleusercontent.com/cA63NdLr4AWOz2O3jTWBXTTqc7DrGOr1aPOIpNDRYl97-o84I_lX8KtotCm6hRWF06ai0RjiJzgTjS_zRlySKFAB-XO1w737N05i7-bC2-GZioOpcWuS5gaRoEnDL63gXnm5CyP6JOEQusRLQMF1sY_1vjfXtdMVIr5uCW1PMIpokH76lpMq2VFZSIyf" class="kg-image" alt="cA63NdLr4AWOz2O3jTWBXTTqc7DrGOr1aPOIpNDRYl97-o84I_lX8KtotCm6hRWF06ai0RjiJzgTjS_zRlySKFAB-XO1w737N05i7-bC2-GZioOpcWuS5gaRoEnDL63gXnm5CyP6JOEQusRLQMF1sY_1vjfXtdMVIr5uCW1PMIpokH76lpMq2VFZSIyf" width="1600" height="1037" loading="lazy"><figcaption>Getting the Auth0 domain</figcaption></figure><p>Siguiente, traspasa esos valores a variables para que así puedan ser utilizadas en las funciones de validación.</p><pre><code class="language-python">AUTH0_DOMAIN = 'TU-DOMINIO-AUTH0'
API_IDENTIFIER = 'IDENTIFICADOR-API'
ALGORITHMS = ["RS256"]</code></pre><h3 id="m-todos-de-error"><strong>Métodos de error</strong></h3><p>Durante esta implementación, necesitaremos una manera de lanzar errores cuando la autenticación falla. Así que usaremos los siguientes ayudantes para esos menesteres:</p><pre><code class="language-python">class AuthError(Exception):
    def __init__(self, error, status_code):
        self.error = error
        self.status_code = status_code

@app.errorhandler(AuthError)
def handle_auth_error(ex):
    response = jsonify(ex.error)
    response.status_code = ex.status_code
    return response</code></pre><h3 id="c-mo-capturar-el-token-jwt"><strong>Cómo capturar el token JWT</strong></h3><p>El primer paso para validar un usuario es obtener el token JWT desde las cabeceras HTTP. Esto es bastante simple, pero hay algunas cosas a tener en mente. Aquí hay un ejemplo de ello:</p><pre><code class="language-python">def get_token_auth_header():
    """
    Obtiene el Token de Acceso desde la cabecera Authorization
    """
    auth = request.headers.get("Authorization", None)
    if not auth:
        raise AuthError({"code": "authorization_header_missing",
                        "description":
                            "Se esperaba cabecera de autenticacion"}, 401)

    parts = auth.split()

    if parts[0].lower() != "bearer":
        raise AuthError({"code": "invalid_header",
                        "description":
                            "La cabecera de autenticacion debe comenzar con"
                            " Bearer"}, 401)
    elif len(parts) == 1:
        raise AuthError({"code": "invalid_header",
                        "description": "Token no encontrado"}, 401)
    elif len(parts) &gt; 2:
        raise AuthError({"code": "invalid_header",
                        "description":
                            "La cabecera debe ser"
                            " Bearer token"}, 401)

    token = parts[1]
    return token</code></pre><h3 id="c-mo-validar-el-token"><strong>Cómo validar el token</strong></h3><p>Tener un token traspasado a nuestra API es una buena señal, pero no significa que es un cliente válido. Necesitamos revisar la firma del token.</p><p>Ya que la lógica para requerir autenticación puede ser utilizada para más de un endpoint, sería importante abstraerla y hacerla fácilmente accesible para los desarrolladores su implementación. La mejor forma de hacer esto es usando <a href="https://book.pythontips.com/en/latest/decorators.html">decoradores</a> <em>(artículos en inglés)</em>.</p><pre><code class="language-python">def requires_auth(f):
    """
	Determina si el Token de Acceso es valido
    """
    @wraps(f)
    def decorated(*args, **kwargs):
        token = get_token_auth_header()
        jsonurl = urlopen("https://"+AUTH0_DOMAIN+"/.well-known/jwks.json")
        jwks = json.loads(jsonurl.read())
        unverified_header = jwt.get_unverified_header(token)
        rsa_key = {}
        for key in jwks["keys"]:
            if key["kid"] == unverified_header["kid"]:
                rsa_key = {
                    "kty": key["kty"],
                    "kid": key["kid"],
                    "use": key["use"],
                    "n": key["n"],
                    "e": key["e"]
                }
        if rsa_key:
            try:
                payload = jwt.decode(
                    token,
                    rsa_key,
                    algorithms=ALGORITHMS,
                    audience=API_IDENTIFIER,
                    issuer="https://"+AUTH0_DOMAIN+"/"
                )
            except jwt.ExpiredSignatureError:
                raise AuthError({"code": "token_expired",
                                "description": "token is expired"}, 401)
            except jwt.JWTClaimsError:
                raise AuthError({"code": "invalid_claims",
                                "description":
                                    "incorrect claims,"
                                    "please check the audience and issuer"}, 401)
            except Exception:
                raise AuthError({"code": "invalid_header",
                                "description":
                                    "Unable to parse authentication"
                                    " token."}, 401)

            _request_ctx_stack.top.current_user = payload
            return f(*args, **kwargs)
        raise AuthError({"code": "invalid_header",
                        "description": "Unable to find appropriate key"}, 401)
    return decorated</code></pre><p>El nuevo decorador creado <code>requires_auth</code>, cuando aplicado a un endpoint, automáticamente rechazará la petición si no hay un usuario válido que pueda ser autenticado. </p><h3 id="c-mo-requerir-una-petici-n-autenticada-para-un-endpoint"><strong>Cómo requerir una petición autenticada para un endpoint</strong></h3><p>Ya estamos listos para asegurar nuestros endpoints, actualicemos los <code>user</code> y <code>admin</code> para utilizar nuestro decorador.</p><pre><code class="language-python">@app.route("/user")
@requires_auth
def user_view():
    """
    Endpoint Usuario, solo puede ser accedido por un usuario autorizado
    """
    return jsonify(msg="Hello user!")

@app.route("/admin")
@requires_auth
def admin_view():
    """
    Endpoint Admin, solo puede ser accedido por un admin
    """
    return jsonify(msg="Hello admin!")</code></pre><p>Nuestro único cambio fue añadir <code>@required_auth</code> al comienzo de la declaración de cada función de endpoint, y con ello podemos probar una vez más:</p><pre><code class="language-shell">~ curl -i http://localhost:5000/user
HTTP/1.0 401 UNAUTHORIZED
Content-Type: application/json
Content-Length: 89
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:42:26 GMT

{"code":"authorization_header_missing","description":"Se esperaba cabecera de autenticacion"}

~ curl -i http://localhost:5000/admin
HTTP/1.0 401 UNAUTHORIZED
Content-Type: application/json
Content-Length: 89
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:42:42 GMT

{"code":"authorization_header_missing","description":"Se esperaba cabecera de autenticacion"}</code></pre><p>Como se esperaba, no podemos acceder a nuestros endpoints ya que la cabecera de autorización no se encuentra. Pero antes de añadir una, veamos si nuestro endpoint público aún funciona:</p><p>Como esperado</p><pre><code class="language-shell">~ curl -i http://localhost:5000
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 21:43:55 GMT

{"msg":"Hello world!"}</code></pre><p>Increíble, funciona como se esperaba.</p><h3 id="c-mo-probarlo"><strong>Cómo probarlo</strong></h3><p>Para probar nuestros nuevos endpoints asegurados, necesitamos obtener un token de acceso válido que podamos pasar a la petición. Podemos hacer eso directamente en la pestaña <code>Test</code> de la página detallada de la API, y es tan simple como copiar un valor desde la pantalla:<br></p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://lh5.googleusercontent.com/XCAWL5taQUs3_5qcAdukl9FP_aTVLya-jyS_4IivFW6JCAfX5d2hbPPCIV4PB8QgcuceQrzC__YYpWMQB1y8HT9AnKO01XH5rCiofvQJAmiAPnGF42FcJFxaVHTLLQcL9UpzFjYgan0Qasna69DlZ8AIkoATbqAtqtqibWUszhvakHZiytPNduTU7_Hb" class="kg-image" alt="XCAWL5taQUs3_5qcAdukl9FP_aTVLya-jyS_4IivFW6JCAfX5d2hbPPCIV4PB8QgcuceQrzC__YYpWMQB1y8HT9AnKO01XH5rCiofvQJAmiAPnGF42FcJFxaVHTLLQcL9UpzFjYgan0Qasna69DlZ8AIkoATbqAtqtqibWUszhvakHZiytPNduTU7_Hb" width="1600" height="1062" loading="lazy"><figcaption>Copying the token for testing</figcaption></figure><p>Once we have the token we can change our curl request accordingly:</p><p>Una vez que tengamos el token podemos cambiar nuestra petición de curl acorde a:</p><pre><code class="language-shell">~ curl -i -H "Authorization: bearer [TOKEN_DE_ACCESO]"  http://localhost:5000/user
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 22
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 22:17:06 GMT

{"msg":"Hello user!"}</code></pre><p>Por favor recuerda reemplazar <code>TOKEN_DE_ACCESO</code> con el valor que copiaste desde tu dashboard.</p><p>¡Funciona! Pero aún tenemos algo de trabajo que hacer. Incluso cuando nuestro endpoint <code>/admin</code> está asegurado, puede ser accedido por cualquier usuario:</p><pre><code class="language-shell">~ curl -i -H "Authorization: bearer [TOKEN_DE_ACCESO]"  http://localhost:5000/admin
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 23
Server: Werkzeug/2.0.1 Python/3.9.1
Date: Tue, 24 Jan 2023 22:21:09 GMT

{"msg":"Hello admin!"}</code></pre><h3 id="control-de-acceso-basado-en-roles"><strong>Control de acceso basado en roles</strong></h3><p>Para el control de acceso basado en roles hay algunas cosas que debemos hacer:</p><ol><li>Crear permisos para la API</li><li>Activar el añadir permisos a el token JWT para la API</li><li>Actualizar el código</li><li>Probar con usuarios</li></ol><p>Los primeros 2 puntos están bastante bien explicados en los <a href="https://auth0.com/docs/manage-users/access-control/configure-core-rbac">documentos Auth0</a> <em>(artículo en inglés)</em>,<em> </em>así que sólo asegúrate de añadir los permisos correspondientes en tu API. </p><p>Siguiente, necesitamos actualizar el código. Necesitamos una función para revisar si dado permiso existe en el token de acceso y retornar <code>True</code> si lo hace y <code>False</code>si no:</p><pre><code class="language-python">def requires_scope(required_scope):
    """
	Determina si el objetivo requerido esta presente en el Token de Acceso
    Args:
    	required_scope (str): El objetivo requerido para acceder al recurso
    """
    token = get_token_auth_header()
    unverified_claims = jwt.get_unverified_claims(token)
    if unverified_claims.get("scope"):
            token_scopes = unverified_claims["scope"].split()
            for token_scope in token_scopes:
                if token_scope == required_scope:
                    return True
    return False</code></pre><p>Y últimamente, puede ser como a continuación:</p><pre><code class="language-python">@app.route("/admin")
@requires_auth
def admin_view():
    """
	Endpoint Admin, solo puede ser accedido por un admin
    """
    if requires_scope("read:admin"):
        return jsonify(msg="Hello admin!")

    raise AuthError({
        "code": "Unauthorized",
        "description": "No tienes acceso a este recurso"
    }, 403)</code></pre><p>Ahora, sólo usuarios con el permiso <code>read:admin</code> pueden acceder a nuestro endpoint admin.</p><p>Con el fin de probar tu implementación final, puedes seguir los pasos detallados en <a href="https://auth0.com/docs/quickstart/backend/python/02-using#obtaining-an-access-token?utm_source=freecodecamp&amp;utm_medium=sc&amp;utm_campaign=securing_flask">obtener un token de acceso</a> <em>(artículo en inglés)</em> para un usuario determinado.</p><p>También puedes usar el Dashboard de Auth0 para probar los permisos, pero eso va fuera del enfoque de este artículo. Si te gustaría aprender más de ello, lee <a href="https://auth0.com/blog/permission-based-security-aspnet-webapi/#Testing-Permissions">aquí</a>.</p><h2 id="conclusi-n"><strong>Conclusión</strong></h2><p>Hoy hemos aprendido cómo asegurar una API Flask. Hemos explorado la forma hágalo-usted-mismo, y hemos construido una API segura con tres niveles de acceso - acceso público, acceso privado y acceso privado-focalizado.</p><p>Hay muchas cosas más que Auth0 puede hacer por tus APIs y también para las aplicaciones de tus clientes. Hoy sólo hemos raspado la superficie, y depende de tí y tu equipo cuando se trabajan en escenarios de la vida real el explorar el potencial de sus servicios.</p><p>El código completo está disponible en <a href="https://gist.github.com/bajcmartinez/5062aa41ccbe2df1bbf4f1a9b95bd085">Github</a>.</p><p>¡Gracias por leer! Si te gusta mi estilo de enseñanza, puedes <a href="https://livecodestream.dev/newsletter/">Suscribirte a mi newsletter semanal</a> para desarrolladores y constructores y obtener un email semanal lleno de contenido relevante.</p> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
