<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Noah Matsell</title>
    <description>The latest articles on Forem by Noah Matsell (@noahm).</description>
    <link>https://forem.com/noahm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1902827%2Fc8812fe2-848b-462d-b9cc-298f59d477d7.jpg</url>
      <title>Forem: Noah Matsell</title>
      <link>https://forem.com/noahm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/noahm"/>
    <language>en</language>
    <item>
      <title>AI Diagramming Tools for Developers</title>
      <dc:creator>Noah Matsell</dc:creator>
      <pubDate>Thu, 12 Dec 2024 17:34:46 +0000</pubDate>
      <link>https://forem.com/noahm/ai-diagramming-tools-for-developers-1mkj</link>
      <guid>https://forem.com/noahm/ai-diagramming-tools-for-developers-1mkj</guid>
      <description>&lt;p&gt;For Software Developers, diagrams are more than just visuals—they’re critical tools for understanding and communicating complex systems. Whether you’re designing a database schema, illustrating a workflow, or debugging an architecture, a well-crafted diagram can save hours of confusion.&lt;/p&gt;

&lt;p&gt;With the rise of AI-powered tools, creating these diagrams has never been easier. In this post, I evaluate some of the most popular AI-enabled diagramming tools, including &lt;a href="https://miro.com/" rel="noopener noreferrer"&gt;Miro&lt;/a&gt;, &lt;a href="https://excalidraw.com/" rel="noopener noreferrer"&gt;Excalidraw&lt;/a&gt;, &lt;a href="https://www.figma.com/figjam/" rel="noopener noreferrer"&gt;Figma's FigJam&lt;/a&gt;, &lt;a href="https://openai.com/index/chatgpt/" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; (with &lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;Mermaid&lt;/a&gt;). My goal is to see how they perform when tasked with real-world developer scenarios like sequence diagrams or database ERDs.&lt;/p&gt;

&lt;p&gt;For this evaluation, I'll be creating two diagrams with each tool:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A sequence diagram&lt;/strong&gt; for a simple uploading a file in a web application.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
  The sequence diagram prompt
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a sequence diagram for uploading a file in a web application. The process includes:
- The user selecting a file.
- The browser sending the file to the server.
- The server validating the file.
- The server storing the file in cloud storage.
- The server sending a success response - back to the browser.
- Label the actors as User, Browser, Server, and Cloud Storage.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An entity relationship diagram (ERD)&lt;/strong&gt; describing a simple database schema with a User, Author, and Post models.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
  The ERD prompt
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create an entity relationship diagram for a database schema with the following models:

- User: Attributes include id (primary key), name, and email.
- Author: Attributes include id (primary key) and bio.
- Post: Attributes include id (primary key), title, content, and authorId (foreign key).

Relationships:
- A User can create many Posts.
- An Author is associated with a User (one-to-one).
- Each Post is written by one Author.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 4 main evaluation criteria are:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ease of use&lt;/li&gt;
&lt;li&gt;Customization&lt;/li&gt;
&lt;li&gt;Accuracy&lt;/li&gt;
&lt;li&gt;Export options + Integration with other tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let the testing begin!&lt;/p&gt;



&lt;h2&gt;
  
  
  Miro
&lt;/h2&gt;

&lt;p&gt;Miro is a popular online whiteboard tool that offers a wide range of features, including diagramming, collaboration, and project management.&lt;/p&gt;

&lt;p&gt;At the moment their "Create with AI" offering seems pretty robust. In addition to diagrams, it allows for creating stickies, docs, and images too.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sequence Diagram Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ToCgFjDBX4M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpkvjuh15222gfygu2mp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqpkvjuh15222gfygu2mp.jpg" alt="miro sequence diagram" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ERD Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8Ea4Y00SKSk"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2grmsr0gwbzxleehguu7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2grmsr0gwbzxleehguu7.jpg" alt="miro erd" width="800" height="993"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Evaluation
&lt;/h3&gt;

&lt;p&gt;Miro had a really strong showing here. Their AI capabilities do feel polished and well integrated in the larger context. It was easy to use and the output was accurate.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢🟢🟢⚪️ &lt;strong&gt;Ease of use&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢🟢⚪️ &lt;strong&gt;Export options + Integration with other tools&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢🟢⚪️ &lt;strong&gt;Customization&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢🟢🟢 &lt;strong&gt;Accuracy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Miro Total Score: 13/16 (81.25%)
&lt;/h4&gt;



&lt;h2&gt;
  
  
  Excalidraw
&lt;/h2&gt;

&lt;p&gt;Excalidraw is a free, open-source whiteboarding tool for creating diagrams. It's known for its simplicity and ease of use.&lt;/p&gt;

&lt;p&gt;Their AI offerings currently include a text-to-diagram tool and a (pretty mindblowing) diagram-to-code tool.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sequence Diagram Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/wPK7Vll40P0"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bd5lxaifcd3xtv5ybi4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3bd5lxaifcd3xtv5ybi4.png" alt="excalidraw sequence" width="800" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ERD Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/v4LRFQO93bo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukfz4ckd614d5pias6xb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukfz4ckd614d5pias6xb.png" alt="excalidraw erd" width="523" height="988"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Evaluation
&lt;/h3&gt;

&lt;p&gt;Excalidraw's AI capabilities are pretty straightforward. They don't feel quite as integrated as Miro's, but the tool is still very easy to use. Unfortunately, the output for the ERD diagram was an image, which makes it less customizable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢🟢🟢⚪️ &lt;strong&gt;Ease of use&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢🟢⚪️ &lt;strong&gt;Export options + Integration with other tools&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢⚪️⚪️ &lt;strong&gt;Customization&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢⚪️⚪️ &lt;strong&gt;Accuracy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Excalidraw Total Score: 10/16 (62.5%)
&lt;/h4&gt;



&lt;h2&gt;
  
  
  ChatGPT + Mermaid
&lt;/h2&gt;

&lt;p&gt;One of the many ways that ChatGPT can be used is to generate diagrams. In this case, I used ChatGPT to generate &lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;Mermaid markdown-inspired code&lt;/a&gt; representing the diagrams.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sequence Diagram Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/KrbCaSvv-8A"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F287fc5pm72tekcnf2lkb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F287fc5pm72tekcnf2lkb.png" alt="chatgpt sequence diagram" width="610" height="1092"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ERD Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/oVROl6qVeU4"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9cpkn2b7hvceo5wctn6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe9cpkn2b7hvceo5wctn6.png" alt="chatgpt erd" width="610" height="1092"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Evaluation
&lt;/h3&gt;

&lt;p&gt;This was a bit difficult to compare against other tools. The output was extremely fast and accurate, but the process was a bit more manual. You do need a bit of technical knowledge to use Mermaid, but once you get the hang of the syntax it becomes extremely powerful. Diagrams represented in code can be easily shared, integrated, and version controlled. Unfortunately the barrier to entry is a bit higher.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢⚪️⚪️⚪️ &lt;strong&gt;Ease of use&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢🟢⚪️ &lt;strong&gt;Export options + Integration with other tools&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢⚪️⚪️ &lt;strong&gt;Customization&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢🟢🟢 &lt;strong&gt;Accuracy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  ChatGPT Total Score: 10/16 (62.5%)
&lt;/h4&gt;



&lt;h2&gt;
  
  
  FigJam
&lt;/h2&gt;

&lt;p&gt;Figma's FigJam is another whiteboarding tool that complements its original design tool. It offers a range of features for collaboration, brainstorming, and diagramming. The "Generate" AI offering is still in beta, and seems to be tucked away in the top right menu bar.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sequence Diagram Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Aq-sedHNmmA"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc5mly77x86vwlgxi2nt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc5mly77x86vwlgxi2nt.png" alt="figjam sequence diagram" width="800" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ERD Output
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/NjYBRDHm7sc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flq0x3vmnc0b3bp7o1j6k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flq0x3vmnc0b3bp7o1j6k.png" alt="Figjam erd" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Evaluation
&lt;/h3&gt;

&lt;p&gt;Unfortunately it seems like the diagramming aspect of this tool isn't really meant for prime time quite yet. I found the input options to be a bit confusing to use and, of all the tools, it had the most trouble accurately representing the diagrams. It seemed to treat everything as a simple flow chart.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢🟢⚪️⚪️ &lt;strong&gt;Ease of use&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢🟢⚪️ &lt;strong&gt;Export options + Integration with other tools&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢🟢⚪️⚪️ &lt;strong&gt;Customization&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;🟢⚪️⚪️⚪️ &lt;strong&gt;Accuracy&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  FigJam Total Score: 8/16 (50%)
&lt;/h4&gt;



&lt;h2&gt;
  
  
  Top Tool and Final Thoughts
&lt;/h2&gt;

&lt;p&gt;After evaluating these tools, while each has strengths and trade-offs, &lt;strong&gt;the clear winner here is Miro&lt;/strong&gt; with a score of 13/16. It stands out for a comparatively mature AI offering that is well integrated in its existing ecosystem. Out of the tools tested, it's the best overall choice for developers seeking quick, accurate, and customizable diagrams.&lt;/p&gt;

&lt;p&gt;I'll also give an &lt;strong&gt;honourable mention to ChatGPT + Mermaid&lt;/strong&gt;. While it does require more technical knowledge, it shines for those who want precise, code-based outputs that are easy to version control. If you'd rather write code than mess around with a GUI, this is the tool for you.&lt;/p&gt;

&lt;p&gt;If you're ready to streamline your diagramming workflows with AI, I'd recommend giving one of these tools a try. And if there's a tool not mentioned here, I'd love to hear about it in the comments below 👇&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Fetching Liked Posts Using the Bluesky API</title>
      <dc:creator>Noah Matsell</dc:creator>
      <pubDate>Sun, 08 Dec 2024 19:35:42 +0000</pubDate>
      <link>https://forem.com/noahm/fetching-liked-posts-using-the-bluesky-api-4iko</link>
      <guid>https://forem.com/noahm/fetching-liked-posts-using-the-bluesky-api-4iko</guid>
      <description>&lt;p&gt;I've recently made the move over to &lt;a href="https://bsky.app/" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;. I can already confirm that there is a vibrant tech community there with tons of interesting, useful, and inspiring content. I'm a happy new user! As a result, I've been wanting to embed my top liked Bluesky posts in my "Dev roundup" monthly newsletter posts. My aim is to provide a curated list of Bluesky posts that is specifically tailored to Software Developers.&lt;/p&gt;

&lt;p&gt;Luckily, &lt;a href="https://docs.bsky.app/docs/get-started" rel="noopener noreferrer"&gt;Bluesky's API&lt;/a&gt; is completely free to use, allowing programmatic access to all of the content within. This tutorial will walk you through the process of retrieving and embedding liked Bluesky posts using their API, perfect for personal blogs, portfolios, or content aggregation projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding my Bluesky API Workflow
&lt;/h2&gt;

&lt;p&gt;I have built a script that allows me to automatically embed my Bluesky posts in a markdown blog post. I think that any or all of the steps used in this script are valuable for many use-cases.&lt;/p&gt;

&lt;p&gt;To summarize my workflow for embedding liked posts, we follow these key steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an authenticated session&lt;/li&gt;
&lt;li&gt;Retrieve liked post URIs for an "actor"&lt;/li&gt;
&lt;li&gt;Use these URIs to fetch &lt;a href="https://docs.bsky.app/docs/advanced-guides/oembed" rel="noopener noreferrer"&gt;oEmbed embed HTML&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Clean and format the embed code&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faauvkd7luz08cd1ne04b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faauvkd7luz08cd1ne04b.png" alt="Image description" width="800" height="643"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Complete Implementation
&lt;/h2&gt;

&lt;p&gt;Let's break down each function and its purpose:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Creating a Bluesky Session
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createSession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://bsky.social/xrpc/com.atproto.server.createSession&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
          &lt;span class="na"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-handle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;responseJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessJwt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to create session: &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This function authenticates your Bluesky account.

&lt;ul&gt;
&lt;li&gt;Note: this example hardcodes strings for credentials, but this should be avoided in production use cases.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;It returns an &lt;code&gt;accessJwt&lt;/code&gt; JWT (JSON Web Token) for subsequent API calls&lt;/li&gt;

&lt;li&gt;Uses the &lt;code&gt;createSession&lt;/code&gt; endpoint from Bluesky's ATP (Authenticated Transfer Protocol)&lt;/li&gt;

&lt;li&gt;Error handling ensures graceful failure if authentication fails&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Retrieving Liked Post URIs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getBlueskyLikeUris&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to get token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://bsky.social/xrpc/app.bsky.feed.getActorLikes?actor=${actor}&amp;amp;limit=${limit}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uris&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;responseJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;uris&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires an authenticated session token&lt;/li&gt;
&lt;li&gt;Uses &lt;a href="https://docs.bsky.app/docs/api/app-bsky-feed-get-actor-likes" rel="noopener noreferrer"&gt;the &lt;code&gt;getActorLikes&lt;/code&gt; endpoint&lt;/a&gt; to fetch liked posts&lt;/li&gt;
&lt;li&gt;Important -- the endpoint domain should be &lt;a href="https://bsky.social" rel="noopener noreferrer"&gt;https://bsky.social&lt;/a&gt;, as this is &lt;a href="https://docs.bsky.app/docs/advanced-guides/entryway" rel="noopener noreferrer"&gt;an authenticated request.&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Extracts unique URIs for each liked post&lt;/li&gt;
&lt;li&gt;Limits to 40 posts (configurable)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Converting URIs to Embeddable HTML
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getBlueskyPostEmbedMarkup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://embed.bsky.app/oembed?url=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formattedHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prettier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;responseJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prettier/parser-html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
      &lt;span class="na"&gt;htmlWhitespaceSensitivity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ignore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;printWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formattedHTML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&amp;lt;script&lt;/span&gt;&lt;span class="se"&gt;[\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;&amp;lt;&lt;/span&gt;&lt;span class="se"&gt;\/&lt;/span&gt;&lt;span class="sr"&gt;script&amp;gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Failed to get Bluesky post embed markup&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Insights:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses &lt;a href="https://docs.bsky.app/docs/advanced-guides/oembed" rel="noopener noreferrer"&gt;Bluesky's oEmbed endpoint&lt;/a&gt; with post URIs to access a post's embeddable HTML&lt;/li&gt;
&lt;li&gt;Optional: Utilizes &lt;code&gt;prettier&lt;/code&gt; to format the HTML consistently&lt;/li&gt;
&lt;li&gt;Optional: Removes &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags for security and clean embedding

&lt;ul&gt;
&lt;li&gt;The reason for this is that I embed a single Bluesky script for each post containing Bluesky content.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Flexible error handling&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Putting It All Together: A Complete Example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;embedLikedPosts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get liked post URIs&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;likedPostUris&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getBlueskyLikeUris&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;likedPostUris&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No liked posts found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Convert URIs to embed HTML&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedPromises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;likedPostUris&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getBlueskyPostEmbedMarkup&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embedHtmlArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embedPromises&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Filter out any failed embeds&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;validEmbeds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;embedHtmlArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;embed&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;embed&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the markup for all liked posts&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`
      ## Some Fave Posts 🦋
      &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;validEmbeds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`\n\n`&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;
    `&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error embedding Bluesky posts:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Potential Enhancements
&lt;/h2&gt;

&lt;p&gt;This solution works for me because all that I require is a statically generated monthly blog post.&lt;/p&gt;

&lt;p&gt;Some improvements could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add pagination support for fetching more than 40 liked posts&lt;/li&gt;
&lt;li&gt;Implement caching to reduce unnecessary API calls&lt;/li&gt;
&lt;li&gt;Create a more robust error handling mechanism&lt;/li&gt;
&lt;li&gt;Creating mechanism for refreshing &lt;code&gt;accessJwt&lt;/code&gt; token if used in long running processes&lt;/li&gt;
&lt;li&gt;Sorting liked posts by popularity (likes)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Verify your Bluesky credentials are correct&lt;/li&gt;
&lt;li&gt;Check that the Bearer token is being set correctly on your authenticated requests.&lt;/li&gt;
&lt;li&gt;Verify that the endpoint domains you are using are all valid.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Embedding Bluesky posts provides a dynamic way to showcase your social media interactions. By understanding the API workflow and implementing robust error handling, you can create engaging, personalized, and curated content integrations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Experiment with the code&lt;/li&gt;
&lt;li&gt;Customize the embed styling&lt;/li&gt;
&lt;li&gt;Explore additional Bluesky API endpoints&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Enjoy and happy tinkering! 🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>typescript</category>
      <category>bluesky</category>
    </item>
  </channel>
</rss>
