{"id":26422,"date":"2024-09-03T19:46:42","date_gmt":"2024-09-03T17:46:42","guid":{"rendered":"https:\/\/blog.mi.hdm-stuttgart.de\/?p=26422"},"modified":"2024-09-03T19:46:45","modified_gmt":"2024-09-03T17:46:45","slug":"how-to-not-get-rich-using-tiktok-and-aws","status":"publish","type":"post","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/","title":{"rendered":"How to (not) get rich using TikTok and AWS"},"content":{"rendered":"\n<p>In our Software Development for Cloud Computing course, we were tasked with building something, anything, using the cloud. After various discarded ideas, our team decided to create a platform aimed at automatically generating short-form videos. In this blog post, we will go over our development journey, the architectural decisions we made, the technologies we chose (and why), the challenges we encountered and the (many) lessons we learned.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p>Short-form video content has enjoyed a massive rise in popularity in recent years, with platforms like YouTube and Instagram, inspired by TikTok, building their own versions of an algorithmically generated feed of short looping videos.<\/p>\n\n\n\n<p>As avid consumers of short-form videos ourselves, we chose to focus on this topic for our project. We found a common pattern amongst the videos we watched, where a text-to-speech (TTS) voice would read a reddit story on top of some kind of background video.<\/p>\n\n\n\n<p>What if we could automatically generate these types of videos, upload them to various platforms using their APIs, and use the creator monetization of the platforms to get rich? All we would need to do is find stories, narrate them using TTS, and generate an accompanying video. Our plan was foolproof!<\/p>\n\n\n\n<p>We didn\u2019t quite end up fully realizing our vision (or we\u2019d be sitting on a yacht instead of writing this blog post), but here\u2019s what we came up with:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large wp-duotone-unset-1\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"492\" data-attachment-id=\"26534\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-54\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2.png\" data-orig-size=\"1600,768\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Video Generation Flow\" data-image-description=\"&lt;p&gt;Video Generation Flow&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Video Generation Flow&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2-1024x492.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2-1024x492.png\" alt=\"\" class=\"wp-image-26534\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2-1024x492.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2-300x144.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2-768x369.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2-1536x737.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-2.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">You\u2019ll understand all of this shortly.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">How does it work?<\/h2>\n\n\n\n<p>Let\u2019s walk through each of the steps a user needs to make in order to generate a finished video.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Getting a story<\/h3>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"614\" data-attachment-id=\"26515\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/localhost_3000_backgrounds-6\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6.png\" data-orig-size=\"2400,1438\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Creator Choose Story Step\" data-image-description=\"&lt;p&gt;Creator Choose Story Step&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Creator Choose Story Step&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6-1024x614.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6-1024x614.png\" alt=\"\" class=\"wp-image-26515\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6-1024x614.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6-300x180.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6-768x460.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6-1536x920.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/localhost_3000_backgrounds-6-2048x1227.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Our initial goal was to automatically scrape stories from Reddit and use them in our videos. Unfortunately, Reddit has been <a href=\"https:\/\/www.theverge.com\/2024\/6\/25\/24185984\/reddit-robots-txt-fight-ai-bots-scraping-crawlers\" target=\"_blank\" rel=\"noopener\" title=\"\">fighting against this type of use<\/a> and their API is increasingly difficult for developers to use. Instead, we decided to automatically generate stories using OpenAI\u2019s ChatGPT 4o model.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>After experimentation with various prompts, we started getting good results with this one:<\/p>\n\n\n\n<p>You&#8217;re a bot for the project TikTok Money Glitch.<\/p>\n\n\n\n<p>Your goal is to generate text for a TikTok video, given a prompt.<\/p>\n\n\n\n<p>The videos are in the style of reddit posts.<\/p>\n\n\n\n<p>Your goal is to generate content that is in some way interesting and would fit for a TikTok audience.<\/p>\n\n\n\n<p>You can either generate a Reddit post&#8217;s body, or one or multiple replies to a Reddit post.<\/p>\n\n\n\n<p>If you are generating a post, only generate the body of the post. Don&#8217;t include the title anywhere and don&#8217;t use Markdown.<\/p>\n\n\n\n<p>If you are generating replies, include the title without any formatting. Then, generate the body of the replies without any formatting.<\/p>\n\n\n\n<p>If your prompt is invalid, reply with I cannot generate a post from this prompt<\/p>\n<\/blockquote>\n\n\n\n<p>Users tell the AI what kind of story they would like, at which point the story input will get automatically filled with a story that the user can edit if they would like to make any adjustments. We\u2019re using Next.js\u2019 streaming feature to load the generated text into user\u2019s browsers as it&#8217;s being generated, similar to the UI for tools like ChatGPT.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Background Video<\/h3>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"693\" data-attachment-id=\"26535\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-55\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3.png\" data-orig-size=\"1600,1083\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Creator Choose Background Video Step\" data-image-description=\"&lt;p&gt;Creator Choose Background Video Step&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Creator Choose Background Video Step&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3-1024x693.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3-1024x693.png\" alt=\"\" class=\"wp-image-26535\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3-1024x693.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3-300x203.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3-768x520.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3-1536x1040.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-3.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>The videos we aim to generate are characterized by video game footage in the background, typically featuring Minecraft Parkour or Subway Surfers. Users can upload this type of footage through a dedicated <code class=\"\" data-line=\"\">backgrounds<\/code> route, where they can manage their library of background videos &#8211; uploading new footage or deleting old clips as needed.<\/p>\n\n\n\n<p>Once users have added their desired videos to their library, they can access these videos during the video creation process. Here, they can select the background video they want to pair with their story.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Preview<\/h3>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-style-default\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1006\" data-attachment-id=\"26536\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-56\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4.png\" data-orig-size=\"1600,1572\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Creator Preview Step\" data-image-description=\"&lt;p&gt;Creator Preview Step&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Creator Preview Step&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4-1024x1006.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4-1024x1006.png\" alt=\"\" class=\"wp-image-26536\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4-1024x1006.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4-300x295.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4-768x755.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4-1536x1509.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-4.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>After a story and background video are provided, a preview is generated following a brief loading time. Behind the scenes, a Next.js API route handler on <code class=\"\" data-line=\"\">\/api\/lambda\/polly<\/code>&nbsp; is triggered. This handler invokes two AWS Lambda functions: one for generating the audio and another for creating the speech marks from the provided story.<\/p>\n\n\n\n<p>The Polly responses are returned as Audio Stream objects. The audio stream is directly stored as a blob in an S3 bucket, while the speech marks stream is parsed into a JSON object array. Each object in the array represents one word with metadata, including start and end times, necessary for displaying subtitles.<\/p>\n\n\n\n<p>At this point all necessary parts have been collected: audio, speech marks, text, and background video. We use Remotion, a tool for programmatically generating video, to turn all these parts into a single video. Remotion has a feature called Remotion Player, which we use to preview the video to our users before continuing to the next step. At this point the finished video can already be viewed, although it\u2019s not actually a video yet. Instead, it\u2019s a collection of HTML-elements just being animated to look like a video.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Rendering<\/h3>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"573\" data-attachment-id=\"26524\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-53\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1.png\" data-orig-size=\"1600,895\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Creator Publish Step rendering\" data-image-description=\"&lt;p&gt;Creator Publish Step rendering&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Creator Publish Step rendering&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1-1024x573.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1-1024x573.png\" alt=\"\" class=\"wp-image-26524\" style=\"width:1000px;height:auto\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1-1024x573.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1-300x168.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1-768x430.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1-1536x859.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-1.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>If the user is happy with the preview, it can be turned into an actual encoded video. Remotion supports distributing these rendering workloads on AWS lambda using <code class=\"\" data-line=\"\">@remotion\/lambda<\/code>. Each lambda gets a configurable amount of frames to render (in our case 10), and after all the tasks have completed, the frames get merged together into one video, which is uploaded to S3.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Publishing<\/h3>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"573\" data-attachment-id=\"26537\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-57\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5.png\" data-orig-size=\"1202,673\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Creator Publish Step\" data-image-description=\"&lt;p&gt;Creator Publish Step&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Creator Publish Step&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5-1024x573.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5-1024x573.png\" alt=\"\" class=\"wp-image-26537\" style=\"width:1000px;height:auto\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5-1024x573.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5-300x168.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5-768x430.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-5.png 1202w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>Finally, in the publishing step, users have the choice of either simply downloading the video directly from the S3 bucket with a pre-signed URL, or automatically uploading it to YouTube. Here, we use a Next.js server action that lives entirely on the server side, communicates with Clerk, gets the user\u2019s access token and uploads the video directly to the signed in Google account as a YouTube short via a call to the YouTube API. This is all based on the use of an extended OAuth provider in Clerk, which initiates the necessary Scopes to upload videos.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"701\" data-attachment-id=\"26538\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-58\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6.png\" data-orig-size=\"1600,1096\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Authentication Flow\" data-image-description=\"&lt;p&gt;Authentication Flow&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Authentication Flow&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6-1024x701.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6-1024x701.png\" alt=\"\" class=\"wp-image-26538\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6-1024x701.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6-300x206.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6-768x526.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6-1536x1052.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-6.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">An overview of our authentication flows<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Technology Stack<\/h2>\n\n\n\n<p>We\u2019d like to spend some time explaining how we made our tech stack choices. We considered several factors while making these decisions like our personal experience, popularity, and fit for the project.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Rendering Videos: Remotion (and its consequences)<\/h3>\n\n\n\n<p>Generating videos was the most central part of our project, and we decided to go with Remotion for it. Remotion can generate videos using templates defined in code (more specifically React). As there is not a lot of competition in this field and Remotion is free to use at our scale, it was an obvious choice.&nbsp;<\/p>\n\n\n\n<p>Because Remotion has direct support for rendering videos on AWS Lambda concurrently (which is faster and more efficient than using a VPS), this locked us in to using AWS as well.<\/p>\n\n\n\n<p>And due to Remotion being React-based, it was an obvious choice to use a React-based framework for the rest of our frontend as well.<\/p>\n\n\n\n<p>This tight integration between Remotion and the rest of our stack made development far quicker and easier, but also came with some disadvantages. If we hadn\u2019t chosen Remotion, we could\u2019ve tried out a framework based on a different library (like Vue or Svelte), but as this would\u2019ve led to us separating our video rendering and application code, we decided to stick with React. We probably would have also decided to use AWS as our cloud provider without Remotion, but we did not spend much time evaluating the alternatives due to Remotion only supporting AWS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Framework Choice: Next.js Ecosystem<\/h3>\n\n\n\n<p>The two most popular React-based frameworks are Next.js and Remix. They have a similar feature-set with both of them supporting server-side rendering, file-based routing and having integrations for SST.<\/p>\n\n\n\n<p>We ended up choosing Next.js ecosystem primarily because our team was already familiar with React from previous lectures and projects. This allowed us to use our existing knowledge, making our development process faster, with the benefit of letting us focus more on the actual cloud infrastructure and less on building the web app. Remotion also provides templates for integrating with Next.js which made starting out far quicker.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Authentication: AWS Cognito vs NextAuth vs Clerk&nbsp;<\/h3>\n\n\n\n<p>As we wanted to use AWS for everything, our initial thought was to use AWS Cognito for authentication. In initial testing, we struggled with this though, and it quickly became clear that this would require significant effort on our part to get working. As authentication is not a central part of our app, we wanted to minimize the time spent working on it.<\/p>\n\n\n\n<p>Instead, we considered using NextAuth, as we had also used this in previous projects. In those projects we had a fair share of issues with it though, and had actually considered switching those projects to use Clerk instead. As we were starting from scratch, we decided this was the way to go.<\/p>\n\n\n\n<p>Clerk is an auth-as-a-service platform that has specific integrations for Next.js which made it extremely simple to set up for our use case. Later on, we needed our users to grant us access to their YouTube accounts. Clerk allowed us to integrate this into their authentication flow by changing a single setting in their admin portal, which reduced the time we had to work on the YouTube integration significantly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Database: DynamoDB vs AWS RDS with Drizzle ORM<\/h3>\n\n\n\n<p>The SST documentation has a <a href=\"https:\/\/sst.dev\/docs\/start\/aws\/drizzle\/\" target=\"_blank\" rel=\"noopener\" title=\"\">tutorial<\/a> for integrating AWS RDS with SST while using the Drizzle ORM to talk to the database. As we wanted to try out Drizzle, we tested this as an option. Despite following the tutorial, we never actually got the integration between the database and Drizzle to work. As discussed later on, we also ran into some very significant cost issues though, due to the very high price of AWS RDS.<\/p>\n\n\n\n<p>As we still needed a database and didn\u2019t want to have a repeat of the cost issue, we chose DynamoDB, primarily due to its simplicity and low cost. We quickly got set up with it and had no further issues, so we stayed with it for the rest of the project.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">E2E-Testing Framework: Cypress vs Playwright<\/h3>\n\n\n\n<p>For E2E-Testing frameworks there are two popular options that integrate well with our stack: Cypress and Playwright<\/p>\n\n\n\n<p>Clerk\u2019s Cypress integration has several helper methods which can authenticate the test client by calling a method with provided credentials. Unfortunately, Playwright lacks such an integration.<\/p>\n\n\n\n<p>However, as a group, we had no experience using Cypress, and Playwright had also been described as a more modern alternative to it. We decided to go ahead with Playwright\u2019s weaker integration, which forced us to manually write the authentication part of our tests. We ended up finding <a href=\"https:\/\/github.com\/clerk\/clerk-playwright-nextjs\" target=\"_blank\" rel=\"noopener\" title=\"\">an example<\/a> from Clerk that helped with this though and had no further struggles getting our E2E tests to work together with Clerk.<\/p>\n\n\n\n<p>We tried setting up our continuous integration to run our E2E-tests automatically, but this turned out to be too challenging due to the required infrastructure needed in the background for our app to work. Instead, we chose to run our E2E-tests manually to validate that our app still worked as intended when refactoring parts behind the scenes.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Storage: AWS S3 for Video Storage<\/h3>\n\n\n\n<p>We needed a way to store files for various parts of the app, and as we had been using AWS for most parts of the app, S3 was the obvious choice.<\/p>\n\n\n\n<p>More specifically, we used S3 for three different parts of our app:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>We needed to store audio generated from Polly so that it could be accessed in the browser. An automatic expiration date for speech audio is set to avoid cluttering storage, because speech audio is generated during video creation and does not need to be accessed at a later point.<\/li>\n\n\n\n<li>Users are able to upload background videos which can be used for the generated videos. In addition to uploading the videos to S3, we also store metadata for those files in DynamoDB, with the filename (a randomly generated UUID) linking the file and metadata. This enhances user experience by sorting the way data is viewed in the web app and making files searchable.<\/li>\n\n\n\n<li>The generated videos are uploaded to an S3 bucket by remotion, at which point they can be downloaded by the user or uploaded directly to YouTube.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">How to get our stuff into the Cloud?<\/h2>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-7.png\"><img loading=\"lazy\" decoding=\"async\" width=\"740\" height=\"236\" data-attachment-id=\"26539\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-59\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-7.png\" data-orig-size=\"740,236\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"The Cloud Comic\" data-image-description=\"&lt;p&gt;The Cloud Comic&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;The Cloud Comic&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-7.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-7.png\" alt=\"\" class=\"wp-image-26539\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-7.png 740w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-7-300x96.png 300w\" sizes=\"auto, (max-width: 740px) 100vw, 740px\" \/><\/a><figcaption class=\"wp-element-caption\">Our code is here (<a href=\"https:\/\/xkcd.com\/908\/\" title=\"\">xkcd 908<\/a>, licensed under <a href=\"https:\/\/creativecommons.org\/licenses\/by-nc\/2.5\/\">CC BY-NC 2.5<\/a>)<\/figcaption><\/figure>\n\n\n\n<p>Deploying our app to the cloud manually would be quite complicated and unpleasant. Ideally, we should use an Infrastructure-as-Code (IaC) tool like Terraform or Pulumi to automate the process and make it repeatable. Since we\u2019re using Next.js, it would be ideal to deploy it in a serverless way, as this is cheaper and can be faster.<\/p>\n\n\n\n<p>Vercel, the creators of Next.js, offer a service which hosts Next.js apps using AWS Lambda behind the scenes. We had good experiences with using them in the past, but as Vercel does not work properly with IaC and is designed to work with no additional configuration, using it would\u2019ve gone against the spirit of learning about the cloud in this lecture.<\/p>\n\n\n\n<p>AWS offers a service called Amplify which is similar to Vercel, so we decided to test it out with our app. We ran into some compatibility issues though, and our goal was still to deploy \u201ccloser to the metal\u201d than with a service like Vercel. As Amplify is a very close copy of Vercel, this wouldn\u2019t work either.<\/p>\n\n\n\n<p>Open Next is an open-source project that turns Next.js\u2019 build output into a format that can be deployed on AWS Lambda. This sounded like it would work great for our use case, so we started exploring its documentation. Their <em>Get Started<\/em> section mentions that \u201cThe easiest way to deploy OpenNext to AWS is with SST\u201d so we looked into that.<\/p>\n\n\n\n<p>We were immediately surprised by the simplicity of the example on their landing page.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"682\" data-attachment-id=\"26540\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-60\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8.png\" data-orig-size=\"1600,1066\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"SST Landing page with example\" data-image-description=\"&lt;p&gt;SST Landing page with example&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;SST Landing page with example&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8-1024x682.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8-1024x682.png\" alt=\"\" class=\"wp-image-26540\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8-1024x682.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8-300x200.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8-768x512.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8-1536x1023.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-8.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">I\u2019m sold<\/figcaption><\/figure>\n\n\n\n<p>After some testing with SST, we quickly made the decision to use it for this project. Let\u2019s take a look at how it works, why we made the decision to use it, and what our experience with it was like during the project.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Infrastructure Management with SST<\/h2>\n\n\n\n<p>SST is an IaC tool, similar to Terraform, Pulumi, and CloudFormation. Infrastructure can be provisioned by creating various objects in TypeScript (called <em>components<\/em> by SST). Unlike those tools though, the components in SST abstract the lower-level infrastructure primitives to a level that is much nicer to work with.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why not Pulumi\/Terraform<\/h3>\n\n\n\n<p>Pulumi and Terraform provide many different low-level primitives for infrastructure. Unfortunately, these are often too low-level to comfortably work with.<\/p>\n\n\n\n<p>As an example, this is the amount of code required to create an AWS Lambda function using Pulumi:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\" data-line=\"\">import * as pulumi from &quot;@pulumi\/pulumi&quot;;\nimport * as archive from &quot;@pulumi\/archive&quot;;\nimport * as aws from &quot;@pulumi\/aws&quot;;\n\nconst assumeRole = aws.iam.getPolicyDocument({\n    statements: [{\n        effect: &quot;Allow&quot;,\n        principals: [{\n            type: &quot;Service&quot;,\n            identifiers: [&quot;lambda.amazonaws.com&quot;],\n        }],\n        actions: [&quot;sts:AssumeRole&quot;],\n    }],\n});\nconst iamForLambda = new aws.iam.Role(&quot;iam_for_lambda&quot;, {\n    name: &quot;iam_for_lambda&quot;,\n    assumeRolePolicy: assumeRole.then(assumeRole =&gt; assumeRole.json),\n});\nconst lambda = archive.getFile({\n    type: &quot;zip&quot;,\n    sourceFile: &quot;lambda.js&quot;,\n    outputPath: &quot;lambda_function_payload.zip&quot;,\n});\nconst testLambda = new aws.lambda.Function(&quot;test_lambda&quot;, {\n    code: new pulumi.asset.FileArchive(&quot;lambda_function_payload.zip&quot;),\n    name: &quot;lambda_function_name&quot;,\n    role: iamForLambda.arn,\n    handler: &quot;index.test&quot;,\n    sourceCodeHash: lambda.then(lambda =&gt; lambda.outputBase64sha256),\n    runtime: aws.lambda.Runtime.NodeJS18dX,\n    environment: {\n        variables: {\n            foo: &quot;bar&quot;,\n        },\n    },\n});<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-center\">That\u2019s a lot<\/p>\n\n\n\n<p>The tasks of setting IAM Permissions, creating an archive with the code, and setting it up to be run all have to be done in separate steps. And this example doesn\u2019t even contain potentially having to compile code from TypeScript to Javascript.<\/p>\n\n\n\n<p>Once infrastructure has been provisioned using a regular IaC tool, actually interacting with it from code is not a great experience either. Infrastructure also can\u2019t be easily duplicated, in case multiple developers want to work on a project simultaneously. And using these primitives to deploy something like a Next.js app is difficult, even with <a href=\"https:\/\/github.com\/RJPearson94\/terraform-aws-open-next\" target=\"_blank\" rel=\"noopener\" title=\"\">a module that integrates with OpenNext<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What makes SST so nice?<\/h3>\n\n\n\n<p>SST is different. Deploying a Next.js app is as simple as using the sst.aws.Nextjs component. That infrastructure is deployed separately for every developer working with the app, and it can access other infrastructure that is linked to it automatically. Behind the scenes, SST is actually based on Pulumi which means that it\u2019s still possible to use the low-level primitives it provides, if necessary.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">SST Components<\/h4>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"822\" height=\"1024\" data-attachment-id=\"26600\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/aws-components-3\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2.png\" data-orig-size=\"978,1218\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"SST Components\" data-image-description=\"&lt;p&gt;SST Components&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;SST Components&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2-822x1024.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2-822x1024.png\" alt=\"\" class=\"wp-image-26600\" style=\"width:450px\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2-822x1024.png 822w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2-241x300.png 241w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2-768x956.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/09\/AWS-Components-2.png 978w\" sizes=\"auto, (max-width: 822px) 100vw, 822px\" \/><\/a><\/figure>\n\n\n\n<p>SST has built-in support for components from various cloud providers, but we only used the AWS-specific ones. Specifically, we used <code class=\"\" data-line=\"\">Bucket<\/code> (creates an S3 Bucket), <code class=\"\" data-line=\"\">Dynamo<\/code> (sets up a DynamoDB behind the scenes), <code class=\"\" data-line=\"\">Function<\/code> (creates a serverless AWS Lambda function), <code class=\"\" data-line=\"\">Secret<\/code> (synchronizes secrets across the app), and of course <code class=\"\" data-line=\"\">Nextjs<\/code> (uses Open Next to deploy a Next.js app on AWS Lambda). As mentioned, these components require much less boilerplate. For example, let\u2019s try to create an AWS Lambda function like the previous example, but using SST:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\" data-line=\"\">new sst.aws.Function(&quot;MyFunction&quot;, {\n  handler: &quot;src\/lambda.handler&quot;\n});<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-center\">That\u2019s a lot less, eh?<\/p>\n\n\n\n<p>All the previous boilerplate necessary to set the correct permissions, compile TypeScript to JavaScript zip the result, and upload it is automatically handled behind the scenes. Because of the unique name generated for each component deployment, it\u2019s not possible for two developers to have conflicting deployments either.<\/p>\n\n\n\n<p>The community can also make their own components, which was very helpful in the case of <a href=\"https:\/\/github.com\/karelnagel\/remotion-sst\" target=\"_blank\" rel=\"noopener\" title=\"\">Remotion SST<\/a>. The Remotion documentation only explains how to manually deploy and update the AWS Lambda function responsible for rendering out videos. This can cause issues when Remotion versions are out of sync between the Lambda and App, and is also just annoying to do manually. Remotion SST can be used as a component like any other component and handles the deployment and updating of the required Lambda automatically.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">SST Linking<\/h4>\n\n\n\n<p>When two different components are intended to work together, like a Next.js app accessing an S3 bucket, they can be linked together.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\" data-line=\"\">const bucket = new sst.aws.Bucket(&quot;MyBucket&quot;);\n\nnew sst.aws.Nextjs(&quot;MyWeb&quot;, {\n\u00a0\u00a0link: [bucket],\n});<\/code><\/pre>\n\n\n\n<p>Internally, SST will modify the IAM permissions of the bucket so that the Next.js app can interact with it without needing any further authentication. At the same time, the SST SDK is notified about the resource, which means that it can be imported using the name defined in the SST config.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\" data-line=\"\">import { Resource } from &quot;sst&quot;;\n\nconsole.log(Resource.MyBucket.name);<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-center\">There\u2019s full type-safety for this (very nice)<\/p>\n\n\n\n<p>Not having to think about authenticating different services with each other and instead things \u201cjust working\u201d made our development experience significantly better. As Remotion SST from earlier also supports linking, we no longer had to worry about setting the correct permissions for remotion either (something we struggled with previously).<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">SST Live<\/h4>\n\n\n\n<p>One of the standout features of SST is SST Live. It makes it possible to test changes made to SST Components almost instantly, without having to redeploy the entire application.&nbsp;<\/p>\n\n\n\n<p>SST Live uses IoT over WebSocket to bridge communication between a developer\u2019s local machine and the remote Lambda function. When starting the app with <code class=\"\" data-line=\"\">sst de<\/code>v, SST swaps out the standard Lambda functions with stub versions. At the same time, a WebSocket client on the developer\u2019s machine connects to their AWS accounts\u2019 IoT endpoint.<\/p>\n\n\n\n<p>When a Lambda function is triggered, it sends an event with the request data. This event is then captured by the local WebSocket client, which then processes the function locally using a Node.js Worker. The result is finally sent back as an event, which the stub Lambda receives and returns as the function\u2019s response.<\/p>\n\n\n\n<p>All this setup may seem complicated, but it happens automatically in the background, and has several big advantages:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It\u2019s possible to debug apps using breakpoints<\/li>\n\n\n\n<li>The same IAM permissions are used in development and production, making it less likely to suddenly get surprised with permission issues after deploying<\/li>\n\n\n\n<li>Webhooks \u201cjust work\u201d, unlike with a local development environment<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">It\u2019s not all perfect though<\/h4>\n\n\n\n<p>While we\u2019re big fans of SST, and we definitely think that we made the right choice using it, it\u2019s not perfect. Our biggest issue with it by far is the lack of support for Windows. Apparently this is something that is being worked on, but for the duration of our project it meant that we had to do all development inside WSL. As both VS Code and WebStorm have support for working inside of WSL, this shouldn\u2019t be that big of an issue. Unfortunately, developing inside WSL consumes massive amounts of resources which meant that even team members with strong desktop machines occasionally struggled with system performance.<\/p>\n\n\n\n<p>We also wanted to set an expiry policy on the S3 bucket storing our generated speech audio (as it\u2019s not required after a video is finished), but SST doesn\u2019t provide an option for doing this. Fortunately, due to it being based on Pulumi, we were able to use Pulumi-native code instead and connect it to the SST component for the bucket.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\" data-line=\"\">const pollyBucket = new sst.aws.Bucket(&#039;PollyBucket&#039;);\nnew aws.s3.BucketLifecycleConfigurationV2(&#039;PollyBucketLifecycle&#039;, {\n      bucket: pollyBucket.name,\n      rules: [\n        {\n          id: &#039;DeleteExpiredObjects&#039;,\n          status: &#039;Enabled&#039;,\n          expiration: {\n            days: 1,\n            expiredObjectDeleteMarker: true,\n          },\n        },\n      ],\n    });\n  },\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Architecture<\/h2>\n\n\n\n<p>Our tech stack has already been mentioned, but let&#8217;s take a closer look at the communication between our various components.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"1008\" data-attachment-id=\"26541\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-61\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9.png\" data-orig-size=\"1600,1575\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"Architecture Diagram\" data-image-description=\"&lt;p&gt;Architecture Diagram&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;Architecture Diagram&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9-1024x1008.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9-1024x1008.png\" alt=\"\" class=\"wp-image-26541\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9-1024x1008.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9-300x295.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9-768x756.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9-1536x1512.png 1536w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-9.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<p>The core of our project is a Next.js app. In addition to serving the frontend and authenticating users using Clerk, it has several API routes and Server Actions which communicate with the other SST components and external providers.<\/p>\n\n\n\n<p>Server Actions are a relatively new feature in Next.js. They are authored as normal asynchronous JavaScript functions, but can be called from both the server and the frontend. Behind the scenes, Next.js actually transforms them into API endpoints which can then be called by the frontend. We could&#8217;ve used regular API endpoints instead, but Server Actions proved to be more convenient for some of our use cases.<\/p>\n\n\n\n<p>As previously mentioned, Remotion SST deploys a Lambda function to concurrently render the final video. We communicate with this Lambda using two regular API endpoints to start a new render and get its progress.<\/p>\n\n\n\n<p>There\u2019s also two lambda functions responsible for generating speech and speechmark, which talk to the Polly API internally. We\u2019re doing this because we struggled with returning both audio and speechmarks in a regular Next.js function. To keep them both in sync, every time we want to invoke a speech processing we call an API endpoint which triggers both lambdas simultaneously.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Struggles<\/h2>\n\n\n\n<p>As with any bigger project, we encountered a number of issues during the development process that caused quite a bit of frustration.<\/p>\n\n\n\n<p>We\u2019ve already talked about how much effort it was to set up a development environment using WSL, but for illustrative purposes, we\u2019d like to briefly go over all the steps: Create an account in IAM Identity Center, install WSL, install the AWS CLI, create an AWS CLI profile, install SST, set up WSL to work with GitHub, install Node inside WSL, make Node work with the PNPM package manager, set up an IDE to work inside WSL, Phew. Doing this process again and again on many different machines almost drove us insane.<\/p>\n\n\n\n<p>Oh, and on the topic of authentication. IAM Identity Center is a tool designed to make it easier to share an AWS account. Each of us got their own IAM Identity Center account, which could then be used to log in to AWS. Unfortunately, the setup process for this is very convoluted. Every user needs to get an invitation, create their account (including mandatory 2FA), then get assigned a group which is allowed to act as our original AWS account. Now, every time one of us wants to sign into AWS, they have to go to a specific sign-in URL (the normal AWS sign-in won\u2019t work), then select the original AWS account. And before we discovered settings to change the session length, this had to be done every 2 hours for both the AWS console and CLI.<\/p>\n\n\n\n<p>The biggest challenge we faced though, was integrating third-party APIs, including those from platforms such as Reddit, TikTok and YouTube. Access to these APIs is difficult to get, making it hard to get the data we needed for our project. Each platform has its own set of restrictions, rate limits and quirks that we had to work around. The Reddit API, as mentioned above, was completely inaccessible to us due to several restrictions that limited automated access. TikTok&#8217;s API has a pretty elaborate registration process and we were also pretty sure that TikTok wouldn&#8217;t like our use case that much. So we decided to try uploading to YouTube, but here was the next hurdle. YouTube&#8217;s API has a rough quota limit per day that only allows us to upload 6 videos per day for the whole application. We could have requested an increase of this, but, like TikTok, they also require a detailed registration process. As this was just a student project, we didn&#8217;t see any chance of success.<\/p>\n\n\n\n<p>Another very scary thing we didn\u2019t think of at first were the costs. During setup, we set the AWS Cost Alert for our infrastructure to 5$. This should\u2019ve prevented any serious cost spike, but, well\u2026<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><a href=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10.png\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"453\" data-attachment-id=\"26542\" data-permalink=\"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2024\/09\/03\/how-to-not-get-rich-using-tiktok-and-aws\/image-62\/\" data-orig-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10.png\" data-orig-size=\"1173,519\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"AWS Cost Explosion\" data-image-description=\"&lt;p&gt;AWS Cost Explosion&lt;\/p&gt;\n\" data-image-caption=\"&lt;p&gt;AWS Cost Explosion&lt;\/p&gt;\n\" data-large-file=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10-1024x453.png\" src=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10-1024x453.png\" alt=\"\" class=\"wp-image-26542\" srcset=\"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10-1024x453.png 1024w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10-300x133.png 300w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10-768x340.png 768w, https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/image-10.png 1173w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><figcaption class=\"wp-element-caption\">Ooops\u2026 Ermmm\u2026<\/figcaption><\/figure>\n\n\n\n<p>We needed a database for storing our metadata and wanted to use Drizzle as ORM due to its convenience. As the official SST documentation had <a href=\"https:\/\/sst.dev\/docs\/start\/aws\/drizzle\" target=\"_blank\" rel=\"noopener\" title=\"\">instructions on how to set this up<\/a>, we decided to follow them.<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-typescript\" data-line=\"\">async run() {\n  const vpc = new sst.aws.Vpc(&quot;MyVpc&quot;);\n  const rds = new sst.aws.Postgres(&quot;MyPostgres&quot;, { vpc });\n},<\/code><\/pre>\n\n\n\n<p class=\"has-text-align-center\">Doesn\u2019t look very threatening, does it?<\/p>\n\n\n\n<p>This turned out to be a massive mistake though. We never actually got Drizzle to work properly, with errors warning about a missing region or hitting the maximum number of addresses allocated to the VPC instance. So we abandoned our attempts and moved on to DynamoDB without an ORM, which was surprisingly simple to set up. It took some time before we eventually received an email from AWS mentioning that we had slightly exceeded our budget. By 75$. It turned out that the VPC and RDS database we had created were still running. And a sentence we had read over in the docs mentioned that this setup, in its smallest form, would cost about $40\/month. For some strange reason, <code class=\"\" data-line=\"\">sst remove<\/code>, which should normally remove all AWS components for a user, didn&#8217;t apply to them. Even worse, as two different people had followed the docs, we actually had two instances of both the VPC and database.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>At the start of this semester, none of us had any experience with the larger cloud providers. While some of us had used platforms like Vercel or hosted a VPS at a provider like Hetzner, the scale and complexity of platforms like AWS was entirely new to us. The learning curve was steep, but it also provided us with an opportunity to expand our skills in an area that is increasingly important in the tech industry.<\/p>\n\n\n\n<p>Through the course, we gained a deeper understanding of cloud platforms and their complexities. AWS, in particular, introduced us to challenges and features we hadn\u2019t encountered before. Whether it was figuring out how to properly configure permissions or instructing the cloud provider on how to perform certain functions, each task forced us to develop new skills and improve our understanding of cloud infrastructure.<\/p>\n\n\n\n<p>We\u2019ve talked a lot about SST throughout this post (we promise we\u2019re not sponsored). Despite the rough edges, overall, it gave us a better development experience than we could have had with any other tool. As we\u2019re writing this, it looks like there\u2019s finally progress being made on a Windows version too. We\u2019d definitely recommend that anyone building projects inside the JavaScript ecosystem give them a try.<\/p>\n\n\n\n<p>It\u2019s unfortunate that we haven\u2019t been able to integrate with the platforms more deeply. As <a href=\"https:\/\/simonwillison.net\/2024\/May\/8\/slop\/\" target=\"_blank\" rel=\"noopener\" title=\"\">AI-generated \u201cslop\u201d<\/a> floods everything from social media to search results, it\u2019s understandable why platforms are becoming increasingly hostile to anyone trying to extract value out of content they host. To be completely honest, we\u2019re a part of this problem too. Scraping the web trying to auto-generate content for the web to try and make a profit isn\u2019t exactly making the world a better place. The fact our project is only a proof of concept is probably for the best.<\/p>\n\n\n\n<p>We\u2019re still proud of the technical achievements we made working on this project. We\u2019ll just try and use our newfound knowledge for better ideas in the future.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In our Software Development for Cloud Computing course, we were tasked with building something, anything, using the cloud. After various discarded ideas, our team decided to create a platform aimed at automatically generating short-form videos. In this blog post, we will go over our development journey, the architectural decisions we made, the technologies we chose (and why), the challenges we encountered and the (many) lessons we learned.<\/p>\n","protected":false},"author":1207,"featured_media":26544,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[652,660,120,659,262,650,22,21],"tags":[106,84,7,533,91,1002,1046],"ppma_author":[1035,1044,1045,1036],"class_list":["post-26422","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-artificial-intelligence","category-chatgpt-and-language-models","category-cloud-technologies","category-devops","category-rich-media-systems","category-scalable-systems","category-student-projects","category-system-architecture","tag-artificial-intelligence","tag-aws","tag-cloud","tag-cloud-computing-2","tag-microservices","tag-next-js","tag-sst"],"aioseo_notices":[],"jetpack_featured_media_url":"https:\/\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2024\/08\/Frame-2-1.png","jetpack-related-posts":[{"id":24246,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/02\/27\/how-edge-computing-is-moving-the-cloud-closer-to-the-user\/","url_meta":{"origin":26422,"position":0},"title":"How Edge Computing is moving the Cloud closer to the User","author":"Nikolai Thees","date":"27. February 2023","format":false,"excerpt":"Did you know clouds have sharp edges? What is Edge Computing? Let\u2019s say you want to deploy a web application. In order to serve your app to your users, you need a server on which you can run your application.Traditionally, you had the option to either buy and run the\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/lh4.googleusercontent.com\/uCYgoQ2o7ueAQYKEvAup43hsF7rDPIyBl5Uh-qMTzmOU5mozruJsWI_kp_BTfpjhMkcrhbEzHoZvBthhNk9GrF9KE3Oxd73nnOJ2YZsIZt66xSEJghrtdVd00YeozgM6k-ACpmcCHexjQ8VLo6EC-QM","width":350,"height":200},"classes":[]},{"id":24051,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2023\/02\/27\/fog-computing-solving-the-limitations-of-cloud-and-edge-computing\/","url_meta":{"origin":26422,"position":1},"title":"Fog Computing: Solving the limitations of Cloud and Edge Computing","author":"Andreas Nicklaus","date":"27. February 2023","format":false,"excerpt":"Fog computing offers a compromise between cloud and edge computing for real-time, scalable data analysis. Ideal for regional applications and IoT. However, authentication and privacy issues must be addressed.","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/edge-computing-diagram-1024x512.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/edge-computing-diagram-1024x512.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/edge-computing-diagram-1024x512.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2023\/08\/edge-computing-diagram-1024x512.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":5635,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2019\/03\/05\/a-dive-into-serverless-on-the-basis-of-aws-lambda\/","url_meta":{"origin":26422,"position":2},"title":"A Dive into Serverless on the Basis of AWS Lambda","author":"Can Kattwinkel","date":"5. March 2019","format":false,"excerpt":"Hypes help to overlook the fact that tech is often reinventing the wheel, forcing developers to update applications and architecture accordingly in painful migrations. Besides Kubernetes one of those current hypes is Serverless computing. While everyone agrees that Serverless offers some advantages it also introduces many problems. The current trend\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/03\/warm.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/03\/warm.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2019\/03\/warm.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":21651,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2021\/09\/18\/deploy-random-chat-application-on-aws-ec2-with-kubernetes\/","url_meta":{"origin":26422,"position":3},"title":"Deploying Random Chat Application on AWS EC2 with Kubernetes","author":"dv029","date":"18. September 2021","format":false,"excerpt":"1. Introduction For the examination of the lecture \u201cSoftware Development for Cloud Computing\u201d, I want to build a simple Random Chat Application. The idea of this application is based on the famous chat application called Omegle. Omegle is where people can meet random people in the world and can have\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2021\/09\/image-19.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":12087,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2020\/09\/30\/getting-started-with-cloud-computing-a-covid-19-data-map\/","url_meta":{"origin":26422,"position":4},"title":"Getting Started with Cloud Computing &#8211; A COVID-19 Data Map","author":"mk306","date":"30. September 2020","format":false,"excerpt":"1. Abstract Are you searching for country-specific, up-to-date numbers and rates for the global pandemic caused by COVID-19? Well, then I got some bad news for you. You won\u2019t find any in this blog post\u2026 not directly anyway. If you are looking for in-depth information about public APIs, location-based data\u2026","rel":"","context":"In &quot;Cloud Technologies&quot;","block_context":{"text":"Cloud Technologies","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/scalable-systems\/cloud-technologies\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/Response-1.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/Response-1.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2020\/09\/Response-1.png?resize=525%2C300&ssl=1 1.5x"},"classes":[]},{"id":22151,"url":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/2022\/02\/22\/designing-and-implementing-a-scalable-web-application\/","url_meta":{"origin":26422,"position":5},"title":"Designing the framework for a scalable CI\/CD supported web application","author":"Danial Eshete","date":"22. February 2022","format":false,"excerpt":"Documentation of our approaches to the project, our experiences and finally the lessons we learned. The development team approaches the project with little knowledge of cloud services and infrastructure. Furthermore, no one has significant experience with containers and\/or containerized applications. However, the team is well experienced in web development and\u2026","rel":"","context":"In &quot;Allgemein&quot;","block_context":{"text":"Allgemein","link":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/category\/allgemein\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Design_Desktop_Logged_In-3-150x150.jpg?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Design_Desktop_Logged_In-3-150x150.jpg?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Design_Desktop_Logged_In-3-150x150.jpg?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Design_Desktop_Logged_In-3-150x150.jpg?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Design_Desktop_Logged_In-3-150x150.jpg?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/blog.mi.hdm-stuttgart.de\/wp-content\/uploads\/2022\/02\/Design_Desktop_Logged_In-3-150x150.jpg?resize=1400%2C800&ssl=1 4x"},"classes":[]}],"jetpack_sharing_enabled":true,"authors":[{"term_id":1035,"user_id":1207,"is_guest":0,"slug":"robin_schmidt","display_name":"Robin Schmidt","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/a0c4d706261a2d836b2eea83799fe2c3cf82a858681a2d685644006285830865?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""},{"term_id":1044,"user_id":1216,"is_guest":0,"slug":"tobias_mener","display_name":"Tobias Me\u00dfner","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/b267d920fa9f5e0bb9eda975e37b34041ee1c4c1720c71e3be6a623608987195?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""},{"term_id":1045,"user_id":1220,"is_guest":0,"slug":"deniz_gazitepe","display_name":"Deniz Gazitepe","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/8f36a80221c8ad145c20db1529ab82547a7fdf0baa986ed531c10928bf1d79b3?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""},{"term_id":1036,"user_id":1212,"is_guest":0,"slug":"michael_jaufmann","display_name":"Michael Jaufmann","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/118a41c65295333e9e425495834f8735b351d5f674865415e7e3ec968fa2ee2a?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/26422","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/users\/1207"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/comments?post=26422"}],"version-history":[{"count":15,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/26422\/revisions"}],"predecessor-version":[{"id":26601,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/posts\/26422\/revisions\/26601"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media\/26544"}],"wp:attachment":[{"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/media?parent=26422"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/categories?post=26422"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/tags?post=26422"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mi.hdm-stuttgart.de\/index.php\/wp-json\/wp\/v2\/ppma_author?post=26422"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}