{"id":7380,"date":"2021-04-28T15:26:23","date_gmt":"2021-04-28T13:26:23","guid":{"rendered":"http:\/\/www.unimedia.tech.mialias.net\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/"},"modified":"2023-12-21T10:32:18","modified_gmt":"2023-12-21T09:32:18","slug":"echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws","status":"publish","type":"post","link":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/","title":{"rendered":"Echtzeit mit Serverless unter Verwendung von Websocket in AWS"},"content":{"rendered":"\n<p>Hallo zusammen, heute werden wir \u00fcber die Implementierung von Websocket mit Serverless diskutieren, indem wir Node.js mit Typescript f\u00fcr die Echtzeitkommunikation verwenden.<\/p>\n\n<p><\/p>\n\n<p>Wenn Sie versuchen, im Internet danach zu suchen, werden Sie zwar einige Details zur <a href=\"https:\/\/www.unimedia.tech\/dedicated-development-teams\/\">Umsetzung <\/a>finden, aber nichts Konkretes. Hier baue ich also ein vollwertiges Websocket-\u00d6kosystem mit allen Code- und Konfigurationsdetails auf.<\/p>\n\n<p><\/p>\n\n<p>F\u00fcr die <a href=\"https:\/\/www.unimedia.tech\/software-development\/\">Entwicklung<\/a> m\u00fcssen Sie <code><strong>serverless<\/strong><\/code>, <code><strong>npm<\/strong><\/code> und\/oder <code><strong>yarn<\/strong><\/code> global installiert haben. Lesen Sie, wie Sie <a href=\"https:\/\/classic.yarnpkg.com\/en\/docs\/install\/\">YARN<\/a> und <a href=\"https:\/\/docs.npmjs.com\/downloading-and-installing-node-js-and-npm\">NPM<\/a> <a href=\"https:\/\/classic.yarnpkg.com\/en\/docs\/install\/\">installieren<\/a>. Ich verwende auch lieber <a href=\"https:\/\/github.com\/nvm-sh\/nvm#installing-and-updating\">NVM<\/a>, um meine Node-Versionen zu verwalten.<\/p>\n\n<p><\/p>\n\n<h2 class=\"wp-block-heading\">Entwicklung<\/h2>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\" id=\"4fcc\">Schritt 1. AWS konfigurieren<\/h3>\n\n<p id=\"a6a6\">Richten Sie AWS CLI ein, falls Sie das noch nicht getan haben. Hier finden Sie einen sehr guten Artikel \u00fcber das <a href=\"https:\/\/sidneyb231.medium.com\/configure-aws-for-development-and-deployment-ad822097fc22\">Erstellen und Konfigurieren von AWS-Anmeldeinformationen<\/a>. Als n\u00e4chstes m\u00fcssen wir unsere Anwendung einrichten.<\/p>\n\n<p><\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\" id=\"b92d\">Schritt 2. Projekt einrichten<\/h3>\n\n<p id=\"7031\">Erstellen und Einrichten eines serverlosen Projekts mit Typescript:<\/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\">Dabei ist <code><strong>&lt;PROJECT-NAME&gt;<\/strong><\/code> der Name Ihres Projekts.<\/p>\n\n<p id=\"f6f1\">Dies erzeugt eine serverlose Boilerplate f\u00fcr unsere Anwendung. Als n\u00e4chstes m\u00fcssen wir zu unserem neuen Projekt <code>cd &lt;PROJECT-NAME&gt;<\/code>navigieren und unsere Abh\u00e4ngigkeiten installieren, indem wir <code><strong>yarn install<\/strong><\/code> ausf\u00fchren.<\/p>\n\n<p id=\"52c5\">Die wichtigsten Dateien, die wir im Laufe der Entwicklung unserer Anwendung aktualisieren werden, sind die <code><strong>handler.ts<\/strong><\/code> und <code><strong>serverless.ts<\/strong><\/code>. Die Datei <code><strong>handler.ts<\/strong><\/code> behandelt unsere Lambda-Funktionen oder Verweise auf unsere Lambda-Funktionen. Sie sollte folgenderma\u00dfen aussehen:<\/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>Die Datei serverless.ts bietet uns eine Vorlage zum Modellieren und Bereitstellen unserer Anwendungsressourcen f\u00fcr AWS CloudFormation, indem die Infrastruktur als Code behandelt wird. Auf diese Weise k\u00f6nnen wir Ressourcen leicht erstellen, aktualisieren und sogar l\u00f6schen. Sie sollte folgenderma\u00dfen aussehen:<\/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\">Au\u00dferdem m\u00fcssen Sie aufgrund von <a href=\"https:\/\/www.serverless.com\/framework\/docs\/deprecations\/\">Verwerfungen<\/a> die Eigenschaft service direkt auf service name setzen:<\/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\">Und aktualisieren Sie das Objekt <code><strong>provider.apiGateway<\/strong><\/code> wie folgt:<\/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\">Wir k\u00f6nnen unsere Funktion in unserem Projektstamm <code><strong>$ serverless invoke local --function hello<\/strong><\/code> ausf\u00fchren und Sie sollten die Antwort sehen:<\/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\">Das serverlose Framework ruft lokal die Funktion <code>hello<\/code> auf und f\u00fchrt die exportierte Methode <code><strong>hello<\/strong><\/code> in der Datei <code><strong>handler.ts<\/strong><\/code> aus. Mit dem Befehl <code>serverless invoke local<\/code> k\u00f6nnen wir unsere Lambda-Funktionen lokal ausf\u00fchren, bevor sie bereitgestellt werden.<\/p>\n\n<p id=\"7be0\">Als n\u00e4chstes m\u00fcssen wir ein paar Abh\u00e4ngigkeiten f\u00fcr unsere Anwendung installieren:<\/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\">\n  <strong>aws-sdk<\/strong>\n<\/a> &#8211; erm\u00f6glicht uns die Kommunikation mit AWS-Diensten<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/uuid\"><strong>uuid<\/strong> <\/a>&#8211; erzeugt eindeutige IDs f\u00fcr unsere Datenbankeintr\u00e4ge<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/serverless-offline\">\n  <strong>serverlos-offline<\/strong>\n<\/a>&#8211; dieses Plugin erm\u00f6glicht es uns, unsere Anwendung und Lambda-Funktionen lokal auszuf\u00fchren<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/serverless-dotenv-plugin\">\n  <strong>serverless-dotenv-plugin<\/strong>\n<\/a> &#8211; damit wir <code><strong>.env<\/strong><\/code> Variablen in unsere Lambda-Umgebung laden k\u00f6nnen<\/li><li><a href=\"https:\/\/www.npmjs.com\/package\/serverless-bundle\"><strong>serverless-bundle<\/strong> <\/a>&#8211; verpackt dieses Plugin unsere Typescript-Funktionen optimal und sorgt daf\u00fcr, dass wir uns nicht um die Installation von <a href=\"https:\/\/babeljs.io\/\">\n  <strong>Babel<\/strong>\n<\/a>, <a href=\"https:\/\/www.typescriptlang.org\/\">\n  <strong>Typescript<\/strong>\n<\/a>, <a href=\"https:\/\/webpack.js.org\/\">\n  <strong>Webpack<\/strong>\n<\/a>, <a href=\"https:\/\/eslint.org\/\">\n  <strong>ESLint<\/strong>\n<\/a> und eine Vielzahl anderer Pakete. Das bedeutet, dass wir unsere eigenen Webpack-Konfigurationen nicht pflegen m\u00fcssen. Wir k\u00f6nnen also fortfahren und die Datei <code><strong>webpack.config.js<\/strong><\/code> und den Verweis auf sie in der benutzerdefinierten Eigenschaft <code>serverless.ts<\/code> l\u00f6schen:<\/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\">Schritt 3. Ressourcen einrichten<\/h3>\n\n<p>Jetzt m\u00fcssen wir die Variablen <code><strong>region<\/strong><\/code>, <code><strong>stage<\/strong><\/code>, <code><strong>table_throughputs<\/strong><\/code>, <code><strong>table_throughput<\/strong><\/code> und <strong>connections_table <\/strong>setzen. Sie konfiguriert auch <code><strong>dynamodb<\/strong><\/code> und <code><strong>serverless-offline<\/strong><\/code> f\u00fcr die lokale Entwicklung.<\/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\">Als n\u00e4chstes m\u00fcssen wir die Eigenschaft <code><strong>provider.environment<\/strong><\/code> in der Datei<code><strong>serverless.ts<\/strong><\/code> wie folgt aktualisieren:<\/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>Als N\u00e4chstes m\u00fcssen wir <code><strong>resources<\/strong><\/code> hinzuf\u00fcgen, das bei der Bereitstellung unserer Anwendung zu Ihrem CloudFormation-Stack hinzugef\u00fcgt wird. Wir m\u00fcssen AWS-Ressourcen in einer Eigenschaft mit dem Titel <code><strong>resources<\/strong><\/code> definieren.  <\/p>\n\n<p>Sie k\u00f6nnen mehr \u00fcber die <a href=\"https:\/\/docs.aws.amazon.com\/amazondynamodb\/latest\/developerguide\/WorkingWithTables.html\">Arbeit mit Tabellen und Daten in DynamoDB<\/a> lesen.<\/p>\n\n<p id=\"b50e\">Mit der Kraft von Javascript k\u00f6nnen wir gro\u00dfe Dienstdefinitionsdateien vermeiden, indem wir sie in Dateien aufteilen und dynamische Importe verwenden. Das ist wichtig, denn wenn unsere Anwendung w\u00e4chst, ist sie durch getrennte Definitionsdateien leichter zu pflegen.<\/p>\n\n<p id=\"e53c\">F\u00fcr dieses Projekt werden wir unsere Ressourcen separat organisieren und in <code><strong>serverless.ts<\/strong><\/code> importieren. Dazu m\u00fcssen wir zun\u00e4chst ein Verzeichnis <code><strong>resources<\/strong><\/code> in unserem Stammverzeichnis anlegen und dann eine Datei <code><strong>dynamodb-tables.ts<\/strong><\/code> f\u00fcr unsere DynamoDB-Tabellen erstellen:<\/p>\n\n<pre class=\"wp-block-code\"><code>\/\/ At project root\n$ touch resources\/dynamodb-tables.ts <\/code><\/pre>\n\n<p>Als n\u00e4chstes aktualisieren wir die Datei <code><strong>dynamodb-tables.ts<\/strong><\/code> wie folgt:<\/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>Und importieren Sie in <code><strong>serverless.ts<\/strong><\/code> &#8211; und stellen Sie es in den Ressourceneigenschaften wie unten angegeben ein,<\/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\">Um DynamoDB lokal auszuf\u00fchren, m\u00fcssen wir zun\u00e4chst das Plugin <code><strong>serverless-dynamodb-local<\/strong><\/code> installieren:<\/p>\n\n<pre class=\"wp-block-code\"><code>$ yarn add -D serverless-dynamodb-local <\/code><\/pre>\n\n<p id=\"1d82\">Als n\u00e4chstes m\u00fcssen wir das Array <code><strong>serverless.ts<\/strong><\/code> plugins aktualisieren:<\/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\">Um das Plugin zu verwenden, m\u00fcssen wir DynamoDB Local installieren, indem wir <code><strong>sls dynamodb install<\/strong><\/code> im Stammverzeichnis des Projekts ausf\u00fchren. Wenn Sie<code><strong>sls dynamodb start<\/strong><\/code> ausf\u00fchren, wird es lokal gestartet:<\/p>\n\n<p><\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Schritt 4. Websocket-Integration<\/h3>\n\n<p>In diesem Schritt werden wir einen Websocket-Handler erstellen, um eine Verbindung mit Websocket-Ereignissen herzustellen.<\/p>\n\n<p>Sie k\u00f6nnen hier Details \u00fcber <a href=\"https:\/\/www.serverless.com\/framework\/docs\/providers\/aws\/events\/websocket\/\" target=\"_blank\" rel=\"noreferrer noopener\">Websocket-Ereignisse<\/a> nachlesen.<\/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><strong>handler.ts-Datei<\/strong> wird wie unten angegeben aussehen:<\/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>Das API-Gateway bietet <a href=\"https:\/\/docs.aws.amazon.com\/apigateway\/latest\/developerguide\/apigateway-websocket-api-overview.html\">4 Arten von Routen<\/a>, die sich auf den Lebenszyklus eines WS-Clients beziehen:<\/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\">Veranstaltung<\/th><th>Aktion<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$verbinden<\/strong><\/td><td>Aufruf beim Verbinden eines ws-Clients<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$Trennung der Verbindung<\/strong><\/td><td>wird beim Trennen eines WS-Clients aufgerufen (kann in manchen Situationen nicht aufgerufen werden)<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$Standard<\/strong><\/td><td>aufgerufen, wenn es keinen Handler f\u00fcr das Ereignis gibt<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><custom-route><\/custom-route><\/td><td>wird aufgerufen, wenn der Routenname f\u00fcr einen Handler angegeben ist<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<p>Auf dieser Grundlage haben wir einen Switch-Case erstellt, bei dem jeder Fall eine entsprechende Aktion in Bezug auf dieses Ereignis ausf\u00fchrt.<\/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\">Veranstaltung<\/th><th>Aktion<\/th><\/tr><\/thead><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$verbinden<\/strong><\/td><td>erstellt einen Eintrag in der <em>Verbindungstabelle<\/em> mit connectionId und ttl.<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$Trennung der Verbindung<\/strong><\/td><td>wird die Verbindung aus der<em> dynamodb-Verbindungstabelle<\/em> entfernt.<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>$Standard<\/strong><\/td><td>sendet die Daten an alle Verbindungen.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<p><strong>schema.ts<\/strong> wird f\u00fcr die Validierung des Schemas zust\u00e4ndig sein.<\/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> wird die Websocket-Ereignisse mit Handlern abbilden:<\/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>Wir m\u00fcssen registrieren <strong>\n  <em>wsHandler <\/em>\n<\/strong>exportieren in<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> enth\u00e4lt 2 wichtige Funktionen (siehe unten)<\/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>Funktion<\/th><th>Erl\u00e4uterung<\/th><\/tr><\/thead><tbody><tr><td>sendMessage<\/td><td>akzeptiert <strong>\n  <em>connectionId <\/em>\n<\/strong>&amp; <em>\n  <strong>K\u00f6rper <\/strong>\n<\/em>als Parameter. Es liest APIG_ENDPOINT aus der Umgebungsvariablen und erstellt eine Instanz von <strong>ApiGatewayManagementApi<\/strong> und sendet dann den Body mit <strong>postToConnection<\/strong> an die angegebene <em>connectionId <\/em>.<\/td><\/tr><tr><td>getAllConnections<\/td><td>gibt alle Verbindungen zur\u00fcck<\/td><\/tr><\/tbody><\/table><\/figure>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Schritt 4. Lokale Websocket-Ausf\u00fchrung<\/h3>\n\n<p>In diesem Schritt werden wir Websocket lokal ausf\u00fchren, indem wir Serverless Offline verwenden:<\/p>\n\n<p>Bevor wir beginnen, vergleichen und best\u00e4tigen Sie die Datei serverless.ts wie unten angegeben<\/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\">Schritt 5: Websocket-Demo<\/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>Ich hoffe, Sie finden diesen Beitrag n\u00fctzlich!<\/p>\n\n<p><\/p>\n\n<h3 class=\"wp-block-heading\">Unimedia Technology<\/h3>\n\n<p>Hier bei <a href=\"https:\/\/www.unimedia.tech\/\">Unimedia Technology<\/a> haben wir ein Team von<strong> BackEnd-Entwicklern<\/strong>, das Ihnen bei der Entwicklung Ihrer komplexesten Anwendungen helfen kann<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hallo zusammen, heute werden wir \u00fcber die Implementierung von Websocket mit Serverless diskutieren, indem wir Node.js mit Typescript f\u00fcr die Echtzeitkommunikation verwenden. Wenn Sie versuchen, im Internet danach zu suchen, werden Sie zwar einige Details zur Umsetzung finden, aber nichts Konkretes. Hier baue ich also ein vollwertiges Websocket-\u00d6kosystem mit allen Code- und Konfigurationsdetails auf. F\u00fcr [&hellip;]<\/p>\n","protected":false},"author":6,"featured_media":6625,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[227,219],"tags":[],"class_list":["post-7380","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-serverlos","category-technical-guides-de"],"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>Echtzeit mit Serverless unter Verwendung von Websocket in 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\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Echtzeit mit Serverless unter Verwendung von Websocket in AWS\" \/>\n<meta property=\"og:description\" content=\"Hallo zusammen, heute werden wir \u00fcber die Implementierung von Websocket mit Serverless diskutieren, indem wir Node.js mit Typescript f\u00fcr die Echtzeitkommunikation verwenden. Wenn Sie versuchen, im Internet danach zu suchen, werden Sie zwar einige Details zur Umsetzung finden, aber nichts Konkretes. Hier baue ich also ein vollwertiges Websocket-\u00d6kosystem mit allen Code- und Konfigurationsdetails auf. F\u00fcr [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-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=\"2023-12-21T09:32:18+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=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Unimedia\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"11\u00a0Minuten\" \/>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Echtzeit mit Serverless unter Verwendung von Websocket in 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\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/","og_locale":"de_DE","og_type":"article","og_title":"Echtzeit mit Serverless unter Verwendung von Websocket in AWS","og_description":"Hallo zusammen, heute werden wir \u00fcber die Implementierung von Websocket mit Serverless diskutieren, indem wir Node.js mit Typescript f\u00fcr die Echtzeitkommunikation verwenden. Wenn Sie versuchen, im Internet danach zu suchen, werden Sie zwar einige Details zur Umsetzung finden, aber nichts Konkretes. Hier baue ich also ein vollwertiges Websocket-\u00d6kosystem mit allen Code- und Konfigurationsdetails auf. F\u00fcr [&hellip;]","og_url":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-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":"2023-12-21T09:32:18+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":{"Verfasst von":"Unimedia","Gesch\u00e4tzte Lesezeit":"11\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/#article","isPartOf":{"@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/"},"author":{"name":"Unimedia","@id":"https:\/\/www.unimedia.tech\/de\/#\/schema\/person\/3a250aa22526d5c9ff6bc95bb380a5dd"},"headline":"Echtzeit mit Serverless unter Verwendung von Websocket in AWS","datePublished":"2021-04-28T13:26:23+00:00","dateModified":"2023-12-21T09:32:18+00:00","mainEntityOfPage":{"@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/"},"wordCount":964,"commentCount":0,"publisher":{"@id":"https:\/\/www.unimedia.tech\/de\/#organization"},"image":{"@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/#primaryimage"},"thumbnailUrl":"https:\/\/www.unimedia.tech\/wp-content\/uploads\/2023\/12\/websocket-serverless-ts-4.png","articleSection":["Serverlos","Technical Guides"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/","url":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/","name":"Echtzeit mit Serverless unter Verwendung von Websocket in AWS - Unimedia Technology","isPartOf":{"@id":"https:\/\/www.unimedia.tech\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/#primaryimage"},"image":{"@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-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":"2023-12-21T09:32:18+00:00","breadcrumb":{"@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.unimedia.tech\/de\/echtzeit-mit-serverless-unter-verwendung-von-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\/de\/echtzeit-mit-serverless-unter-verwendung-von-websocket-in-aws\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.unimedia.tech\/de\/"},{"@type":"ListItem","position":2,"name":"Echtzeit mit Serverless unter Verwendung von Websocket in AWS"}]},{"@type":"WebSite","@id":"https:\/\/www.unimedia.tech\/de\/#website","url":"https:\/\/www.unimedia.tech\/de\/","name":"Unimedia Technology","description":"Your software development partner","publisher":{"@id":"https:\/\/www.unimedia.tech\/de\/#organization"},"alternateName":"Unimedia Tech","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.unimedia.tech\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/www.unimedia.tech\/de\/#organization","name":"Unimedia Technology","alternateName":"Unimedia Tech","url":"https:\/\/www.unimedia.tech\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.unimedia.tech\/de\/#\/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\/de\/#\/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\/de\/#\/schema\/person\/3a250aa22526d5c9ff6bc95bb380a5dd","name":"Unimedia","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.unimedia.tech\/de\/#\/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\/de\/wp-json\/wp\/v2\/posts\/7380","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/users\/6"}],"replies":[{"embeddable":true,"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/comments?post=7380"}],"version-history":[{"count":0,"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/posts\/7380\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/media\/6625"}],"wp:attachment":[{"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/media?parent=7380"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/categories?post=7380"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.unimedia.tech\/de\/wp-json\/wp\/v2\/tags?post=7380"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}