Construyendo un Backend Serverless con Google Cloud Functions y Firestore
Este tutorial te guiará paso a paso en la creación de un backend serverless robusto y escalable utilizando Google Cloud Functions y Firestore. Descubrirás cómo configurar tu entorno, desplegar funciones sin servidor y persistir datos eficientemente, todo ello sin gestionar infraestructura.
Introducción al Backend Serverless con Google Cloud Functions y Firestore 🚀
El desarrollo de aplicaciones modernas exige escalabilidad, bajo mantenimiento y eficiencia de costos. La arquitectura serverless, o sin servidor, ha emergido como una solución potente para estos desafíos, permitiendo a los desarrolladores enfocarse en escribir código sin preocuparse por la infraestructura subyacente. En Google Cloud Platform (GCP), dos servicios clave para construir backends serverless son Google Cloud Functions y Firestore.
- Google Cloud Functions te permite ejecutar tu código en respuesta a eventos sin tener que aprovisionar o administrar servidores. Es ideal para construir microservicios, procesar datos en tiempo real o integrar sistemas.
- Firestore es una base de datos NoSQL, orientada a documentos, altamente escalable y flexible, diseñada para almacenar, sincronizar y consultar datos para aplicaciones web, móviles y de servidor.
Juntos, forman una combinación poderosa para crear backends altamente reactivos, escalables y rentables. En este tutorial, construiremos un backend simple que gestionará una colección de "tareas" (todos).
🎯 Objetivos del Tutorial
Al finalizar este tutorial, serás capaz de:
- Configurar un proyecto en Google Cloud Platform.
- Entender los conceptos básicos de Google Cloud Functions y Firestore.
- Crear y desplegar una función de Cloud Functions que interactúe con Firestore.
- Realizar operaciones CRUD (Crear, Leer, Actualizar, Eliminar) en Firestore desde una Cloud Function.
- Probar tu backend serverless.
🛠️ Prerrequisitos
Antes de empezar, asegúrate de tener lo siguiente:
- Una cuenta de Google Cloud Platform. Si no tienes una, puedes crear una nueva y obtener créditos gratuitos (generalmente $300).
- El SDK de Google Cloud (gcloud CLI) instalado y configurado en tu máquina local. Puedes seguir las instrucciones aquí.
- Node.js y npm instalados (recomendado para el ejemplo de funciones, aunque Cloud Functions soporta varios runtimes). Puedes descargar Node.js aquí.
- Conocimientos básicos de JavaScript (o el lenguaje que elijas para tus funciones).
Paso 1: Configuración del Proyecto GCP y Firestore ⚙️
1.1 Crear un Nuevo Proyecto GCP
Si ya tienes un proyecto existente, puedes usarlo. De lo contrario, crea uno nuevo:
- Ve a la Consola de Google Cloud.
- En la barra de navegación superior, haz clic en el selector de proyectos y luego en "Nuevo Proyecto".
- Asigna un nombre a tu proyecto (ej.
my-serverless-todo-app) y haz clic en "Crear".
Una vez creado el proyecto, selecciónalo.
1.2 Habilitar Firestore
Firestore es una base de datos NoSQL flexible y escalable. Vamos a configurarla:
- En la consola de GCP, ve a Firestore (puedes buscarlo en la barra de búsqueda o navegar a
Base de datos>Firestore). - Haz clic en "Seleccionar modo nativo".
- Elige la ubicación de tu base de datos (ej.
nam5para Norteamérica o la más cercana a tus usuarios). Esto es importante y no se puede cambiar después. - Haz clic en "Crear base de datos".
1.3 Habilitar APIs Necesarias
Para que Cloud Functions y Firestore puedan interactuar, debemos asegurarnos de que las APIs estén habilitadas:
- En la consola de GCP, ve a API y Servicios > Biblioteca.
- Busca y habilita las siguientes APIs si aún no lo están:
Cloud Functions APICloud Build API(usado para desplegar funciones)Cloud Datastore API(Firestore usa la infraestructura de Datastore)
Paso 2: Entendiendo Google Cloud Functions ✨
Google Cloud Functions son fragmentos de código que se ejecutan en un entorno totalmente administrado. Se activan por eventos, como:
- HTTP requests: Solicitudes web estándar (REST APIs).
- Eventos de Cloud Storage: Subida o eliminación de archivos.
- Eventos de Firestore: Creación, actualización o eliminación de documentos.
- Eventos de Pub/Sub: Mensajes de colas.
- Programados: Mediante Cloud Scheduler.
En este tutorial, nos centraremos en funciones activadas por HTTP para nuestro API RESTful y funciones activadas por Firestore para operaciones específicas.
Paso 3: Desarrollando la Primera Cloud Function (Crear Tarea) ✍️
Vamos a crear una función para agregar una nueva tarea a nuestra colección de Firestore.
3.1 Inicializar el Entorno Local
Crea un directorio para tu proyecto y un archivo package.json:
mkdir serverless-todo-backend
cd serverless-todo-backend
npm init -y
Instala el SDK de Firebase Admin para interactuar con Firestore:
npm install firebase-admin --save
3.2 Crear la Función addTask
Crea un archivo llamado index.js en tu directorio serverless-todo-backend con el siguiente contenido:
const functions = require('@google-cloud/functions');
const admin = require('firebase-admin');
// Inicializa Firebase Admin SDK si no ha sido inicializado
if (admin.apps.length === 0) {
admin.initializeApp();
}
const db = admin.firestore();
/**
* HTTP Cloud Function para añadir una nueva tarea.
* Espera un JSON en el cuerpo de la solicitud con un campo 'description'.
*/
exports.addTask = async (req, res) => {
// Configura CORS para permitir solicitudes desde cualquier origen (solo para desarrollo)
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
// Send response to OPTIONS requests
res.set('Access-Control-Allow-Methods', 'POST');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
return;
}
if (req.method !== 'POST') {
return res.status(405).send('Método no permitido. Use POST.');
}
const { description } = req.body;
if (!description) {
return res.status(400).send('La descripción de la tarea es requerida.');
}
try {
const newTaskRef = await db.collection('tasks').add({
description: description,
completed: false,
createdAt: admin.firestore.FieldValue.serverTimestamp()
});
res.status(201).json({
id: newTaskRef.id,
description: description,
completed: false,
createdAt: 'timestamp-generado'
});
} catch (error) {
console.error('Error al añadir tarea:', error);
res.status(500).send('Error interno del servidor.');
}
};
Este código define una función HTTP que recibe una descripción de tarea y la guarda en una colección tasks en Firestore.
3.3 Desplegar la Función
Abre tu terminal en el directorio serverless-todo-backend y ejecuta el siguiente comando:
gcloud functions deploy addTask \
--runtime nodejs18 \
--trigger-http \
--allow-unauthenticated \
--entry-point addTask \
--project my-serverless-todo-app # Reemplaza con el ID de tu proyecto
--runtime nodejs18: Especifica el entorno de ejecución.--trigger-http: Indica que la función se activará por solicitudes HTTP.--allow-unauthenticated: Permite que la función sea invocada sin autenticación (útil para pruebas, pero considera la autenticación para producción).--entry-point addTask: El nombre de la función exportada enindex.jsque GCF debe invocar.
El despliegue puede tardar unos minutos. Una vez completado, verás una URL similar a esta en la salida del comando:
... https://YOUR_REGION-YOUR_PROJECT_ID.cloudfunctions.net/addTask
Guarda esta URL. La usaremos para probar nuestra función.
Paso 4: Implementando Operaciones CRUD Restantes 📚
Ahora ampliaremos nuestra index.js para incluir las funciones de leer, actualizar y eliminar tareas.
4.1 Función getTasks (Leer Todas las Tareas)
Esta función leerá todas las tareas de Firestore.
Añade lo siguiente a tu index.js (después de addTask):
/**
* HTTP Cloud Function para obtener todas las tareas.
*/
exports.getTasks = async (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
res.set('Access-Control-Allow-Methods', 'GET');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
return;
}
if (req.method !== 'GET') {
return res.status(405).send('Método no permitido. Use GET.');
}
try {
const snapshot = await db.collection('tasks').orderBy('createdAt', 'desc').get();
const tasks = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data()
}));
res.status(200).json(tasks);
} catch (error) {
console.error('Error al obtener tareas:', error);
res.status(500).send('Error interno del servidor.');
}
};
Despliega esta nueva función:
gcloud functions deploy getTasks \
--runtime nodejs18 \
--trigger-http \
--allow-unauthenticated \
--entry-point getTasks \
--project my-serverless-todo-app
4.2 Función updateTask (Actualizar una Tarea)
Esta función actualizará el estado de completed de una tarea específica.
Añade lo siguiente a tu index.js:
/**
* HTTP Cloud Function para actualizar una tarea específica por ID.
* Espera un JSON en el cuerpo con 'completed' (boolean) y el ID en la URL.
*/
exports.updateTask = async (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
res.set('Access-Control-Allow-Methods', 'PUT');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
return;
}
if (req.method !== 'PUT') {
return res.status(405).send('Método no permitido. Use PUT.');
}
const taskId = req.query.id || req.params.id;
const { completed } = req.body;
if (!taskId) {
return res.status(400).send('El ID de la tarea es requerido.');
}
if (typeof completed === 'undefined' || typeof completed !== 'boolean') {
return res.status(400).send('El campo \'completed\' (boolean) es requerido.');
}
try {
const taskRef = db.collection('tasks').doc(taskId);
await taskRef.update({ completed: completed });
res.status(200).send(`Tarea ${taskId} actualizada correctamente.`);
} catch (error) {
console.error('Error al actualizar tarea:', error);
res.status(500).send('Error interno del servidor.');
}
};
Despliega esta nueva función:
gcloud functions deploy updateTask \
--runtime nodejs18 \
--trigger-http \
--allow-unauthenticated \
--entry-point updateTask \
--project my-serverless-todo-app
4.3 Función deleteTask (Eliminar una Tarea)
Esta función eliminará una tarea específica.
Añade lo siguiente a tu index.js:
/**
* HTTP Cloud Function para eliminar una tarea específica por ID.
* Espera el ID de la tarea en la URL.
*/
exports.deleteTask = async (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
res.set('Access-Control-Allow-Methods', 'DELETE');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.set('Access-Control-Max-Age', '3600');
res.status(204).send('');
return;
}
if (req.method !== 'DELETE') {
return res.status(405).send('Método no permitido. Use DELETE.');
}
const taskId = req.query.id || req.params.id;
if (!taskId) {
return res.status(400).send('El ID de la tarea es requerido.');
}
try {
await db.collection('tasks').doc(taskId).delete();
res.status(200).send(`Tarea ${taskId} eliminada correctamente.`);
} catch (error) {
console.error('Error al eliminar tarea:', error);
res.status(500).send('Error interno del servidor.');
}
};
Despliega esta nueva función:
gcloud functions deploy deleteTask \
--runtime nodejs18 \
--trigger-http \
--allow-unauthenticated \
--entry-point deleteTask \
--project my-serverless-todo-app
Paso 5: Probando el Backend Serverless ✅
Una vez que todas las funciones están desplegadas, podemos probarlas. Puedes usar herramientas como Postman, Insomnia o incluso curl.
Consideremos que las URLs de tus funciones son:
ADD_TASK_URL(paraaddTask)GET_TASKS_URL(paragetTasks)UPDATE_TASK_URL(paraupdateTask)DELETE_TASK_URL(paradeleteTask)
5.1 Probar addTask (POST)
Para añadir una tarea:
curl -X POST -H "Content-Type: application/json" -d '{"description": "Aprender Cloud Functions"}' "${ADD_TASK_URL}"
Respuesta esperada (ejemplo):
{
"id": "somefirestoreid",
"description": "Aprender Cloud Functions",
"completed": false,
"createdAt": "timestamp-generado"
}
5.2 Probar getTasks (GET)
Para obtener todas las tareas:
curl -X GET "${GET_TASKS_URL}"
Respuesta esperada (ejemplo):
[
{
"id": "somefirestoreid",
"description": "Aprender Cloud Functions",
"completed": false,
"createdAt": {"_seconds": 1678886400, "_nanoseconds": 0}
}
]
5.3 Probar updateTask (PUT)
Para marcar una tarea como completada, necesitarás el id de una tarea existente. Asumamos somefirestoreid.
curl -X PUT -H "Content-Type: application/json" -d '{"completed": true}' "${UPDATE_TASK_URL}?id=somefirestoreid"
Respuesta esperada:
Tarea somefirestoreid actualizada correctamente.
5.4 Probar deleteTask (DELETE)
Para eliminar una tarea, necesitarás su id. Asumamos somefirestoreid.
curl -X DELETE "${DELETE_TASK_URL}?id=somefirestoreid"
Respuesta esperada:
Tarea somefirestoreid eliminada correctamente.
Verifica en la consola de Firestore que los cambios se reflejan en tu colección tasks.
6. Consideraciones Adicionales y Buenas Prácticas 💡
6.1 Autenticación y Autorización
Para una aplicación de producción, no querrás que tus funciones estén --allow-unauthenticated. Considera usar:
- Firebase Authentication: Para gestionar usuarios y securizar el acceso a tus funciones.
- Cloud Identity and Access Management (IAM): Para controlar quién puede invocar tus funciones.
- API Gateway: Para centralizar la gestión de API, incluyendo la autenticación y el throttling.
6.2 Manejo de Errores y Logging
Es crucial tener un buen manejo de errores y un logging adecuado. Cloud Functions integra logs con Cloud Logging, donde puedes ver los errores y el rendimiento de tus funciones.
6.3 Monitoreo y Escalabilidad
Cloud Functions escala automáticamente según la demanda. Puedes monitorear su rendimiento y uso a través de Cloud Monitoring. Configura alertas para detectar problemas o picos de uso.
6.4 Costos
El modelo de precios de Cloud Functions y Firestore es pay-as-you-go, lo que significa que solo pagas por los recursos que consumes. Ambos servicios tienen un nivel gratuito generoso, lo que los hace muy atractivos para startups y proyectos personales.
Factores que influyen en el costo:
- Número de invocaciones.
- Tiempo de ejecución.
- Memoria asignada.
- Transferencia de datos.
- Lecturas/escrituras/eliminaciones de Firestore y almacenamiento.
6.5 Versionado y CI/CD
Integra tus funciones en un pipeline de CI/CD para automatizar pruebas y despliegues. Herramientas como Cloud Build o GitHub Actions pueden facilitar esto.
Conclusión ✨
Has construido un backend serverless completo utilizando Google Cloud Functions y Firestore. Esta arquitectura te permite desarrollar aplicaciones altamente escalables, robustas y de bajo costo, liberándote de la gestión de servidores y la infraestructura. Los principios que has aprendido aquí son fundamentales para construir aplicaciones modernas en la nube.
Este es solo el comienzo. Puedes expandir este backend agregando autenticación de usuarios con Firebase Authentication, implementando notificaciones en tiempo real con WebSockets (a través de servicios como Cloud Run o integraciones de terceros) o procesando imágenes con Cloud Storage y otras funciones activadas por eventos.
¡Experimenta y sigue construyendo!
Tutoriales relacionados
- Orquestación de Flujos de Trabajo Serverless con AWS Step Functions: Guía Completaintermediate18 min
- Simplificando la Orquestación de Contenedores Serverless con AWS App Runner: Guía Prácticaintermediate18 min
- Gestionando el Estado en Aplicaciones Serverless con AWS Step Functions y DynamoDBintermediate20 min
- Despliegue de APIs Serverless con AWS Lambda y API Gateway: Una Guía Prácticaintermediate15 min
- Desarrollo de Microservicios Serverless con AWS Lambda y la Arquitectura Hexagonalintermediate25 min
Comentarios (0)
Aún no hay comentarios. ¡Sé el primero!