7 cosas que debes saber al comenzar con las API sin servidor
- ¿Cómo creo un nuevo proyecto sin servidor?
- ¿Cómo ejecuto y depuro funciones sin servidor localmente?
- ¿Cómo instalo dependencias?
- ¿Cómo me conecto a servicios de terceros?
- Where can I save connection strings?
- How do I customize the URL path?
- How can I deploy to the cloud?
- Are you ready to make Serverless APIs?
Quiero que te tomes un segundo y pienses en Twitter, y lo pienses en términos de escala. Twitter tiene 326 millones de usuarios . En conjunto, creamos ~6000 tweets cada segundo . Cada minuto, se crean 360.000 tweets. Eso suma casi 200 mil millones de tweets al año. Ahora bien, ¿qué pasaría si los creadores de Twitter se hubieran paralizado sobre cómo escalar y ni siquiera hubieran empezado?
Ese soy yo en cada idea de startup que he tenido, y es por eso que adoro tanto la tecnología sin servidor: ¡se encarga de los problemas de escalamiento y me deja a mí construir el próximo Twitter!
Como puede ver en lo anterior, escalamos de uno a siete servidores en cuestión de segundos, a medida que llegan más solicitudes de los usuarios. También puede escalar eso fácilmente.
Entonces, creemos una API que se escalará instantáneamente a medida que ingresen más y más usuarios y aumente nuestra carga de trabajo. Lo haremos respondiendo las siguientes preguntas:
- ¿Cómo creo un nuevo proyecto sin servidor?
- ¿Cómo ejecuto y depuro código localmente?
- ¿Cómo instalo dependencias?
- ¿Cómo me conecto a servicios de terceros?
- ¿Dónde puedo guardar cadenas de conexión?
- ¿Cómo personalizo la ruta URL?
- ¿Cómo puedo implementar en la nube?
¿Cómo creo un nuevo proyecto sin servidor?
Con cada nueva tecnología, necesitamos descubrir qué herramientas están disponibles para nosotros y cómo podemos integrarlas en nuestro conjunto de herramientas existente. Al comenzar con la tecnología sin servidor, debemos considerar algunas opciones.
Primero, podemos usar el viejo navegador para crear, escribir y probar funciones. Es poderoso y nos permite codificar dondequiera que estemos; todo lo que necesitamos es una computadora y un navegador en funcionamiento. El navegador es un buen punto de partida para escribir nuestra primera función sin servidor.
A continuación, a medida que se acostumbre más a los nuevos conceptos y se vuelva más productivo, es posible que desee utilizar su entorno local para continuar con su desarrollo. Normalmente necesitarás soporte para algunas cosas:
- Escribir código en el editor de su elección
- Herramientas que hacen el trabajo pesado y generan el código repetitivo por usted
- Ejecutar y depurar código localmente
- Soporte para implementar rápidamente su código
Microsoft es mi empleador y he creado principalmente aplicaciones sin servidor utilizando Azure Functions, por lo que durante el resto de este artículo continuaré usándolas como ejemplo. Con Azure Functions , tendrá soporte para todas estas características cuando trabaje con las herramientas principales de Azure Functions que puede instalar desde npm .
npm install -g azure-functions-core-tools
A continuación, podemos inicializar un nuevo proyecto y crear nuevas funciones utilizando la CLI interactiva:
Si su editor preferido es VS Code , también puede usarlo para escribir código sin servidor. De hecho, existe una gran extensión para ello.
Una vez instalado, se agregará un nuevo ícono a la barra lateral izquierda: ¡aquí es donde podemos acceder a todas nuestras extensiones relacionadas con Azure! Todas las funciones relacionadas se pueden agrupar en el mismo proyecto (también conocido como aplicación de funciones). Esto es como una carpeta para agrupar funciones que deben escalarse juntas y que queremos administrar y monitorear al mismo tiempo. Para inicializar un nuevo proyecto usando VS Code, haga clic en el ícono de Azure y luego en el ícono de la carpeta .
Esto generará algunos archivos que nos ayudarán con la configuración global. Repasemos eso ahora.
host.json
Podemos configurar opciones globales para todas las funciones del proyecto directamente en el host.json
archivo.
En él, nuestra aplicación de funciones está configurada para utilizar la última versión del tiempo de ejecución sin servidor (actualmente 2.0). También configuramos funciones para que expiren después de diez minutos estableciendo la functionTimeout
propiedad en 00:10:00
: el valor predeterminado para eso es actualmente cinco minutos ( 00:05:00
).
En algunos casos, es posible que queramos controlar el prefijo de ruta de nuestras URL o incluso modificar la configuración, como la cantidad de solicitudes simultáneas. Azure Functions incluso nos permite personalizar otras funciones como el registro healthMonitor
y diferentes tipos de extensiones.
Aquí hay un ejemplo de cómo configuré el archivo:
// host.json{ "version": "2.0", "functionTimeout": "00:10:00", "extensions": { "http": { "routePrefix": "tacos", "maxOutstandingRequests": 200, "maxConcurrentRequests": 100, "dynamicThrottlesEnabled": true }}}
Configuraciones de la aplicación
La configuración de la aplicación es una configuración global para administrar el tiempo de ejecución, el idioma y la versión, las cadenas de conexión, el acceso de lectura/escritura y la implementación de ZIP, entre otros. Algunas son configuraciones requeridas por la plataforma, como FUNCTIONS_WORKER_RUNTIME
, pero también podemos definir configuraciones personalizadas que usaremos en el código de nuestra aplicación, como DB_CONN
las que podemos usar para conectarnos a una instancia de base de datos.
Mientras desarrollamos localmente, definimos estas configuraciones en un archivo llamado local.settings.json
y accedemos a ellas como cualquier otra variable de entorno.
Nuevamente, aquí hay un fragmento de ejemplo que conecta estos puntos:
// local.settings.json{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "your_key_here", "FUNCTIONS_WORKER_RUNTIME": "node", "WEBSITE_NODE_DEFAULT_VERSION": "8.11.1", "FUNCTIONS_EXTENSION_VERSION": "~2", "APPINSIGHTS_INSTRUMENTATIONKEY": "your_key_here", "DB_CONN": "your_key_here", }}
Proxies de funciones de Azure
Los proxy de Azure Functions se implementan en el proxies.json
archivo y nos permiten exponer múltiples aplicaciones de funciones bajo la misma API, así como modificar solicitudes y respuestas. En el código siguiente, publicamos dos puntos finales diferentes en la misma URL.
// proxies.json{ "$schema": "http://json.schemastore.org/proxies", "proxies": { "read-recipes": { "matchCondition": { "methods": ["POST"], "route": "/api/recipes" }, "backendUri": "https://tacofancy.azurewebsites.net/api/recipes" }, "subscribe": { "matchCondition": { "methods": ["POST"], "route": "/api/subscribe" }, "backendUri": "https://tacofancy-users.azurewebsites.net/api/subscriptions" } }}
Cree una nueva función haciendo clic en el ícono del trueno en la extensión.
La extensión utilizará plantillas predefinidas para generar código, según las selecciones que hicimos: idioma, tipo de función y nivel de autorización.
Usamos function.json
para configurar qué tipo de eventos escucha nuestra función y, opcionalmente, para vincularnos a fuentes de datos específicas. Nuestro código se ejecuta en respuesta a desencadenantes específicos que pueden ser de tipo HTTP cuando reaccionamos a solicitudes HTTP (cuando ejecutamos código en respuesta a la carga de un archivo en una cuenta de almacenamiento). Otros activadores utilizados comúnmente pueden ser de tipo cola , para procesar un mensaje cargado en una cola o activadores de tiempo para ejecutar código en intervalos de tiempo específicos. Los enlaces de funciones se utilizan para leer y escribir datos en fuentes de datos o servicios como bases de datos o enviar correos electrónicos.
Aquí, podemos ver que nuestra función está escuchando solicitudes HTTP y obtenemos acceso a la solicitud real a través del objeto llamado req
.
// function.json{ "disabled": false, "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": ["get"], "route": "recipes" }, { "type": "http", "direction": "out", "name": "res" } ]}
index.js
es donde implementamos el código para nuestra función. Tenemos acceso al objeto de contexto, que utilizamos para comunicarnos con el tiempo de ejecución sin servidor. Podemos hacer cosas como registrar información, establecer la respuesta para nuestra función y leer y escribir datos del bindings
objeto. A veces, nuestra aplicación de funciones tendrá varias funciones que dependen del mismo código (es decir, conexiones de bases de datos) y es una buena práctica extraer ese código en un archivo separado para reducir la duplicación de código.
//Index.jsmodule.exports = async function (context, req) { context.log('JavaScript HTTP trigger function processed a request.'); if (req.query.name || (req.body req.body.name)) { context.res = { // status: 200, /* Defaults to 200 */ body: "Hello " + (req.query.name || req.body.name) }; } else { context.res = { status: 400, body: "Please pass a name on the query string or in the request body" }; }};
¿Quién está emocionado de probar esto?
¿Cómo ejecuto y depuro funciones sin servidor localmente?
Cuando usamos VS Code, la extensión Azure Functions nos brinda gran parte de la configuración que necesitamos para ejecutar y depurar funciones sin servidor localmente. Cuando creamos un nuevo proyecto usándolo, .vscode
se creó automáticamente una carpeta para nosotros, y aquí es donde está contenida toda la configuración de depuración. Para depurar nuestra nueva función, podemos usar la paleta de comandos ( Ctrl+ Shift+ P) filtrando por Depurar: Seleccionar e iniciar depuración , o escribiendo debug
.
Una de las razones por las que esto es posible es porque el tiempo de ejecución de Azure Functions es de código abierto y se instala localmente en nuestra máquina al instalar el azure-core-tools
paquete.
¿Cómo instalo dependencias?
Es probable que ya sepas la respuesta a esta pregunta si has trabajado con Node.js. Como en cualquier otro proyecto de Node.js, primero debemos crear un package.json
archivo en la carpeta raíz del proyecto. Esto se puede hacer ejecutando npm init -y
: -y
inicializará el archivo con la configuración predeterminada.
Luego instalamos dependencias usando npm como lo haríamos normalmente en cualquier otro proyecto. Para este proyecto, sigamos adelante e instalemos el paquete MongoDB desde npm ejecutando:
npm i mongodb
El paquete ahora estará disponible para importar en todas las funciones de la aplicación de funciones.
¿Cómo me conecto a servicios de terceros?
Las funciones sin servidor son bastante poderosas y nos permiten escribir código personalizado que reacciona a eventos. Pero el código por sí solo no ayuda mucho a la hora de crear aplicaciones complejas. El verdadero poder proviene de la fácil integración con servicios y herramientas de terceros.
So, how do we connect and read data from a database? Using the MongoDB client, we’ll read data from an Azure Cosmos DB instance I have created in Azure, but you can do this with any other MongoDB database.
//Index.jsconst MongoClient = require('mongodb').MongoClient;// Initialize authentication details required for database connectionconst auth = { user: process.env.user, password: process.env.password};// Initialize global variable to store database connection for reuse in future callslet db = null;const loadDB = async () = { // If database client exists, reuse it if (db) { return db; } // Otherwise, create new connection const client = await MongoClient.connect( process.env.url, { auth: auth } ); // Select tacos database db = client.db('tacos'); return db;};module.exports = async function(context, req) { try { // Get database connection const database = await loadDB(); // Retrieve all items in the Recipes collection let recipes = await database .collection('Recipes') .find() .toArray(); // Return a JSON object with the array of recipes context.res = { body: { items: recipes } }; } catch (error) { context.log(`Error code: ${error.code} message: ${error.message}`); // Return an error message and Internal Server Error status code context.res = { status: 500, body: { message: 'An error has occurred, please try again later.' } }; }};
One thing to note here is that we’re reusing our database connection rather than creating a new one for each subsequent call to our function. This shaves off ~300ms of every subsequent function call. I call that a win!
Where can I save connection strings?
When developing locally, we can store our environment variables, connection strings, and really anything that’s secret into the local.settings.json
file, then access it all in the usual manner, using process.env.yourVariableName
.
local.settings.json{ "IsEncrypted": false, "Values": { "AzureWebJobsStorage": "", "FUNCTIONS_WORKER_RUNTIME": "node", "user": "your-db-user", "password": "your-db-password", "url": "mongodb://your-db-user.documents.azure.com:10255/?ssl=true" }}
In production, we can configure the application settings on the function’s page in the Azure portal.
However, another neat way to do this is through the VS Code extension. Without leaving your IDE, we can add new settings, delete existing ones or upload/download them to the cloud.
How do I customize the URL path?
With the REST API, there are a couple of best practices around the format of the URL itself. The one I settled on for our Recipes API is:
GET /recipes
: Retrieves a list of recipesGET /recipes/1
: Retrieves a specific recipePOST /recipes
: Creates a new recipePUT /recipes/1
: Updates recipe with ID 1DELETE /recipes/1
: Deletes recipe with ID 1
The URL that is made available by default when creating a new function is of the form http://host:port/api/function-name
. To customize the URL path and the method that we listen to, we need to configure them in our function.json
file:
// function.json{ "disabled": false, "bindings": [ { "authLevel": "anonymous", "type": "httpTrigger", "direction": "in", "name": "req", "methods": ["get"], "route": "recipes" }, { "type": "http", "direction": "out", "name": "res" } ]}
Moreover, we can add parameters to our function’s route by using curly braces: route: recipes/{id}
. We can then read the ID parameter in our code from the req
object:
const recipeId = req.params.id;
How can I deploy to the cloud?
Congratulations, you’ve made it to the last step! Time to push this goodness to the cloud. As always, the VS Code extension has your back. All it really takes is a single right-click we’re pretty much done.
The extension will ZIP up the code with the Node modules and push them all to the cloud.
While this option is great when testing our own code or maybe when working on a small project, it’s easy to overwrite someone else’s changes by accident — or even worse, your own.
Don’t let friends right-click deploy!
— every DevOps engineer out there
A much healthier option is setting up on GitHub deployment which can be done in a couple of steps in the Azure portal, via the Deployment Center tab.
Are you ready to make Serverless APIs?
This has been a thorough introduction to the world of Servless APIs. However, there’s much, much more than what we’ve covered here. Serverless enables us to solve problems creatively and at a fraction of the cost we usually pay for using traditional platforms.
Chris has mentioned it in other posts here on , but he created this excellent website where you can learn more about serverless and find both ideas and resources for things you can build with it. Definitely check it out and let me know if you have other tips or advice scaling with serverless.
Deja una respuesta