{"id":7174,"date":"2021-04-28T15:26:23","date_gmt":"2021-04-28T13:26:23","guid":{"rendered":"http:\/\/www.unimedia.tech.mialias.net\/realtime-with-serverless-using-websocket-in-aws\/"},"modified":"2024-01-10T18:20:10","modified_gmt":"2024-01-10T17:20:10","slug":"realtime-with-serverless-using-websocket-in-aws","status":"publish","type":"post","link":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/","title":{"rendered":"Temps real sense servidor mitjan\u00e7ant Websocket a AWS"},"content":{"rendered":"\n<p>Hola a tots, Avui parlarem sobre la implementaci\u00f3 de websocket amb arquitectura sense servidor fent servir Node.js amb Typescript per a la comunicaci\u00f3 en temps real.<\/p>\n\n<p><\/p>\n\n<p>Si proveu de cercar-lo a Internet, trobareu detalls <a href=\"https:\/\/www.unimedia.tech\/dedicated-development-teams\/\">d&#8217;implementaci\u00f3<\/a> , per\u00f2 no hi ha res concret disponible. Per tant, aqu\u00ed estic construint un ecosistema de websocket complet amb tots els detalls de codi i configuraci\u00f3.<\/p>\n\n<p><\/p>\n\n<p>Per <a href=\"https:\/\/www.unimedia.tech\/software-development\/\">al desenvolupament<\/a> , cal tenir<code><strong>serverless<\/strong><\/code> ,<code><strong>npm<\/strong><\/code> i\/o<code><strong>yarn<\/strong><\/code> instal\u00b7lat globalment. Vegeu com <a href=\"https:\/\/classic.yarnpkg.com\/en\/docs\/install\/\">instal\u00b7lar YARN<\/a> i <a href=\"https:\/\/docs.npmjs.com\/downloading-and-installing-node-js-and-npm\">NPM<\/a> . Tamb\u00e9 prefereixo utilitzar <a href=\"https:\/\/github.com\/nvm-sh\/nvm#installing-and-updating\">NVM<\/a> per gestionar les meves versions de Node.<\/p>\n\n<p><\/p>\n\n<h2 class=\"wp-block-heading\">Desenvolupament<\/h2>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\" id=\"4fcc\">Pas 1. Configurar AWS<\/h3>\n\n<p id=\"a6a6\">Configureu l&#8217;AWS CLI si encara no ho heu fet, aqu\u00ed hi ha un article molt bo sobre <a href=\"https:\/\/sidneyb231.medium.com\/configure-aws-for-development-and-deployment-ad822097fc22\">crear i configurar credencials d&#8217;AWS<\/a> . A continuaci\u00f3, hem de configurar la nostra aplicaci\u00f3.<\/p>\n\n<p><\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\" id=\"b92d\">Pas 2. Projecte d&#8217;instal\u00b7laci\u00f3<\/h3>\n\n<p id=\"7031\">Creeu i configureu un projecte sense servidor amb mecanografia:<\/p>\n\n<pre class=\"wp-block-code\"><code>$ sls create --template aws-nodejs-typescript --path &lt;PROJECT-NAME&gt;<\/code><\/pre>\n\n<p id=\"3e73\">On<code><strong>&lt;PROJECT-NAME&gt;<\/strong><\/code> \u00e9s el nom del teu projecte.<\/p>\n\n<p id=\"f6f1\">Aix\u00f2 genera un boilerplate sense servidor per a la nostra aplicaci\u00f3. A continuaci\u00f3, hem d&#8217;anar al nostre nou projecte<code>cd &lt;PROJECT-NAME&gt;<\/code> i instal\u00b7leu les nostres depend\u00e8ncies executant<code><strong>yarn install<\/strong><\/code> .<\/p>\n\n<p id=\"52c5\">Els fitxers m\u00e9s importants que anirem actualitzant a mesura que desenvolupem la nostra aplicaci\u00f3 s\u00f3n els<code><strong>handler.ts<\/strong><\/code> i<code><strong>serverless.ts<\/strong><\/code> . El<code><strong>handler.ts<\/strong><\/code> gestiona les nostres funcions lambda o refer\u00e8ncies a les nostres funcions lambda. Hauria de ser aix\u00ed:<\/p>\n\n<pre class=\"wp-block-code\"><code>import { APIGatewayProxyHandler } from 'aws-lambda';\nimport 'source-map-support\/register';\n\nexport const hello: APIGatewayProxyHandler = async (event, _context) =&gt; {\n  return {\n    statusCode: 200,\n    body: JSON.stringify({\n      message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!',\n      input: event,\n    }, null, 2),\n  };\n}<\/code><\/pre>\n\n<p><\/p>\n\n<p><\/p>\n\n<p>El fitxer serverless.ts ens proporciona una plantilla per modelar i subministrar els nostres recursos d&#8217;aplicaci\u00f3 per a AWS CloudFormation tractant la infraestructura com a codi. Aix\u00f2 ens ajuda a crear, actualitzar i fins i tot eliminar recursos f\u00e0cilment. Hauria de ser aix\u00ed:<\/p>\n\n<pre class=\"wp-block-code\"><code>import type { Serverless } from 'serverless\/aws';\n\nconst serverlessConfiguration: Serverless = {\n  service: {\n    name: 'serverless-websocket-ts',\n    \/\/ app and org for use with dashboard.serverless.com\n    \/\/ app: your-app-name,\n    \/\/ org: your-org-name,\n  },\n  frameworkVersion: '&gt;=1.72.0',\n  custom: {\n    webpack: {\n      webpackConfig: '.\/webpack.config.js',\n      includeModules: true\n    }\n  },\n  \/\/ Add the serverless-webpack plugin\n  plugins: &#91;'serverless-webpack'],\n  provider: {\n    name: 'aws',\n    runtime: 'nodejs12.x',\n    apiGateway: {\n      minimumCompressionSize: 1024,\n    },\n    environment: {\n      AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',\n    },\n  },\n  functions: {\n    hello: {\n      handler: 'handler.hello',\n      events: &#91;\n        {\n          http: {\n            method: 'get',\n            path: 'hello',\n          }\n        }\n      ]\n    }\n  }\n}\n\nmodule.exports = serverlessConfiguration;<\/code><\/pre>\n\n<p><\/p>\n\n<p id=\"16b6\">A m\u00e9s, a causa de <a href=\"https:\/\/www.serverless.com\/framework\/docs\/deprecations\/\">les obsolesc\u00e8ncies<\/a> , haureu d&#8217;establir la propietat del servei directament amb el nom del servei:<\/p>\n\n<pre class=\"wp-block-code\"><code>{ \n  service: 'serverless-websocket-ts',\n  ...\n}<\/code><\/pre>\n\n<p><\/p>\n\n<p id=\"ed9f\">I actualitzeu el<code><strong>provider.apiGateway<\/strong><\/code> objecte de la seg\u00fcent manera:<\/p>\n\n<pre class=\"wp-block-code\"><code>provider: {<br>  ...<br>  apiGateway: {<br>    shouldStartNameWithService: true,<br>    ...<br>  },<br>  ...<br>},<\/code><\/pre>\n\n<p id=\"e05c\">Podem executar la nostra funci\u00f3 a l&#8217;arrel del nostre projecte<code><strong>$ serverless invoke local --function hello<\/strong><\/code> i haur\u00edeu de veure la resposta:<\/p>\n\n<pre class=\"wp-block-code\"><code>{\n    \"statusCode\": 200,\n    \"body\": \"{n  \"message\": \"Go Serverless Webpack (Typescript) v1.0! Your function executed           successfully!\",n  \"input\": \"n}\"\n}<\/code><\/pre>\n\n<p><\/p>\n\n<p id=\"59bf\">El marc sense servidor invoca localment el<code>hello<\/code> funci\u00f3 i executa l&#8217;exportat<code><strong>hello<\/strong><\/code> m\u00e8tode en el<code><strong>handler.ts<\/strong><\/code> dossier. El<code>serverless invoke local<\/code> L&#8217;ordre ens permet executar les nostres funcions Lambda localment abans de desplegar-les.<\/p>\n\n<p id=\"7be0\">A continuaci\u00f3, hem d&#8217;instal\u00b7lar algunes depend\u00e8ncies per a la nostra aplicaci\u00f3:<\/p>\n\n<pre class=\"wp-block-code\"><code>$ yarn add -D serverless-offline serverless-dotenv-plugin serverless-bundle<br>$ yarn add aws-sdk uuid @types\/uuid<\/code><\/pre>\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/www.npmjs.com\/package\/aws-sdk\"><strong>aws-sdk<\/strong><\/a> : ens permet comunicar-nos amb els serveis d&#8217;AWS<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/uuid\"><strong>uuid<\/strong><\/a> \u2014 genera identificadors \u00fanics per a les entrades de la nostra base de dades<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/serverless-offline\"><strong>serverless-offline<\/strong><\/a> : aquest connector ens permet executar la nostra aplicaci\u00f3 i les funcions Lambda localment<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/serverless-dotenv-plugin\"><strong>serverless-dotenv-plugin<\/strong><\/a> \u2014 per permetre&#8217;ns la c\u00e0rrega<code><strong>.env<\/strong><\/code> variables al nostre entorn Lambda<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/serverless-bundle\"><strong>serverless-bundle<\/strong><\/a> : aquest connector empaqueta de manera \u00f2ptima les nostres funcions Typescript i assegura que no ens hem de preocupar per instal\u00b7lar <a href=\"https:\/\/babeljs.io\/\"><strong>Babel<\/strong><\/a> , <a href=\"https:\/\/www.typescriptlang.org\/\"><strong>Typescript<\/strong><\/a> , <a href=\"https:\/\/webpack.js.org\/\"><strong>Webpack<\/strong><\/a> , <a href=\"https:\/\/eslint.org\/\"><strong>ESLint<\/strong><\/a> i una s\u00e8rie d&#8217;altres paquets. Aix\u00f2 vol dir que no necessitem mantenir les nostres pr\u00f2pies configuracions de paquet web. Aix\u00ed que podem seguir endavant i eliminar el<code><strong>webpack.config.js<\/strong><\/code> fitxer i la refer\u00e8ncia a ell<code>serverless.ts<\/code> propietat personalitzada:<\/li><\/ul>\n\n<pre class=\"wp-block-code\"><code>\/\/ FROM<br>custom: {<br>  webpack: {<br>    webpackConfig: '.\/webpack.config.js',<br>    includeModules: true<br>  }<br>}\/\/ TO<br>custom: {<br>  <br>}<\/code><\/pre>\n\n<p><\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Pas 3. Recursos de configuraci\u00f3<\/h3>\n\n<p>Ara ens hem de posar<code><strong>region<\/strong><\/code> ,<code><strong>stage<\/strong><\/code> ,<code><strong>table_throughputs<\/strong><\/code> ,<code><strong>table_throughput<\/strong><\/code> i les variables <strong>connections_table<\/strong> . Tamb\u00e9 es configura<code><strong>dynamodb<\/strong><\/code> i<code><strong>serverless-offline<\/strong><\/code> per al desenvolupament local.<\/p>\n\n<p><\/p>\n\n<pre class=\"wp-block-code\"><code>import type { Serverless } from 'serverless\/aws';\n\nconst serverlessConfiguration: Serverless = {\n  service: 'serverless-todo',\n  frameworkVersion: '&gt;=1.72.0',\n  custom: {\n    region: '${opt:region, self:provider.region}',\n    stage: '${opt:stage, self:provider.stage}',\n    connections_table: '${self:service}-connections-table-${opt:stage, self:provider.stage}',\n    table_throughputs: {\n      prod: 5,\n      default: 1,\n    },\n    table_throughput: '${self:custom.TABLE_THROUGHPUTS.${self:custom.stage}, self:custom.table_throughputs.default}',\n    dynamodb: {\n      stages: &#91;'dev'],\n      start: {\n        port: 8008,\n        inMemory: true,\n        heapInitial: '200m',\n        heapMax: '1g',\n        migrate: true,\n        seed: true,\n        convertEmptyValues: true,\n        \/\/ Uncomment only if you already have a DynamoDB running locally\n        \/\/ noStart: true\n      }\n    },\n    &#91;'serverless-offline']: {\n      httpPort: 3000,\n      babelOptions: {\n        presets: &#91;\"env\"]\n      }\n    }\n  },\n  plugins: &#91;\n      'serverless-bundle',\n      'serverless-offline',\n      'serverless-dotenv-plugin',\n  ],\n  package: {\n    individually: true,\n  },\n  provider: {\n    name: 'aws',\n    runtime: 'nodejs12.x',\n    stage: 'dev',\n    region: 'eu-west-1',\n    apiGateway: {\n      shouldStartNameWithService: true,\n      minimumCompressionSize: 1024,\n    },\n    environment: {\n      AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',\n    },\n  },\n  functions: {\n    hello: {\n      handler: 'handler.hello',\n      events: &#91;\n        {\n          http: {\n            method: 'get',\n            path: 'hello',\n          }\n        }\n      ]\n    }\n  },\n  \n}\n\nmodule.exports = serverlessConfiguration;<\/code><\/pre>\n\n<p><\/p>\n\n<p id=\"7fde\">A continuaci\u00f3, hem d&#8217;actualitzar el<code><strong>provider.environment<\/strong><\/code> propietat a la<code><strong>serverless.ts<\/strong><\/code> fitxer de la seg\u00fcent manera:<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ serverless.ts\nprovider: {\n  ...\n  environment: {\n    ...\n    REGION: '${self:custom.region}',\n    STAGE: '${self:custom.stage}',\n    CONNECTIONS_TABLE: '${self:custom.connections_table}',\n  },\n  ...\n},<\/code><\/pre>\n\n<p><\/p>\n\n<p>A continuaci\u00f3, hem d&#8217;afegir<code><strong>resources<\/strong><\/code> que s&#8217;afegeixen a la vostra pila de CloudFormation quan es desplega la nostra aplicaci\u00f3. Hem de definir els recursos d&#8217;AWS en una propietat titulada<code><strong>resources<\/strong><\/code> .  <\/p>\n\n<p>Podeu llegir m\u00e9s sobre com <a href=\"https:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/WorkingWithTables.html\">treballar amb taules i dades a DynamoDB<\/a> .<\/p>\n\n<p id=\"b50e\">Utilitzant la pot\u00e8ncia de Javascript, podem evitar grans fitxers de definici\u00f3 de serveis dividint-los en fitxers i utilitzant importacions din\u00e0miques. Aix\u00f2 \u00e9s important perqu\u00e8 a mesura que la nostra aplicaci\u00f3 creix, els fitxers de definici\u00f3 separats fan que sigui m\u00e9s f\u00e0cil de mantenir.<\/p>\n\n<p id=\"e53c\">Per a aquest projecte, organitzarem els nostres recursos per separat i els importarem<code><strong>serverless.ts<\/strong><\/code> . Per fer-ho, primer hem de crear un<code><strong>resources<\/strong><\/code> directori al nostre directori arrel i despr\u00e9s creeu un<code><strong>dynamodb-tables.ts<\/strong><\/code> fitxer per a les nostres taules DynamoDB:<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ At project root\n$ touch resources\/dynamodb-tables.ts <\/code><\/pre>\n\n<p>A continuaci\u00f3, actualitzem el<code><strong>dynamodb-tables.ts<\/strong><\/code> fitxer de la seg\u00fcent manera:<\/p>\n\n<p><\/p>\n\n<pre class=\"wp-block-code\"><code>export default {\n    ConnectionsTable: {\n        Type: 'AWS::DynamoDB::Table',\n        Properties: {\n            TableName: '${self:provider.environment.CONNECTIONS_TABLE}',\n            AttributeDefinitions: &#91;\n                { AttributeName: 'connectionId', AttributeType: 'S' }\n            ],\n            KeySchema: &#91;\n                { AttributeName: 'connectionId', KeyType: 'HASH' }\n            ],\n            ProvisionedThroughput: {\n                ReadCapacityUnits: '${self:custom.table_throughput}',\n                WriteCapacityUnits: '${self:custom.table_throughput}'\n            },\n            SSESpecification: {\n                SSEEnabled: true\n            },\n            TimeToLiveSpecification: {\n                AttributeName: 'ttl',\n                Enabled: true\n            }\n        }\n    },\n}<\/code><\/pre>\n\n<p><\/p>\n\n<p>I importar a<code><strong>serverless.ts<\/strong><\/code> \u2014 i establiu-lo a les propietats de recursos tal com es mostra a continuaci\u00f3,<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ DynamoDB\nimport dynamoDbTables from '.\/resources\/dynamodb-tables';\n\nconst serverlessConfiguration: Serverless = {\n  service: 'serverless-todo',\n  ...\n  resources: {\n    Resources: dynamoDbTables,\n  }\n  ...\n}\n\nmodule.exports = serverlessConfiguration;<\/code><\/pre>\n\n<p><\/p>\n\n<p id=\"bc8b\">Per executar DynamoDB localment, primer hem d&#8217;instal\u00b7lar el fitxer<code><strong>serverless-dynamodb-local<\/strong><\/code> connectar:<\/p>\n\n<pre class=\"wp-block-code\"><code>$ yarn add -D serverless-dynamodb-local <\/code><\/pre>\n\n<p id=\"1d82\">A continuaci\u00f3, hem d&#8217;actualitzar<code><strong>serverless.ts<\/strong><\/code> matriu de connectors:<\/p>\n\n<pre class=\"wp-block-code\"><code>plugins: &#91;<br>    'serverless-bundle',<br>    'serverless-dynamodb-local',<br>    'serverless-offline',<br>    'serverless-dotenv-plugin',<br>],<\/code><\/pre>\n\n<p id=\"f80b\">Per utilitzar el connector, hem d&#8217;instal\u00b7lar DynamoDB Local executant-lo<code><strong>sls dynamodb install<\/strong><\/code> a l&#8217;arrel del projecte. C\u00f3rrer<code><strong>sls dynamodb start<\/strong><\/code> l&#8217;iniciar\u00e0 localment:<\/p>\n\n<p><\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Pas 4. Integraci\u00f3 de Websocket<\/h3>\n\n<p>En aquest pas, crearem un gestor de socket web per connectar-nos amb esdeveniments de websocket.<\/p>\n\n<p>Podeu consultar aqu\u00ed els detalls sobre <a href=\"https:\/\/www.serverless.com\/framework\/docs\/providers\/aws\/events\/websocket\/\" target=\"_blank\" rel=\"noreferrer noopener\">els esdeveniments<\/a> de websocket.<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ create a websocket folder\nmkdir websocket\n\ncd websocket\ntouch handler.ts\ntouch index.ts\ntouch schemas.ts\ntouch broadcast.ts<\/code><\/pre>\n\n<p><\/p>\n\n<p>El fitxer <strong>handler.ts<\/strong> ser\u00e0 com es mostra a continuaci\u00f3:<\/p>\n\n<pre class=\"wp-block-code\"><code>\n\/\/ handler.ts\n\nimport type { ValidatedEventAPIGatewayProxyEvent } from '@libs\/apiGateway';\nimport { formatJSONResponse } from '@libs\/apiGateway';\nimport { middyfy } from '@libs\/lambda';\nimport * as AWS from 'aws-sdk';\nimport { getAllConnections, sendMessage } from '.\/broadcast';\nimport schema from '.\/schema';\n\nconst config: any = { region: \"us-east-1\" };\nif (process.env.STAGE === process.env.DYNAMODB_LOCAL_STAGE) {\n  config.accessKeyId = process.env.DYNAMODB_LOCAL_ACCESS_KEY_ID;\n  config.secretAccessKey = process.env.DYNAMODB_LOCAL_SECRET_ACCESS_KEY;\n  config.endpoint = process.env.DYNAMODB_LOCAL_ENDPOINT;\n}\n\nAWS.config.update(config);\n\nconst dynamodb = new AWS.DynamoDB.DocumentClient();\n\nconst connectionTable = process.env.CONNECTIONS_TABLE;\n\nconst websocketHandler: ValidatedEventAPIGatewayProxyEvent&lt;typeof schema&gt; = async (event) =&gt; {\n  console.log(event);\n  const { body, requestContext: { connectionId, routeKey }} = event;\n  console.log(body, routeKey, connectionId);\n  \n  switch (routeKey) {\n    case '$connect':\n      await dynamodb.put({\n        TableName: connectionTable,\n        Item: {\n          connectionId,\n          \/\/ Expire the connection an hour later. This is optional, but recommended.\n          \/\/ You will have to decide how often to time out and\/or refresh the ttl.\n          ttl: parseInt((Date.now() \/ 1000).toString() + 3600)\n        }\n      }).promise();\n      break;\n\n    case '$disconnect':\n      await dynamodb.delete({\n        TableName: connectionTable,\n        Key: { connectionId }\n      }).promise();\n      break;\n\n    case '$default':\n    default:\n      const connections = await getAllConnections();\n      await Promise.all(\n        connections.map(connectionId =&gt; sendMessage(connectionId, body))\n      );\n      break;\n  }\n\n  return formatJSONResponse({ statusCode: 200 });\n}\n\nexport const wsHandler = middyfy(websocketHandler);\n<\/code><\/pre>\n\n<p>L&#8217;API-Gateway ofereix <a href=\"https:\/\/docs.aws.amazon.com\/apigateway\/latest\/developerguide\/apigateway-websocket-api-overview.html\">4 tipus de rutes<\/a> relacionades amb el cicle de vida d&#8217;un client ws:<\/p>\n\n<figure class=\"wp-block-table\"><table class=\"has-background\" style=\"background-color:#e7f5fe\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Esdeveniment<\/th><th>Acci\u00f3<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$connect<\/strong><\/td><td>cridat a la connexi\u00f3 d&#8217;un client ws<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$desconnectar<\/strong> t<\/td><td>cridat a la desconnexi\u00f3 d&#8217;un client ws (\u00e9s possible que no es cridi en algunes situacions)<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$per defecte<\/strong><\/td><td>crida si no hi ha cap controlador per utilitzar per a l&#8217;esdeveniment<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><custom-route><\/custom-route><\/td><td>crida si el nom de la ruta s&#8217;especifica per a un controlador<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<p>A partir d&#8217;aix\u00f2, hem creat un cas de canvi, on cada cas far\u00e0 l&#8217;acci\u00f3 adequada relacionada amb aquest esdeveniment.<\/p>\n\n<figure class=\"wp-block-table\"><table class=\"has-background\" style=\"background-color:#e7f5fe\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Esdeveniment<\/th><th>Acci\u00f3<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$connect<\/strong><\/td><td>crear\u00e0 una entrada a <em>connections-table<\/em> , amb connectionId i ttl.<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$desconnectar<\/strong> t<\/td><td>eliminar\u00e0 la connexi\u00f3 de dynamodb <em>connections-table<\/em> .<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$per defecte<\/strong><\/td><td>transmetr\u00e0 les dades a totes les connexions.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<p><strong>schema.ts<\/strong> ser\u00e0 responsable de validar l&#8217;esquema.<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ schema.ts\n\nexport default {\n  type: \"object\",\n  properties: {\n    name: { type: 'string' }\n  },\n  required: &#91;'name']\n} as const;<\/code><\/pre>\n\n<p><\/p>\n\n<p><\/p>\n\n<p><strong>index.ts<\/strong> mapar\u00e0 els esdeveniments de websocket amb el controlador:<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ index.ts\n\nimport { handlerPath } from '@libs\/handlerResolver';\n\nexport const wsHandler = {\n  handler: `${handlerPath(__dirname)}\/handler.wsHandler`,\n  events: &#91;\n    {\n      websocket: '$connect'\n    },\n    {\n      websocket: '$disconnect',\n    },\n    {\n      websocket: '$default'\n    }\n  ]\n};<\/code><\/pre>\n\n<p><\/p>\n\n<p>Hem de registrar l&#8217;exportaci\u00f3 <strong><em>de wsHandler<\/em><\/strong> a <em>src\/functions\/index.ts<\/em><\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ src\/functions\/index.ts\n\nexport { default as hello } from '.\/hello';\nexport { wsHandler } from '.\/websocket';\n<\/code><\/pre>\n\n<p><\/p>\n\n<p><strong>broadcast.ts<\/strong> cont\u00e9 2 funcions importants com es mostren a continuaci\u00f3<\/p>\n\n<pre class=\"wp-block-code\"><code>import * as AWS from 'aws-sdk';\n\nconst config: any = { region: \"us-east-1\" };\nif (process.env.STAGE === process.env.DYNAMODB_LOCAL_STAGE) {\n    config.accessKeyId = process.env.DYNAMODB_LOCAL_ACCESS_KEY_ID;\n    config.secretAccessKey = process.env.DYNAMODB_LOCAL_SECRET_ACCESS_KEY;\n    config.endpoint = process.env.DYNAMODB_LOCAL_ENDPOINT;\n}\n\nAWS.config.update(config);\n\nconst dynamodb = new AWS.DynamoDB.DocumentClient();\n\nconst connectionTable = process.env.CONNECTIONS_TABLE;\n\nexport async function sendMessage(connectionId, body) {\n    try {\n        const endpoint = process.env.APIG_ENDPOINT;\n        const apig = new AWS.ApiGatewayManagementApi({\n            apiVersion: '2018-11-29',\n            endpoint\n        });\n        await apig.postToConnection({\n            ConnectionId: connectionId,\n            Data: JSON.stringify(body)\n        }).promise();\n    } catch (err) {\n        \/\/ Ignore if connection no longer exists\n        if (err.statusCode !== 400 &amp;&amp; err.statusCode !== 410) {\n            throw err;\n        }\n    }\n}\n\nexport async function getAllConnections() {\n    const { Items, LastEvaluatedKey } = await dynamodb.scan({\n        TableName: connectionTable,\n        AttributesToGet: &#91;'connectionId']\n    }).promise();\n\n    const connections = Items.map(({ connectionId }) =&gt; connectionId);\n    if (LastEvaluatedKey) {\n        connections.push(...await getAllConnections());\n    }\n\n    return connections;\n}<\/code><\/pre>\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Funci\u00f3<\/th><th>Explicaci\u00f3<\/th><\/tr><\/thead><tbody><tr><td>enviar missatge<\/td><td>accepta <strong><em>connectionId<\/em><\/strong> i <em><strong>body<\/strong><\/em> com a par\u00e0metres. Llegeix APIG_ENDPOINT de la variable d&#8217;entorn i crea una inst\u00e0ncia d&#8217; <strong>ApiGatewayManagementApi<\/strong> i despr\u00e9s envia el cos a <em>connectionId<\/em> donat mitjan\u00e7ant <strong>postToConnection<\/strong> .<\/td><\/tr><tr><td>getAllConnections<\/td><td>retorna totes les connexions<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Pas 4. Websocket que s&#8217;executa localment<\/h3>\n\n<p>En aquest pas, executarem el websocket localment, fent servir fora de l\u00ednia sense servidor:<\/p>\n\n<p>Abans de comen\u00e7ar, compareu i confirmeu el fitxer serverless.ts tal com es mostra a continuaci\u00f3<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ serverless.ts\n\nimport type { AWS } from '@serverless\/typescript';\n\nimport hello from '@functions\/hello';\nimport { wsHandler } from '@functions\/websocket';\nimport dynamoDbTables from '.\/dynamodb-tables';\n\n\nconst serverlessConfiguration: AWS = {\n  service: 'serverless-websocket-ts',\n  frameworkVersion: '2',\n  custom: {\n    region: '${opt:region, self:provider.region}',\n    stage: '${opt:stage, self:provider.stage}',\n    prefix: '${self:service}-${self:custom.stage}',\n    connections_table: '${self:service}-connections-table-${opt:stage, self:provider.stage}',\n    &#91;'serverless-offline']: {\n      httpPort: 3000,\n    },\n    &#91;'bundle']: {\n      linting: false\n    },\n    table_throughputs: {\n      prod: 5,\n      default: 1,\n    },\n    table_throughput: '${self:custom.TABLE_THROUGHPUTS.${self:custom.stage}, self:custom.table_throughputs.default}',\n    dynamodb: {\n      stages: &#91;'dev'],\n      start: {\n        port: 8008,\n        inMemory: true,\n        heapInitial: '200m',\n        heapMax: '1g',\n        migrate: true,\n        seed: true,\n        convertEmptyValues: true,\n        \/\/ Uncomment only if you already have a DynamoDB running locally\n        \/\/ noStart: true\n      }\n    }\n  },\n  plugins: &#91;\n    'serverless-bundle',\n    'serverless-dynamodb-local',\n    'serverless-offline',\n    'serverless-dotenv-plugin',\n  ],\n  package: {\n    individually: true,\n  },\n  provider: {\n    name: 'aws',\n    runtime: 'nodejs14.x',\n    apiGateway: {\n      minimumCompressionSize: 1024,\n      shouldStartNameWithService: true,\n    },\n    environment: {\n      AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1',\n      REGION: '${self:custom.region}',\n      STAGE: '${self:custom.stage}',\n      APIG_ENDPOINT: 'http:\/\/localhost:3001',\n      CONNECTIONS_TABLE: '${self:custom.connections_table}',\n    },\n    lambdaHashingVersion: '20201221',\n  },\n  \/\/ import the function via paths\n  functions: { \n    hello,\n    wsHandler\n  },\n\n  resources: {\n    Resources: dynamoDbTables,\n  }\n};\n\nmodule.exports = serverlessConfiguration;\n<\/code><\/pre>\n\n<pre class=\"wp-block-code\"><code>$ serverless offline start\n\nServerless: Bundling with Webpack...\nServerless: Watching for changes...\nDynamodb Local Started, Visit: http:\/\/localhost:8008\/shell\nIssues checking in progress...\nNo issues found.\nServerless: DynamoDB - created table serverless-websocket-ts-connections-table-dev\noffline: Starting Offline: dev\/us-east-1.\noffline: Offline &#91;http for lambda] listening on http:\/\/localhost:3002\noffline: Function names exposed for local invocation by aws-sdk:\n           * hello: serverless-websocket-ts-dev-hello\n           * wsHandler: serverless-websocket-ts-dev-wsHandler   \noffline: route '$connect'\noffline: route '$disconnect'\noffline: route '$default'\noffline: Offline &#91;websocket] listening on ws:\/\/localhost:3001\n\n   \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n   \u2502                                                                         \u2502\n   \u2502   POST | http:\/\/localhost:3000\/dev\/hello                                \u2502\n   \u2502   POST | http:\/\/localhost:3000\/2015-03-31\/functions\/hello\/invocations   \u2502\n   \u2502                                                                         \u2502\n   \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n\noffline: Offline &#91;http for websocket] listening on http:\/\/localhost:3001\noffline: &#91;HTTP] server ready: http:\/\/localhost:3000  \noffline:\noffline: Enter \"rp\" to replay the last request\n<\/code><\/pre>\n\n<p><\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Pas 5: demostraci\u00f3 de Websocket<\/h3>\n\n<p><\/p>\n\n<p><\/p>\n\n<figure class=\"wp-block-video\"><video controls=\"\" src=\"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2021\/04\/Websocket-Demo.mp4\"><\/video><\/figure>\n\n<p>Espero que aquest post us sigui \u00fatil!<\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Unimedia Technology<\/h3>\n\n<p>Aqu\u00ed a <a href=\"https:\/\/www.unimedia.tech\/\">Unimedia Technology<\/a> tenim un equip de <strong>desenvolupadors de BackEnd<\/strong> que us poden ajudar a desenvolupar les vostres aplicacions m\u00e9s complexes<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hola a tots, Avui parlarem sobre la implementaci\u00f3 de websocket amb arquitectura sense servidor fent servir Node.js amb Typescript per a la comunicaci\u00f3 en temps real. Si proveu de cercar-lo a Internet, trobareu detalls d&#8217;implementaci\u00f3 , per\u00f2 no hi ha res concret disponible. Per tant, aqu\u00ed estic construint un ecosistema de websocket complet amb tots [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":6624,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[216,218],"tags":[],"class_list":["post-7174","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-serverless-ca","category-technical-guides-ca"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v21.6 (Yoast SEO v27.1.1) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Temps real sense servidor mitjan\u00e7ant Websocket a AWS - Unimedia Technology<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/\" \/>\n<meta property=\"og:locale\" content=\"ca_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Temps real sense servidor mitjan\u00e7ant Websocket a AWS\" \/>\n<meta property=\"og:description\" content=\"Hola a tots, Avui parlarem sobre la implementaci\u00f3 de websocket amb arquitectura sense servidor fent servir Node.js amb Typescript per a la comunicaci\u00f3 en temps real. Si proveu de cercar-lo a Internet, trobareu detalls d&#8217;implementaci\u00f3 , per\u00f2 no hi ha res concret disponible. Per tant, aqu\u00ed estic construint un ecosistema de websocket complet amb tots [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/\" \/>\n<meta property=\"og:site_name\" content=\"Unimedia Technology\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.linkedin.com\/company\/unimedia-technology\/\" \/>\n<meta property=\"article:published_time\" content=\"2021-04-28T13:26:23+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-01-10T17:20:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/websocket-serverless-ts-4.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1120\" \/>\n\t<meta property=\"og:image:height\" content=\"660\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Unimedia\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@UnimediaCTO\" \/>\n<meta name=\"twitter:site\" content=\"@UnimediaCTO\" \/>\n<meta name=\"twitter:label1\" content=\"Escrit per\" \/>\n\t<meta name=\"twitter:data1\" content=\"Unimedia\" \/>\n\t<meta name=\"twitter:label2\" content=\"Temps estimat de lectura\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minuts\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Temps real sense servidor mitjan\u00e7ant Websocket a AWS - Unimedia Technology","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/","og_locale":"ca_ES","og_type":"article","og_title":"Temps real sense servidor mitjan\u00e7ant Websocket a AWS","og_description":"Hola a tots, Avui parlarem sobre la implementaci\u00f3 de websocket amb arquitectura sense servidor fent servir Node.js amb Typescript per a la comunicaci\u00f3 en temps real. Si proveu de cercar-lo a Internet, trobareu detalls d&#8217;implementaci\u00f3 , per\u00f2 no hi ha res concret disponible. Per tant, aqu\u00ed estic construint un ecosistema de websocket complet amb tots [&hellip;]","og_url":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/","og_site_name":"Unimedia Technology","article_publisher":"https:\/\/www.linkedin.com\/company\/unimedia-technology\/","article_published_time":"2021-04-28T13:26:23+00:00","article_modified_time":"2024-01-10T17:20:10+00:00","og_image":[{"width":1120,"height":660,"url":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/websocket-serverless-ts-4.png","type":"image\/png"}],"author":"Unimedia","twitter_card":"summary_large_image","twitter_creator":"@UnimediaCTO","twitter_site":"@UnimediaCTO","twitter_misc":{"Escrit per":"Unimedia","Temps estimat de lectura":"11 minuts"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#article","isPartOf":{"@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/"},"author":{"name":"Unimedia","@id":"https:\/\/www.unimedia.tech\/ca\/#\/schema\/person\/3a250aa22526d5c9ff6bc95bb380a5dd"},"headline":"Temps real sense servidor mitjan\u00e7ant Websocket a AWS","datePublished":"2021-04-28T13:26:23+00:00","dateModified":"2024-01-10T17:20:10+00:00","mainEntityOfPage":{"@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/"},"wordCount":1027,"commentCount":0,"publisher":{"@id":"https:\/\/www.unimedia.tech\/ca\/#organization"},"image":{"@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#primaryimage"},"thumbnailUrl":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/websocket-serverless-ts-4.png","articleSection":["Serverless","Technical Guides"],"inLanguage":"ca","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/","url":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/","name":"Temps real sense servidor mitjan\u00e7ant Websocket a AWS - Unimedia Technology","isPartOf":{"@id":"https:\/\/www.unimedia.tech\/ca\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#primaryimage"},"image":{"@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#primaryimage"},"thumbnailUrl":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/websocket-serverless-ts-4.png","datePublished":"2021-04-28T13:26:23+00:00","dateModified":"2024-01-10T17:20:10+00:00","breadcrumb":{"@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#breadcrumb"},"inLanguage":"ca","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/"]}]},{"@type":"ImageObject","inLanguage":"ca","@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#primaryimage","url":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/websocket-serverless-ts-4.png","contentUrl":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/websocket-serverless-ts-4.png","width":1120,"height":660},{"@type":"BreadcrumbList","@id":"https:\/\/www.unimedia.tech\/ca\/realtime-with-serverless-using-websocket-in-aws\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.unimedia.tech\/ca\/"},{"@type":"ListItem","position":2,"name":"Temps real sense servidor mitjan\u00e7ant Websocket a AWS"}]},{"@type":"WebSite","@id":"https:\/\/www.unimedia.tech\/ca\/#website","url":"https:\/\/www.unimedia.tech\/ca\/","name":"Unimedia Technology","description":"Your software development partner","publisher":{"@id":"https:\/\/www.unimedia.tech\/ca\/#organization"},"alternateName":"Unimedia Tech","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.unimedia.tech\/ca\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ca"},{"@type":"Organization","@id":"https:\/\/www.unimedia.tech\/ca\/#organization","name":"Unimedia Technology","alternateName":"Unimedia Tech","url":"https:\/\/www.unimedia.tech\/ca\/","logo":{"@type":"ImageObject","inLanguage":"ca","@id":"https:\/\/www.unimedia.tech\/ca\/#\/schema\/logo\/image\/","url":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/cloud_border-3.png","contentUrl":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/cloud_border-3.png","width":403,"height":309,"caption":"Unimedia Technology"},"image":{"@id":"https:\/\/www.unimedia.tech\/ca\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/company\/unimedia-technology\/","https:\/\/x.com\/UnimediaCTO","https:\/\/www.instagram.com\/unimedia.technology\/"]},{"@type":"Person","@id":"https:\/\/www.unimedia.tech\/ca\/#\/schema\/person\/3a250aa22526d5c9ff6bc95bb380a5dd","name":"Unimedia","image":{"@type":"ImageObject","inLanguage":"ca","@id":"https:\/\/www.unimedia.tech\/ca\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/5901fd1c4628e2b48ffd4e47324e8fe0751b39e556a167f078471d4c4bec0f6f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/5901fd1c4628e2b48ffd4e47324e8fe0751b39e556a167f078471d4c4bec0f6f?s=96&d=mm&r=g","caption":"Unimedia"}}]}},"_links":{"self":[{"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/posts\/7174","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/comments?post=7174"}],"version-history":[{"count":0,"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/posts\/7174\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/media\/6624"}],"wp:attachment":[{"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/media?parent=7174"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/categories?post=7174"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.unimedia.tech\/ca\/wp-json\/wp\/v2\/tags?post=7174"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}