Original article: How to Hide API Keys in Frontend Apps using Netlify Functions
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 de usuarios y más.
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.
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.
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.
Prerrequisitos
Para seguir los pasos en este tutorial deberás contar con lo siguiente:
- Una cuenta en Netlify (regístrate aquí)
- Entendimiento básico de APIs RESTFUL, funciones Lambda y conceptos de asincronía (async/await).
El demo terminado lo puedes encontrar en la rama principal de GitHub o en este enlace: https://netlify-func-demo.netlify.app
.
¿Qué es una función Netlify?
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.
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.
"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
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).
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.
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.
Otras funciones serverless utilizadas para ejecutar código sin tener que manejar servidores incluyen: las AWS Lambda Functions, Azure Functions y Google Cloud Functions.
Cómo configurar una aplicación cliente
Cómo clonar la aplicación demo
Para empezar con este tutorial puedes clonar la "aplicación motor de búsqueda de fotos de stock" desde su repositorio GitHub. Mira la previsualización en Netlify en https://netlify-func-demo.netlify.app.
El primer paso es clonar el repositorio:
git clone https://github.com/frankiefab100/netlify-serverless-functions-demo.git
Paso siguiente, cambia (ingresa) al directorio netlify-serverless-functions-demo.
cd netlify-serverless-functions-demo
Luego tendrás que instalar las dependencias.
npm install
#OR
yarn add
Ahora ejecuta el servidor de desarrollo. Ejecuta el siguiente comando para este propósito:
netlify dev
La aplicación estará disponible en: https://localhost:8888
.
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.
Cómo construir la aplicación demo usando JavaScript
El primer paso es configurar la aplicación front-end (cliente). Abre tu editor de código favorito como, por ejemplo: VS Code.
A continuación, crea la carpeta dist y dentro crea el archivo index.html. Copia y pega ahí el siguiente código:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>Stock Photos Search Engine</title>
</head>
<body>
<div class="container">
<header>
<h1>Search For Stock Photos</h1>
<div class="search-section">
<input
type="text"
name="search"
class="search"
placeholder="Enter a keyword"
/>
<input
id="searchBtn"
class="search-btn"
type="submit"
value="Search"
/>
</div>
</header>
<div class="photo-wrapper">
<img src="" alt="" class="photo" />
</div>
</div>
<script src="./script.js" type="module"></script>
</body>
</html>
En el mismo directorio dist
agrega la siguiente hoja de estilos en un archivo llamado style.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;
}
Crea una cuenta gratuita en Pixabay
El primer paso para utilizar la Pixabay API es darse de alta una cuenta con tu correo electrónico.
Como se muestra en la imagen de arriba, tus llaves API las puedes encontrar debajo de la seccion de parámetros en la página oficial de documentación de Pixabay API.
Crea una variable de entorno
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.
Usar variables de entorno se recomienda para configurar servicios de terceros y sus credenciales durante el desarrollo.
Instala dotenv
Una vez que haya completado la creación de tu cuenta en Pixabay, abre tu terminal e instala el paquete dotenv. Esto permitirá que tu aplicación pueda leer las variables de entorno del archivo .env.
En el próximo paso podrás guardar la clave API en el archivo .env.
Crea el archivo .env
En la raíz del directorio de tu aplicación crea el archivo .env y almacena las llaves (keys) API copiadas de tu perfil Pixabay.
PIXABAY_API_KEY=123456-7890
Donde PIXABAY_API_KEY=123456-7890
representa la llave y su valor asociado.
Nota: Reemplaza este par clave/valor con el valor apropiado.
Crea el archivo .gitignore
Para evitar incluir archivos y valores sensibles como node_modules
y secret keys
en cada commit en repositorios públicos, crea el archivo .gitignore en la misma carpeta raíz del proyecto y agrega lo siguiente:
La carpeta .netlify 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.
Crea una función get request
Ahora deberás añadir la lógica para realizar la petición fetch en el archivo script.js. Ajustaremos la lógica más adelante usando funciones Netlify.
/* 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", () => {
getPhoto(searchbar.value);
searchbar.value = "";
});
window.addEventListener("keydown", (e) => {
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}&q=${keyword}&image_type=photo&safesearch=true&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) => {
let imageElement = document.createElement("img");
imageElement.setAttribute("src", `${result.webformatURL}`);
photoWrapper.appendChild(imageElement);
});
} catch (error) {
alert(error);
}
}
Nota: 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 .env
, mas adelante veremos como.
Para ilustrar esto, selecciona la rama testing
del repositorio de esta aplicación. La previsualización del sitio mostrara los siguientes errores de referencia (Reference Errors) en la consola del navegador:
Uncaught ReferenceError: require is not defined
Uncaught ReferenceError: require is not defined at getPhotos
Uncaught ReferenceError: process is not defined at getPhotos
Esto se debe a que no hay manera de vincular con las variables de entorno especificadas en el archivo .env, ya que este no se envió en el commit al repositorio público en GitHub.
En el siguiente paso, selecciona y clona la rama testing
en tu máquina de forma local con los siguientes comandos:
git clone https://github.com/frankiefab100/netlify-serverless-functions-demo.git
cd netlify-serveless-functions-demo
npm install
netlify dev
La aplicación estará disponible en tu navegador en la dirección localhost:8888
.
Ahora abre las herramientas para desarrolladores (Developer Tools) o haz clic derecho y selecciona inspeccionar. O puedes presionar la tecla F12. Luego vea la pestaña Network y haz clic en la URL de la petición del archivo getPhotos.js
.
Ahora deberías poder ver la clave API expuesta públicamente en la sección de encabezados de la pestaña Network donde fue devuelta con los datos de la respuesta en tu navegador.
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.
En la siguiente sección encontraras la manera segura de ocultas la clave API utilizando las funciones Netlify.
Cómo empezar a usar Netlify Functions
Primero, necesitarás iniciar sesión en tu terminal e instalar la Netlify CLI y Lambda como dependencias de desarrollo. Puedes hacerlo al ejecutar este comando:
npm install -g netlify-cli netlify-lambda
#OR
yarn add -D netlify-cli netlify-lambda --save-dev
Agrega los comandos personalizados build y dev en package.json
Estos comandos, crearán e iniciarán la aplicación en el servidor y también la ejecutarán en el nevegador. Aquí puedes ver un ejemplo de como podrías agregar este script con comandos en el archivo package.json:
"scripts": {
"build": "npm run-script",
"dev": "netlify dev"
}
Instala Axios
Usaremos el método axios.get
porque esta función de Node difiere del método fetch en que este último está pensado para correr en el navegador.
Para instalar Axios, abre tu terminal e ingresa el comando:
npm install axios
#OR
yarn add -D axios
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:
const axios = require("axios");
Para cuando se usan librerias como React, debes importar asi:
import axios from "axios";
Crea una función serverless
En la raíz del proyecto, crea una carpeta llamada netlify
y a su vez dentro de este crea otra carpeta functions
. En esta carpeta functions
crea un archivo llamado getPhotos.js
.
Crearás una función serverless en el archivo getPhotos
. Esto ocultará efectivamente las llaves API cuando traigamos las imágenes desde la API de Pixabay.
//netlify/functions/getPhotos.js
require("dotenv").config();
const axios = require("axios");
exports.handler = async (event, context) => {
try {
const { keyword } = event.queryStringParameters;
let response = await axios.get(
`https://pixabay.com/api/?key=${process.env.PIXABAY_API_KEY}&q=${keyword}&image_type=photo&safesearch=true&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 }),
};
}
};
Aquí la clave process.env.PIXABAY_API_KEY
hace referencia al entorno de configuración de la clave API especificado en el archivo .env
para el modo de desarrollo.
El parámetro keyword
acepta una cadena de texto (string) que es accesible en la propiedad queryStringParameters
y devuelve datos commo respuesta almacenados en la variable imageURL
. Esta será enviada al archivo script.js
como respuesta a la petición (veremos esto más adelante).
Si la petición GET es exitosa, devolverá una respuesta con el código (statusCode
) 200 y el objeto JSON correspondiente. En caso de errores, recibiremos una alerta con el mensaje y código de estado.
Debido a cambios de versión, Axios podría devolver un Buffer como respuesta en tu terminal que se vera algo como esto:
Para evitar esto deberás adjuntar lo siguiente a la petición GET:
let response = await axios.get(
`https://pixabay.com/api/?key=${process.env.PIXABAY_API_KEY}&q=${keyword}&image_type=photo&safesearch=true&per_page=3`,
{
headers: { Accept: "application/json", "Accept-Encoding": "identity" },
params: { trophies: true },
}
);
Crea un archivo de configuracion Netlify
En la raíz del proyecto, crea el archivo netlify.toml
. Este archivo especifica como es que Netlify construirá y desplegará tu aplicación.
Ahora, agrega la siguiente configuración en el archivo netlify.toml
:
[build]
command = "npm run build"
functions = "netlify/functions"
publish = "dist"
Nota:
command = "npm run build"
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.functions = "netlify/functions"
indica que las funciones engetPhotos
existen en ña carpetanetlify/functions
.publish = "dist"
identificadist
como la carpeta desde donde será "servido" este archivo.
Edita el archivo script.js con la URL para las peticiones a Netlify functions
Siguiente paso, edita la apiURL
de esto:
let apiURL = `https://pixabay.com/api/?key=${apiKey}&q=${keyword}&image_type=photo&safesearch=true&per_page=3`;
A esto (el endpoint para la petición HTTP a las Netlify Functions):
let apiURL = `/.netlify/functions/getPhotos?keyword=${keyword}`;
Esta función "serverless" será consultada desde el lado del cliente de tu aplicación mediante el endpoint: /.netlify/functions/getPhotos
. Una vez que la solicitud es enviada, la función getPhotos
será invocada desde script.js
.
La respuesta imageURL
obtenida de la Netlify functions getPhotos
será pasada o enviada como el valor asignado al parámetro keyword
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.
El archivo script.js debe verse así:
/* dist/script.js */
const searchbar = document.querySelector(".search");
const submitBtn = document.querySelector(".search-btn");
const photoWrapper = document.querySelector(".photo-wrapper");
submitBtn.addEventListener("click", () => {
getPhoto(searchbar.value);
searchbar.value = "";
photoWrapper.innerHTML = "";
});
window.addEventListener("keydown", (e) => {
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 < data.imageURL.length; i++) {
let imageElement = document.createElement("img");
imageElement.setAttribute("src", `${data.imageURL[i].webformatURL}`);
photoWrapper.appendChild(imageElement);
}
} catch (error) {
alert(error);
}
}
Nota: En el código de arriba mantiene tu variable de entorno segura ya que se accede a esta desde una función serverless.
Ejecuta el servidor de desarrollo
Ahora, ejecuta tu aplicación con el siguiente comando:
netlify dev
Esto hará que se cargue la función getPhotos mediante https://localhost:8888/.netlify/functions/getPhotos.
Luego, inicia el servidor de desarrollo y lanza la aplicación en tu navegador por defecto en localhost:8888
.
En este punto, la configuración de la función Netlify está completa y ya es posible realizar solicitudes GET
HTTP.
Cómo realizar solicitudes para traer datos
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 Enter o haz clic en el botón para traer las imágenes desde la API de Pixabay.
Para mayor información acerca del API de Pixabay, ve a su sitio oficial de documentación.
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:
Request from ::1: GET /.netlify/functions/getPhotos?keyword=flower
Response with status 200 in 4895 ms.
También puedes utilizar las herramientas de desarrollador (Developer Tools) en la pestaña Network (Red) para validar la solicitud.
La solicitud devuelve la URL de nuestra aplicación, la función Netlify getPhotos, el script.js y las imágenes de Pixabay.
Cómo hospedar la aplicación en el repositorio remoto
Ahora, deberías empujar tu proyecto al hosting de control de versiones GitHub.
git add .
git commit -m"initial commit"
git push origin -u main
Cómo desplegar la aplicación y la función serverless en Netlify
Una vez que hayas publicado el proyecto en GitHub, inicia sesión en Netlify y conecta tu cuenta GitHub para dar autorización.
Como se muestra en la imagen de arriba, haz clic en Add New Project
y luego busca el repositorio de tu aplicación en la lista desplegable. Enseguida, haz clic en Build Your Site
. Esto tomará algunos minutos en completarse.
Acabas de desplegar la aplicación usando la UI de Netlify. Ahora tu aplicación estará disponible en: https://netlify-func-demo.netlify.app.
La URL para peticiones "fetch" (del inglés traer) debería verse como ésta: https://netlify-func-demo.netlify.app/.netlify/functions/getPhotos
.
Opcional - Cómo configurar la aplicación desde el tablero Netlify
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 netlify.toml, se sobreescribirán cualquier configiracion correspondiente.
Primero, selecciona la configuración de la carpeta del proyecto en "Site settings".
Configura el comando Build y el directorio público
Ve a Site settings
> Deploy
> Build & Deploy
, edita los siguientes comandos y luego guarda los cambios:
- Commando build: npm run build
- Directorio público: dist
Configura la carpeta de las funciones
Por defecto, Netlify utiliza netlify/functions
como el directorio en el cual encontrar las funciones a desplegar. En este caso, nuestra función serverless getPhotos
se encuentra en la carpeta netlify/funcions
.
Ahora configuraremos una carpeta personalizada donde Netlify puede encontrar tus funciones compiladas. Ve a Site settings
> Functions
e ingresa la ruta la carpeta en donde se encuentran almacenadas las funciones de tu repositorio.
Cómo configurar variables de entorno para producción
En el panel de control de Netlify (Dashboard) haz clic en Site settings
> Build & deploy
> Environment
> Environment varibales
y luego configura el par clave/valor de la siguiente manera:
PIXABAY_API_KEY=your-api-key-here
Haz clic en guardar, y enseguida redespliega la aplicación para que los cambios se apliquen.
Para redesplegar la aplicación en Netlify, ve a Deploys
> Trigger deploy
. A continuación, selecciona Clear cache and redeploy site
.
Nota: El nombre de la variable de entorno (PIXABAY_API_KEY) debe coincidir con el que se referencia en el código en getPhotos
.
Para una aplicación React, usa el prefijo REACT_APP_
para que puedan ser leídas desde el archivo .env
.
REACT_APP_PIXABAY_API_KEY=your-api-key-here
Cómo inicializar la función serverless desde la aplicación remota
Para conectar la carpeta de tu proyecto a la aplicación web ya desplegada, inicia sesión en tu cuenta Netlify desde la terminal:
netlify login
A continuación, inicializa la aplicación en Netlify ejecutando el siguiente comando en tu terminal:
netlify init
Tu aplicación ahora está configurada para despliegues continuos via Netlify.
Cómo compilar la función serverless de Netlify
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:
netlify link
Luego de esto, se te pedirá que enlaces la carpeta mediante cualquiera de las siguientes formas:
- Búsqueda por nombre completo o parcial del sitio.
- Elegir de una lista de tus sitios recientemente editados.
- Ingresa el ID de tu sitio.
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 CLI de Netlify y automáticamente desplegar el proyecto desde el repositorio cada que haya cambios en el código.
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 netlify.toml
, ejecuta el siguiente comando:
netlify build
Esto a su vez, ejecutará el comando npm run-script
como fue especificado en el package.json
. Ahora, tu función serverless que está en la carpeta netlify/functions
será empaquetado ¡exitosamente!
Cómo poner a prueba la aplicación
Para realizar pruebas y confirmar que la función Netlify funciona correctamente, ejecuta el comando siguiente en tu terminal:
netlify functions:serve
Esto inyecta las variables de entorno de tu proyecto del archivo .env y ejecuta la función serverless..
Cómo confirmar la seguridad de la clave API Keys
Para inspeccionar tu aplicación y confirmar que las claves API se encuentran ocultas sigue los pasos a continuación:
Haz clic en la URL de tu aplicación hospedada y una vez abierta, navega a las herramientas de desarrollador (Developer Tools) presionando la tecla F12 o haciendo clic en cualquier lugar y seleccionando Inspeccionar. Navega a la pestaña de Red, donde deberías ver los datos traídos desde la API Pixabay.
Ahora ya has confirmado que has configurado exitosamente una función serverless y la has desplegado en Netlify.
Conclusión
Este tutorial introdujo las funciones serverless, JavaScript asíncrono y conceptos de APIs RESTful.
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.
Si te atoraste con algo, puedes tener acceso al código completo en el Repositorio en GitHub.
¡Gracias por leer el artículo! Sígueme en Twitter.