Creación de una aplicación del lado del servidor con funciones asíncronas y Koa 2

 

 

 

  • Creación y mantenimiento de sistemas de diseño exitosos, con Brad Fost
  • Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX

  • Índice
    1. Lecturas adicionales sobre SmashingMag:
    2. Funciones asíncronas
    3. ¿Qué es la Koa?
    4. Koa 2 contra Koa 1
    5. Koa versus expreso
    6. Estado de Koa 2
    7. Aplicación de demostración
      1. Dependencias de la aplicación
      2. ¿Cómo organizar la aplicación?
      3. Scripts de NPM como ejecutor de tareas
      4. El código de la aplicación
      5. Definiendo la API
      6. Capa de persistencia
      7. Pruebas
      8. Implementación con PM2
      9. Conclusión

    Una de las próximas características de JavaScript que me gusta especialmente es la compatibilidad con funciones asincrónicas . En este artículo, me gustaría mostrarles un ejemplo muy práctico de cómo crear una aplicación del lado del servidor usando Koa 2 , una nueva versión del marco web, que depende en gran medida de esta característica. Primero, recapitularé qué son las funciones asíncronas y cómo funcionan. Luego, resaltaré las diferencias entre Koa 1 y Koa 2. Después de eso, describiré mi aplicación de demostración para Koa 2, cubriendo todos los aspectos del desarrollo, incluidas las pruebas (usando Mocha, Chai y Supertest) y la implementación (usando PM2). .

     

    Una de las próximas características de JavaScript que me gusta especialmente es la compatibilidad con funciones asincrónicas . En este artículo, me gustaría mostrarles un ejemplo muy práctico de cómo crear una aplicación del lado del servidor usando Koa 2 , una nueva versión del marco web, que depende en gran medida de esta característica.

    Primero, recapitularé qué son las funciones asíncronas y cómo funcionan. Luego, resaltaré las diferencias entre Koa 1 y Koa 2. Después de eso, describiré mi aplicación de demostración para Koa 2, cubriendo todos los aspectos del desarrollo, incluidas las pruebas (usando Mocha, Chai y Supertest) y la implementación (usando PM2). .

    Lecturas adicionales sobre SmashingMag:

    • Comprender la función.prototype.bind de JavaScript
    • Representación del lado del servidor con React, Node y Express
    • Por qué debería considerar React Native para su aplicación móvil

    Funciones asíncronas

    El viejo problema de las aplicaciones JavaScript complejas es cómo lidiar con las devoluciones de llamadas y cómo estructurar el código para evitar el llamado "infierno de las devoluciones de llamadas". A lo largo del tiempo se han desarrollado varias soluciones, algunas de las cuales todavía se basan en devoluciones de llamada y otras se basan en características más recientes de JavaScript: promesas y generadores . Comparemos devoluciones de llamada, promesas y generadores usando el ejemplo de una función que recupera dos archivos JSON en secuencia.

    // Fetch two files with callbacksfunction doWork(cb) { fetch(‘data1.json’, (err1, result1) = { if (err1) { return cb(err1); } fetch(‘data2.json’, (err2, result2) = { if (err2) { return cb(err2); } cb(null, { result1, result2, }); }) });}

    Las funciones anidadas de devolución de llamadas en línea son el principal indicador del infierno de las devoluciones de llamadas. Podrías reestructurar el código y dividir las funciones en módulos, pero de todos modos tendrías que depender de las devoluciones de llamada.

    // Fetch two files with promisesfunction doWork(cb) { fetch(‘data1.json’) .then(result1 = fetch(‘data2.json’) .then(result2 = cb(null, { result1, result2, }))) .catch(cb);}

    La versión basada en promesas se ve un poco mejor, pero las llamadas aún están anidadas y necesitamos reorganizar el código para que parezca secuencial.

    // Fetch two files with generatorsfunction* doWork() { var result1 = yield fetch(‘data1.json’); var result2 = yield fetch(‘data2.json’); return { result1, result2 };}

    Los generadores permiten la solución más corta, y esto parece un código sincrónico normal, mientras que los fragmentos de devolución de llamada y promesa son claramente asincrónicos y altamente anidados. Sin embargo, la solución del generador requiere que cambiemos el tipo de función a la función del generador agregando *después de la functionpalabra clave, y requiere una forma especial de invocar doWork. Esto parece poco intuitivo y la async/awaitsintaxis soluciona este inconveniente proporcionando una mejor abstracción. Mire el mismo ejemplo usando funciones asíncronas:

    async function doWork() { // Fetch two files with async/await var result1 = await fetch(‘data1.json’); var result2 = await fetch(‘data2.json’); return { result1, result2 };}

    Esta sintaxis se puede interpretar de la siguiente manera: Las funciones marcadas con la asyncpalabra clave nos permiten utilizar la awaitpalabra clave en ellas, lo que pausa la ejecución de la función para esperar a que finalicen las operaciones asincrónicas. La operación asincrónica puede representarse mediante un generador, una promesa u otra función asincrónica. Además, puede utilizar try/ catchpara gestionar cualquier error o rechazo que se produzca durante las operaciones asíncronas que realice await. El mismo mecanismo de manejo de errores es posible con el flujo de control basado en generador.

    ¿Qué es la Koa?

    Koa se posiciona como un framework web de próxima generación y fue diseñado por las personas que crearon Express (y en particular, por TJ ). Koa es muy liviano y modular y permite escribir código sin utilizar devoluciones de llamada. La aplicación Koa es una serie de funciones de middleware que se ejecutan en secuencia procesando las solicitudes entrantes y proporcionando una respuesta. Cada función de middleware tiene acceso al objeto de contexto que envuelve los objetos de solicitud y respuesta del nodo nativo y proporciona una API mejorada para trabajar con ellos. La aplicación básica de Koa se ve así:

    // This is Koa 1var koa = require(‘koa’);var app = koa();

    app.use(function *(){ this.body = ‘Hello World’;});

    app.listen(3000);

    Esto no es mucho excepto esto en el núcleo de Koa. La funcionalidad avanzada la proporcionan módulos de terceros.

    Koa 2 contra Koa 1

    Koa 1 es famoso por su temprana adopción de generadores y por admitir el control basado en generadores listo para usar. Así es como se ve un fragmento de código típico para Koa 1 que utiliza la cascada de middleware y un manejo mejorado de errores:

    // Adapted from https://github.com/koajs/koa// In Koa 1.0, middleware is a generator function.app.use(function *(next) { try { yield next; } catch (err) { // the request context is codethis/code this.body = { message: err.message } this.status = err.status || 500 }})

    app.use(function *(next) { const user = yield User.getById(this.session.id); this.body = user;})

    Koa 2 elimina el soporte integrado para generadores y utiliza funciones asíncronas en su lugar. La firma de las funciones de middleware cambiará para admitir asyncfunciones de flecha. Así es como se ve el mismo código escrito para Koa 2:

    // Taken from https://github.com/koajs/koa// Uses async arrow functionsapp.use(async (ctx, next) = { try { await next(); // next is now a function, await instead of yield } catch (err) { ctx.body = { message: err.message }; ctx.status = err.status || 500; }});

    app.use(async ctx = { // await instead of yield const user = await User.getById(ctx.session.id); // ctx instead of this ctx.body = user;});

     

    Aún es posible utilizar funciones normales, promesas y funciones de generador, como se describe en la documentación de Koa 2 .

    Koa versus expreso

    Koa es un marco más simple y ágil que Express, construido sobre el módulo HTTP de Node.js. Express proporciona funciones integradas para enrutar, crear plantillas y enviar archivos y otras funciones, mientras que Koa proporciona lo mínimo, así como un flujo de control basado en generador (Koa 1) o basado en asíncrono/espera (Koa 2). La comunidad proporciona enrutamiento, plantillas y otras características como módulos separados y normalmente hay varias alternativas para elegir. La sección Koa de GitHub tiene un documento con información adicional sobre las diferencias entre Koa y Express.

    Estado de Koa 2

    Koa 2 se lanzará una vez que la función async/await esté disponible en Node.js de forma nativa. Afortunadamente, async/await y Koa 2.0 se pueden probar ahora mismo usando Babel. Llegaremos a esto pronto. Primero, repasemos el alcance de la aplicación que creé para demostrar Koa 2 y las funciones asíncronas.

    Aplicación de demostración

    El objetivo aquí es crear una aplicación simple que rastree las visitas a la página de un sitio web estático, algo así como Google Analytics pero mucho más simple. La aplicación tendrá dos puntos finales:

    • uno para almacenar la información sobre un evento (por ejemplo, una vista de página);
    • y uno para obtener el número total de eventos;
    • Además, los puntos finales deben estar protegidos con una clave API.

    Redis se utilizará para almacenar los datos de los eventos. La funcionalidad se probará mediante pruebas unitarias y API. El código fuente completo de la aplicación está disponible en Github .

    Dependencias de la aplicación

    Comencemos con la lista de módulos necesarios para la aplicación, junto con explicaciones de por qué son necesarios. Primero, aquí están las dependencias necesarias en tiempo de ejecución:

    npm install --save # babel-polyfill provides the runtime that async functions need babel-polyfill # The Koa framework itself koa@next # Because Koa is minimalist, we need a parser # to parse JSON in the body of requests koa-bodyparser@next # and the router koa-router@next # The redis module to store the app's data redis # The Koa cors module to allow cross-origin requests kcors@next

    Tenga en cuenta que la versión de los módulos Koa que estamos usando es next. Esto significa que esta versión será compatible con Koa 2 y muchos módulos ya lo proporcionan. Estos son los módulos necesarios para el desarrollo y las pruebas:

    npm install --save-dev # To write assertions in our tests chai # A popular testing framework mocha # API-level tests supertest # Babel CLI to build our app babel-cli # A set of Babel plugins to support ES6 features babel-preset-es2015 # and to support stage-3 features babel-preset-stage-3 # Overrides Node.js's require and compiles modules at runtime babel-register # To watch JavaScript files in development and restart the server # if there are changes in the files nodemon 

    ¿Cómo organizar la aplicación?

    Después de probar varias formas de organizar los archivos de la aplicación, se me ocurrió la siguiente estructura simple que es adecuada para aplicaciones y equipos pequeños:

     

    • index.jsEl principal punto de entrada de la aplicación.
    • ecosystem.jsonEl ecosistema PM2, que describe cómo iniciar la aplicación.
    • srcEl código fuente de la aplicación, que contiene archivos JavaScript que Babel compilará.
    • configLos archivos de configuración de la aplicación.
    • buildLa compilación de la aplicación, que contiene el código compilado del srcdirectorio src, contiene lo siguiente:

      • api.jsEl módulo que define la API de la aplicación.
      • app.jsEl módulo que crea una instancia y configura la instancia de la aplicación Koa.
      • config.jsEl módulo que proporciona la configuración de la aplicación a otros módulos. Si se necesitan archivos o módulos adicionales a medida que la aplicación crece, los colocaríamos en una subcarpeta de la srccarpeta, por ejemplo, src/modelspara modelos de aplicaciones, src/routespara definiciones de API más modulares, src/gatewayspara módulos que interactuar con servicios externos, etc.

      Scripts de NPM como ejecutor de tareas

      Después de usar Gulp y Grunt como ejecutores de tareas, llegué a la conclusión de que los scripts npm son mejores que las herramientas independientes cuando se trabaja en proyectos del lado del servidor. Una de las ventajas de los scripts npm es que le permiten invocar módulos instalados localmente como si estuvieran instalados globalmente. Utilizo los siguientes scripts, que deben definirse en package.json:

      "scripts": { "start": "node index.js", "watch": "nodemon --exec npm run start", "build": "babel src -d build", "test": "npm run build; mocha --require 'babel-polyfill' --compilers js:babel-register"}

      El startscript simplemente se ejecuta index.js. El watchscript se ejecuta startutilizando la herramienta nodemon, que reinicia automáticamente el servidor cada vez que cambia algo en la aplicación. Tenga en cuenta que nodemon se instala como una dependencia de desarrollo local y no es necesario instalarlo globalmente. El buildscript compila los archivos en la srccarpeta usando Babel y envía los resultados a la buildcarpeta. El testscript ejecuta buildprimero el script y luego ejecuta las pruebas usando mocha. Mocha requiere dos módulos: babel-polyfillpara proporcionar dependencias de tiempo de ejecución del código compilado y babel-registerpara compilar los archivos de prueba antes de ejecutarlos. Además, se deben agregar ajustes preestablecidos para Babel package.json, de modo que no sea necesario proporcionarlos en la línea de comando: Todo sobre Hoteles

      { "babel": { "presets": [ "es2015", "stage-3" ] }}

      Este ajuste preestablecido habilita todas las funciones de ECMAScript 2015, así como las funciones que se encuentran actualmente en la etapa 3 . Con esto instalado y configurado, podemos comenzar a desarrollar la aplicación.

       

      El código de la aplicación

      Primero, veamos index.js:

      const port = process.env.PORT || 4000;const env = process.env.NODE_ENV || 'development';const src = env === 'production' ? './build/app' : './src/app';require('babel-polyfill');if (env === 'development') { // for development use babel/register for faster runtime compilation require('babel-register');}const app = require(src).default;app.listen(port);

      El módulo lee dos variables ambientales: PORTy NODE_ENV. NODE_ENVdebería ser cualquiera developmento production. En modo de desarrollo, babel-registerse utilizará para compilar módulos en tiempo de ejecución. babel-registeralmacena en caché los resultados de la compilación y, por lo tanto, reduce el tiempo de inicio del servidor, para que pueda iterar más rápido durante el desarrollo. Debido a que este módulo no se recomienda para uso en producción, la versión precompilada del builddirectorio se usará en modo de producción. index.jses el único archivo del proyecto que no será compilado por Babel y que debe usar la sintaxis del módulo nativo (es decir, CommonJS). Por lo tanto, la instancia de la aplicación se encuentra en la defaultpropiedad del appmódulo importado, que es un módulo ECMAScript 6 que exporta la instancia de la aplicación como exportación predeterminada:

      export default app;

      Es importante tener esto en cuenta si combina módulos ECMAScript 6 y CommonJS. Ahora al app.jsarchivo en sí. Babel siempre compila este y los otros archivos que se analizan a continuación en entornos de desarrollo y producción, y la nueva sintaxis (incluidas las funciones asíncronas) se puede usar libremente en ellos:

      import Koa from 'koa';import api from './api';import config from './config';import bodyParser from 'koa-bodyparser';import cors from 'kcors';const app = new Koa() .use(cors()) .use(async (ctx, next) = { ctx.state.collections = config.collections; ctx.state.authorizationHeader = `Key ${config.key}`; await next(); }) .use(bodyParser()) .use(api.routes()) .use(api.allowedMethods());export default app;

      Aquí, utilizamos importla sintaxis de ECMAScript 2015 para importar los módulos necesarios. Luego, creamos una nueva instancia de la aplicación Koa y le adjuntamos varias funciones de middleware utilizando el usemétodo. Lo último que hacemos es exportar la aplicación para que pueda ser utilizada por index.js. La segunda función de middleware en la cadena es una función asíncrona y una función de flecha al mismo tiempo:

      app.use(async (ctx, next) = { // Set up the request context ctx..state.collections = config.collections; ctx..state.authorizationHeader = `Key ${config.key}`; await next(); // The execution will reach here only when // the next function returns and finishes all async tasks // console.log('Request is done');})

      En Koa 2, el nextparámetro es una función asíncrona que activa la siguiente función de middleware en la lista. Al igual que en Koa 1, puedes controlar si la función de middleware actual debe hacer su trabajo antes o después de las demás al realizar la llamada nextal principio de la función actual o al final. Además, puede detectar todos los errores en las funciones de middleware posteriores envolviendo la await next();declaración en una try/catchdeclaración siempre que tenga sentido hacerlo.

       

      Definiendo la API

      El api.jsarchivo es donde reside la lógica central de nuestra aplicación. Debido a que Koa no proporciona enrutamiento listo para usar, la aplicación debe usar el koa-routermódulo:

      import KoaRouter from 'koa-router';const api = KoaRouter();

      Aquí, koa-routerse proporcionan funciones para definir funciones de middleware para métodos y rutas HTTP específicos, por ejemplo, la ruta que almacena eventos en la base de datos:

      // Declare a post method and what it does// :collection is a parameterapi.post('/:collection', // First, validate auth key validateKey, // Then, validate that the provided collection exists validateCollection, // Handle adding the new item to the collection async (ctx, next) = { // Use ES6 destructuring to extract the collection param const { collection } = ctx.params; // Wait until the persistence layer saves the item const count = await ctx .state .collections[collection] .add(ctx.request.body); // Reply with 201 Created when the item is saved ctx.status = 201; });

      Cada método puede tener varios controladores, que se ejecutan secuencialmente y tienen exactamente la misma firma que las funciones de middleware definidas en el nivel superior de app.js. Por ejemplo, validateKeyy validateCollectionson simplemente funciones asíncronas que validan la solicitud entrante y devuelven 404 o 401 si la colección de eventos proporcionada no existe o si la clave API no es válida:

      const validateCollection = async (ctx, next) = { const { collection } = ctx.params; if (!(collection in ctx.state.collections)) { return ctx.throw(404); } await next();}const validateKey = async (ctx, next) = { const { authorization } = ctx.request.headers; if (authorization !== ctx.state.authorizationHeader) { return ctx.throw(401); } await next();}

      Tenga en cuenta que las funciones de middleware de flecha no pueden hacer referencia al contexto de la solicitud actual utilizando this(es decir, thissiempre no está definida en los ejemplos hasta ahora). Por lo tanto, los objetos de solicitud y respuesta, así como los ayudantes de Koa, están disponibles en el objeto de contexto ( ctx). Koa 1 no tenía un objeto de contexto separado y thisse refería al contexto de solicitud actual. Después de definir otros métodos API , finalmente exportamos la API para conectarla a la aplicación Koa en app.js:

      export default api;

      Capa de persistencia

      En api.js, accedemos a la collectionsmatriz en el contexto ctx, que inicializamos en app.js. Estos objetos de colección son responsables de almacenar y recuperar datos almacenados en Redis. La Collectionclase es la siguiente:

      // Use promise-based Redis clientconst redis = require('promise-redis')();const db = redis.createClient();class Collection { // Full source: // https://github.com/OrKoN/koa2-example-app/blob/master/src/collection.js async count() { // We can `await` for promises // The await syntax allows us to work with // async calls as if they were synchronous var count = await db .zcount(this.name, '-inf', '+inf'); return Number(count); } async add(event) { await db .zadd(this.name, 1, JSON.stringify(event)); await this._incrGroups(event); } async _incrGroups(event) { // ES6 for:of syntax allows for easier iteration // groupBy is an array that holds possible attributes of the event for (let attr of this.groupBy) { // We can use await inside loops, // thus calling async operations sequentially in the loop await db.hincrby(`${this.name}_by_${attr}`, event[attr], 1); } }}export default Collection;

      La sintaxis async/await es realmente útil aquí porque nos permite coordinar varias operaciones asíncronas fácilmente, por ejemplo, en un bucle. Pero hay una cosa importante a tener en cuenta. Veamos el _incrGroupsmétodo:

       

      async _incrGroups(event) { // ES6 for:of syntax allows iterating easier for (let attr of this.groupBy) { // We can use await inside loops, // thus calling async operations sequentially in the loop await db.hincrby(`${this.name}_by_${attr}`, event[attr], 1); }}

      Aquí, las claves se incrementan secuencialmente, lo que significa que la siguiente clave se incrementará una vez que el incremento anterior haya tenido éxito. ¡Pero este tipo de trabajo se puede realizar en paralelo! Con async/await, la tarea puede no ser fácil de realizar, pero las promesas pueden ayudar:

      // Start all increments in parallel// because there is no await inside the map callback hereconst promises = this.groupBy.map(attr = db.hincrby(`${this.name}_by_${attr}`, event[attr], 1));// Wait for all of them to finishawait Promise.all(promises);

      La intercambiabilidad de promesas y funciones asíncronas es muy útil.

      Pruebas

      Las pruebas de la aplicación se encuentran en la testcarpeta . Veamos apiSpec.js, que representa la especificación de prueba para la API de la aplicación:

      import { expect } from 'chai';import request from 'supertest';import app from '../build/app';import config from '../build/config';

      Importamos expectdesde chaiy supertest. Usamos la versión precompilada de la aplicación y la configuración para asegurarnos de que estamos probando exactamente el mismo código que se ejecutará en producción. A continuación, escribimos las pruebas para la API, aprovechando la sintaxis async/await para lograr la ejecución secuencial de los pasos de prueba:

      describe('API', () = { const inst = app.listen(4000); describe('POST /:collection', () = { it('should add an event', async () = { const page = 'https://google.com'; const encoded = encodeURIComponent(page); const res = await request(inst) .post(code/pageviews/code) .set({ Authorization: 'Key ' + config.key }) .send({ time: 'now', referrer: 'a', agent: 'b', source: 'c', medium: 'd', campaign: 'e', page: page }) .expect(201); // here res is available // you can use res.headers, res.body etc // no callback for codeexpect/code or codeend/code functions is required. expect(res.body).to.be.empty; }); });});

      Tenga en cuenta que las funciones pasadas a itfunciones están marcadas con async. Esto significa que awaitse puede utilizar para ejecutar tareas asincrónicas, incluidas las supertestsolicitudes, que devuelven thenobjetos capaces y que, por tanto, son compatibles con lo awaitesperado.

       

      Implementación con PM2

      Una vez que la aplicación esté lista y hayan pasado las pruebas, preparemos todo para ejecutar la aplicación en producción. Para esto, declaremos ecosystem.json, que contendrá la configuración de producción de la aplicación:

      { "apps" : [ { "name" : "koa2-example-app", // The entry point to the compiled version of the app "script" : "index.js", // In production, we don't want to watch for changes in files "watch" : false, // And we want to merge logs from all instances "merge_logs" : true, // We want to timestamp log messages "log_date_format": "YYYY-MM-DD HH:mm Z", "env": { // And include environment variables for the app "NODE_ENV": "production", "PORT": 4000 }, // Start two processes of the app, and balance the load between them "instances": 2, // Start app as a cluster "exec_mode" : "cluster_mode", // Watch failures and auto-restart processes "autorestart": true } ]}

      Si su aplicación requiere servidores adicionales (por ejemplo, una tarea cron), puede agregarlos a ecosystem.jsony se iniciarán junto con el servidor principal. Para iniciar la aplicación en el servidor de producción, puede ejecutar esto:

      pm2 start ecosystem.json

      Y para conservar la configuración, ejecutarías esto:

      pm2 save

      PM2 proporciona algunas funciones de monitoreo (pruebe pm2 list, pm2 info, pm2 monit). Por ejemplo, PM2 muestra cuánta memoria está utilizando su aplicación. Esta aplicación básica de Koa consume 44 MB por proceso de Node.js.

      Conclusión

      Babel nos permite crear aplicaciones utilizando la sintaxis ECMAScript que no está disponible de forma nativa pero que incluye async/await, lo que hace que escribir código asincrónico sea más agradable. El código que utiliza la sintaxis async/await es más fácil de leer y mantener. Koa 2 y Babel te permiten comenzar a usar funciones asíncronas ahora mismo. Sin embargo, Babel conlleva una sobrecarga adicional y requiere una configuración adicional y un paso de compilación adicional. Por lo tanto, se recomienda esperar hasta que async/await esté disponible de forma nativa en Node.js. Koa 2 debería lanzarse oficialmente una vez que esto suceda. Entonces, Koa 2 será una buena alternativa a Express porque es más modular y sencillo y permite configurarlo como quieras. La sección de implementación de este tutorial puede ser demasiado simple y no escalable. Deja abierta la cuestión de cómo y cuándo construir e implementar el código; puede hacerlo manualmente ( rsync, scp) o configurar un servidor de integración continua para esto. Además, la arquitectura interna de la aplicación es demasiado simple pero adecuada para una demostración. Las aplicaciones más grandes y ambiciosas pueden requerir otras entidades, como puertas de enlace, mapeadores, repositorios, etc., pero todas ellas pueden aprovechar las funciones asíncronas. Espero que hayas disfrutado de este tutorial. ¡Gracias por leer!

      Enlaces

      • Koa , GitHub Una lista de módulos Koa
      • “ Funciones asíncronas, borrador de etapa 3 ”, Comité Técnico Internacional 39 de Ecma, GitHub La especificación asíncrona
      • koa2-example-app El código fuente completo de nuestra aplicación de demostración
      • “ Middleware ”, Koa, soporte de GitHub Middleware para Koa 2
      • “ Koa 2.0.0 ”, GitHub El problema de GitHub para seguir el progreso de Koa 2
      • “ Implementación de Async/Await en V8 ”, GitHub El problema de GitHub para rastrear el progreso de la implementación de Async/Await en Node (al)

    Explora más en

    • Codificación
    • Marcos
    • javascript
    • API





    Tal vez te puede interesar:

    1. Creación de su propia biblioteca de validación de React: la experiencia del desarrollador (Parte 3)
    2. Introducción a Quasar Framework: creación de aplicaciones multiplataforma
    3. Creación de un componente web retro que se puede arrastrar con iluminación
    4. Creación y acoplamiento de una aplicación Node.js con arquitectura sin estado con la ayuda de Kinsta

    Creación de una aplicación del lado del servidor con funciones asíncronas y Koa 2

    Creación de una aplicación del lado del servidor con funciones asíncronas y Koa 2

    Creación y mantenimiento de sistemas de diseño exitosos, con Brad Fost Patrones de diseño de interfaces inteligentes, vídeo de 10h + formación UX Índi

    programar

    es

    https://aprendeprogramando.es/static/images/programar-creacion-de-una-aplicacion-del-lado-del-servidor-con-funciones-asincronas-y-koa-2-900-0.jpg

    2024-05-20

     

    Creación de una aplicación del lado del servidor con funciones asíncronas y Koa 2
    Creación de una aplicación del lado del servidor con funciones asíncronas y Koa 2

    Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

     

     

    Top 20