<?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>DEV Community: Diagrid</title>
    <description>The latest articles on DEV Community by Diagrid (@diagrid).</description>
    <link>https://dev.to/diagrid</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%2Forganization%2Fprofile_image%2F6621%2Fca07de48-8345-4222-95f4-7c01627ee72e.png</url>
      <title>DEV Community: Diagrid</title>
      <link>https://dev.to/diagrid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/diagrid"/>
    <language>en</language>
    <item>
      <title>Join the Diagrid Catalyst AWS Hackathon!</title>
      <dc:creator>Marc Duiker</dc:creator>
      <pubDate>Mon, 29 Apr 2024 08:13:50 +0000</pubDate>
      <link>https://dev.to/diagrid/join-the-diagrid-catalyst-aws-hackathon-4849</link>
      <guid>https://dev.to/diagrid/join-the-diagrid-catalyst-aws-hackathon-4849</guid>
      <description>&lt;p&gt;We are excited to invite you to the Diagrid Catalyst AWS Hackathon—a four-week, virtual event where you can unleash your creativity and showcase your coding skills by building or modernizing a cutting-edge application using the powerful Diagrid Catalyst platform. Get ready to collaborate, compete, and win cash prizes! &lt;a href="http://pages.diagrid.io/catalyst-hackathon"&gt;Register now&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Diagrid Catalyst?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.diagrid.io/catalyst"&gt;Diagrid Catalyst&lt;/a&gt; is a Developer API platform providing a brand-new approach to distributed application development. Using the Catalyst APIs, powered by the &lt;a href="https://dapr.io/"&gt;Dapr&lt;/a&gt; open source project, developers can overcome the complexity of rewriting common software patterns and achieve higher productivity by offloading infrastructure concerns from their code to Catalyst.&lt;/p&gt;

&lt;p&gt;Say hello to making API calls from any platform, in any language to build modern, cloud-applications with ease. This hackathon is your chance to explore the potential of Diagrid Catalyst while bringing your solution ideas to life! For more on Catalyst, watch our most recent &lt;a href="https://youtu.be/mLpIfX3hcwg?list=PLdl4NkEiMsJscq00RLRrN4ip_VpzvuwUC"&gt;webinar&lt;/a&gt; for a platform overview and API deep dive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Participate?
&lt;/h2&gt;

&lt;p&gt;Join the Diagrid Catalyst Hackathon to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Innovate with an Emerging Product:&lt;/strong&gt; Explore the ways Diagrid Catalyst can expedite your development process by building an application to solve real-world challenges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collaborate with Peers:&lt;/strong&gt; Connect with fellow developers, designers, and entrepreneurs. Collaborate, share ideas, and learn from each other's experiences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhance Your Skills:&lt;/strong&gt; Sharpen your application development and architecture skills using the coding languages of your choice and deploying your solution on Amazon Web Services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Win Prizes:&lt;/strong&gt; Compete for impressive cash prizes! We have rewards lined up for the most exceptional projects and for providing product feedback.
‍
All participation is evaluated based on the criteria in the Hackathon &lt;a href="https://22146261.fs1.hubspotusercontent-na1.net/hubfs/22146261/Catalyst%20Hackathon/Diagrid%20Catalyst%20AWS%20Hackathon%20Rules.docx.pdf"&gt;Rules, Terms &amp;amp; Conditions&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  What to Build?
&lt;/h2&gt;

&lt;p&gt;Build or update an open-source microservice-based software solution utilizing at least two Diagrid Catalyst APIs. The solution should be composed of multiple application services and either be newly written or significantly enhanced to utilize Diagrid Catalyst. The solution should be architected using AWS infrastructure and hosting services. The solution must align with one of the areas below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polyglot compute/ Cross-platform service discovery:&lt;/strong&gt; Application services are deployed to different AWS hosting platforms, which interact via the Catalyst APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stateful business process orchestration:&lt;/strong&gt; Application services are instrumented as part of a Dapr Workflow using the Dapr Workflow authoring SDK paired with the Catalyst Workflow API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless architecture:&lt;/strong&gt; Application services are optimized to run on AWS PaaS/SaaS services, which abstracts infrastructure concerns and enables high-scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hackathon Timeline
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registration Open:&lt;/strong&gt; April 19th 2024&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Submissions Open:&lt;/strong&gt; May 10th 2024&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback Submissions Open:&lt;/strong&gt; May 24th 2024&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registration Closes:&lt;/strong&gt; May 17th 2024&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Project Submissions Deadline:&lt;/strong&gt; June 7th 2024&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feedback Submissions Deadline:&lt;/strong&gt; June 14th 2024&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Location:&lt;/strong&gt; Virtual Only&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Catalyst Reference Apps
&lt;/h2&gt;

&lt;p&gt;Looking for inspiration? See below for a couple of sample projects we’ve built using Diagrid Catalyst. More samples, including an AWS-specific reference, will be available to participants on the Hackathon site.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tetris Game
&lt;/h3&gt;

&lt;p&gt;The classic “blocks falling from the sky” game! The &lt;a href="https://github.com/diagrid-labs/catalyst-tetris"&gt;solution&lt;/a&gt; consists of two services:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Python Flask &lt;em&gt;user&lt;/em&gt; service which serves the JavaScript/HTML front-end for the user sign up and leaderboard and handles user state management and leaderboard updates.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;game&lt;/em&gt; service written in Go, which serves the JavaScript/HTML frontend for the game itself and manages the game session lifecycle.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These two services need to manage state and communicate to ensure games are started with the appropriate users, and user profiles are updated with the game results. The service interaction and data storage is all implemented using the APIs provided by Catalyst. Read the &lt;a href="https://www.diagrid.io/blog/build-multiplayer-tetris-in-the-browser-using-python-and-go"&gt;blog post&lt;/a&gt;.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Pizza Store Management System
&lt;/h3&gt;

&lt;p&gt;A distributed pizza store with Catalyst APIs. The solution comprises two .NET microservices, two serverless JavaScript functions and a Vue front-end. The applications depend on Diagrid Catalyst as the serverless API layer to connect to the underlying data and messaging infrastructure. Read the blog.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  How to Participate?
&lt;/h2&gt;

&lt;p&gt;Participation is straightforward:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Register:&lt;/strong&gt; Submit a registration request form for the hackathon. Eligible participants will receive a direct invite to the Hackathon site, hosted on Devpost. Accept your invitation to Devpost and click the blue “Register” button on the Hackathon landing page to register.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a project:&lt;/strong&gt; Create a project to frame out your hackathon solution ideas for others to see and potentially join in to help execute. You have a few options when it comes to projects:

&lt;ol&gt;
&lt;li&gt;Create a closed project to participate as an individual.&lt;/li&gt;
&lt;li&gt;Create an open project to enable other Hackathon participants to join you in your mission. You can even choose to invite people to participate in your project.&lt;/li&gt;
&lt;li&gt;Join an existing, open project if one is available and intrigues you.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Learn:&lt;/strong&gt; Join our ongoing &lt;a href="https://pages.diagrid.io/diagrid-webinar-registration-catalyst"&gt;Catalyst webinar series&lt;/a&gt; or &lt;a href="https://www.youtube.com/playlist?list=PLdl4NkEiMsJscq00RLRrN4ip_VpzvuwUC"&gt;watch on-demand&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build:&lt;/strong&gt; Develop your application using Diagrid Catalyst. Let your creativity shine, and don't forget to integrate innovative features.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Submit:&lt;/strong&gt; Submit your project with the required deliverables by the deadline, June 7&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For more information on resource access, project requirements and judging criteria, view the Hackathon &lt;a href="https://22146261.fs1.hubspotusercontent-na1.net/hubfs/22146261/Catalyst%20Hackathon/Diagrid%20Catalyst%20AWS%20Hackathon%20Rules.docx.pdf"&gt;Rules, Terms &amp;amp; Conditions&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prizes
&lt;/h2&gt;

&lt;p&gt;We have exciting cash prizes up for grabs! Prizes range from a Grand Prize of $2000 to Runner-up Prizes of $1000 each and Bonus Prizes from $250-$500! Get swag for participating &amp;amp; provide valuable product feedback for the chance to win another $100.&lt;/p&gt;

&lt;p&gt;If you participate in the hackathon, you are also eligible for a $100 referral bonus for spreading the word! Share the Hackathon details with your friends and encourage them to sign up. The participant with the most referrals wins!&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Ready to Hack
&lt;/h2&gt;

&lt;p&gt;Don’t miss this unique opportunity to explore Diagrid Catalyst, build innovative apps, and compete for cash prizes. Whether you're a seasoned developer or a curious beginner, the Diagrid Catalyst Hackathon is your time to shine.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://pages.diagrid.io/catalyst-hackathon"&gt;Sign up&lt;/a&gt; to participate today.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>programming</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Building a distributed pizza store in .NET with serverless Dapr APIs</title>
      <dc:creator>Marc Duiker</dc:creator>
      <pubDate>Thu, 01 Feb 2024 10:57:11 +0000</pubDate>
      <link>https://dev.to/diagrid/building-a-distributed-pizza-store-in-net-with-serverless-dapr-apis-j1j</link>
      <guid>https://dev.to/diagrid/building-a-distributed-pizza-store-in-net-with-serverless-dapr-apis-j1j</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;When developers build distributed systems, they need to think about cross-cutting concerns such as security, observability, and resiliency. Developers also write a lot of glue code to orchestrate services, create long-running and durable workflows, and connect to external resources, such as message brokers and state stores. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://dapr.io/" rel="noopener noreferrer"&gt;Dapr&lt;/a&gt;, the Distributed Application Runtime, is ideal for building distributed systems since it has built-in support for cross-cutting concerns, and it offers a suite of APIs for communication, state and workflow, that make developers more efficient.  According to the &lt;a href="https://www.diagrid.io/blog/the-state-of-dapr-2023-report" rel="noopener noreferrer"&gt;State of Dapr Report&lt;/a&gt;, 95% of Dapr developers claim that Dapr saves time, and 55% of the developers surveyed claimed it saved them more than 30% of development time.&lt;/p&gt;

&lt;p&gt;The usage of Dapr is also increasing thanks to a growing number of platforms that support the Dapr APIs. Dapr is no longer limited to running as a sidecar in Kubernetes, as is described in &lt;a href="https://www.diagrid.io/blog/dapr-deployment-models" rel="noopener noreferrer"&gt;Dapr Deployment Models&lt;/a&gt;. In this article, &lt;a href="https://www.diagrid.io/catalyst" rel="noopener noreferrer"&gt;Diagrid Catalyst&lt;/a&gt;, a suite of serverless Dapr APIs, is used to orchestrate services with &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/" rel="noopener noreferrer"&gt;Dapr workflow&lt;/a&gt;, and interact with message brokers and key-value stores.&lt;/p&gt;

&lt;p&gt;Throughout this article, you’ll be using the following to configure a distributed system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two Dapr services (.NET)&lt;/li&gt;
&lt;li&gt;Two serverless functions (JavaScript)&lt;/li&gt;
&lt;li&gt;A front-end (Vue)&lt;/li&gt;
&lt;li&gt;A key value state store&lt;/li&gt;
&lt;li&gt;A pub/sub message broker&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.diagrid.io/catalyst" rel="noopener noreferrer"&gt;Diagrid Catalyst&lt;/a&gt; as the serverless API layer to connect everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; Check the code on &lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and join the &lt;a href="https://pages.diagrid.io/catalyst-early-access-waitlist" rel="noopener noreferrer"&gt;Catalyst early access program&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagrid Pizza Store
&lt;/h2&gt;

&lt;p&gt;The demo that is used in this article revolves around an online pizza store, where the user selects pizzas, orders them, and follows the progress of the order in realtime. It’s a simplified example where the durable workflow focuses on the inventory of ingredients, and communicating with the kitchen. The source code is &lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo" rel="noopener noreferrer"&gt;on GitHub&lt;/a&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%2Fm7abjviwgbse7hnhc36g.gif" 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%2Fm7abjviwgbse7hnhc36g.gif" alt="Pizza store front-end" width="1200" height="1019"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The solution includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two &lt;a href="http://dapr.io/" rel="noopener noreferrer"&gt;Dapr&lt;/a&gt; web services written in .NET, &lt;em&gt;PizzaOrderService&lt;/em&gt; and &lt;em&gt;KitchenService&lt;/em&gt;. In this demo, they’re running locally, but these can be hosted in any cloud that supports ASP.NET services.&lt;/li&gt;
&lt;li&gt;The website (based on Vue) and two serverless functions written in JavaScript, &lt;em&gt;getAblyToken&lt;/em&gt; and &lt;em&gt;placeOrder&lt;/em&gt;. These are hosted on &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ably.com/" rel="noopener noreferrer"&gt;Ably&lt;/a&gt; as the realtime communication component between the &lt;em&gt;PizzaOrderService&lt;/em&gt; and the website.&lt;/li&gt;
&lt;li&gt;A key/value store to manage inventory (managed by Diagrid).&lt;/li&gt;
&lt;li&gt;A pub/sub message broker to communicate between the &lt;em&gt;PizzaOrderService&lt;/em&gt; and the &lt;em&gt;KitchenService&lt;/em&gt; (managed by Diagrid).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.diagrid.io/catalyst" rel="noopener noreferrer"&gt;Diagrid Catalyst&lt;/a&gt; that offers serverless APIs for communication, state, and workflow powered by Dapr.&lt;/li&gt;
&lt;/ul&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%2Fu1ornquwh30esbeu2k8d.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%2Fu1ornquwh30esbeu2k8d.png" alt="Pizza store architecture" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;PizzaOrderService&lt;/em&gt; contains a Dapr workflow that orchestrates the activities for checking the inventory and communicating with the &lt;em&gt;KitchenService.&lt;/em&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%2Fp1gdgcp6n4hiq9plzxrm.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%2Fp1gdgcp6n4hiq9plzxrm.png" alt="Pizza store workflow" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The workflow also contains activities for sending realtime messages to the website, these have been omitted from the workflow diagram to keep it concise.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Pizza Store locally
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;The following services, tools, and frameworks are required for this demo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.diagrid.io/catalyst" rel="noopener noreferrer"&gt;Diagrid Catalyst account&lt;/a&gt; (&lt;a href="https://pages.diagrid.io/catalyst-early-access-waitlist" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; for early access) and the &lt;a href="https://docs.diagrid.io/catalyst/references/cli-reference/intro" rel="noopener noreferrer"&gt;Diagrid CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://vercel.com/signup" rel="noopener noreferrer"&gt;Vercel account (hobby)&lt;/a&gt; and the &lt;a href="https://vercel.com/docs/cli" rel="noopener noreferrer"&gt;Vercel CLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ably.com/signup" rel="noopener noreferrer"&gt;Ably account (free)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/download/dotnet/8.0" rel="noopener noreferrer"&gt;.NET 8 SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/en/download/" rel="noopener noreferrer"&gt;Node 18&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the code for the website, serverless functions, and back-end services is available in the &lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo" rel="noopener noreferrer"&gt;catalyst-pizza-demo&lt;/a&gt; GitHub repository.  This repository contains a devcontainer configuration that has the following preinstalled: .NET 8, Node LTS, Vercel CLI, Diagrid CLI. &lt;/p&gt;

&lt;p&gt;You can use this devcontainer &lt;a href="https://code.visualstudio.com/docs/devcontainers/containers" rel="noopener noreferrer"&gt;locally in VSCode&lt;/a&gt; (requires &lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;) or directly in &lt;a href="https://github.com/features/codespaces" rel="noopener noreferrer"&gt;GitHub Codespaces&lt;/a&gt;. The &lt;em&gt;npm install&lt;/em&gt; and &lt;em&gt;dotnet build&lt;/em&gt; commands described in this article can be skipped if the devcontainer is used.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo/fork" rel="noopener noreferrer"&gt;Fork&lt;/a&gt; the &lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo" rel="noopener noreferrer"&gt;catalyst-pizza-demo&lt;/a&gt; and clone it locally, or use GitHub Codespaces (recommended).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Ably
&lt;/h3&gt;

&lt;p&gt;Ably is used as the serverless realtime messaging component. There is a default Ably app when you sign up for an account that can be used for this demo. Alternatively, you can create a new Ably app.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Log into the &lt;a href="https://ably.com/accounts/" rel="noopener noreferrer"&gt;Ably portal&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Using the Ably portal: copy the &lt;a href="https://ably.com/docs/ids-and-keys#api-key" rel="noopener noreferrer"&gt;Root API key&lt;/a&gt; from the Ably app. This will be copied later as an environment variable for both Vercel and Diagrid.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Vercel
&lt;/h3&gt;

&lt;p&gt;The Vue-based &lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo/tree/main/front-end/src" rel="noopener noreferrer"&gt;front-end&lt;/a&gt; and two &lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo/tree/main/front-end/api" rel="noopener noreferrer"&gt;JavaScript functions&lt;/a&gt; are hosted on Vercel. The &lt;a href="https://vercel.com/docs/cli" rel="noopener noreferrer"&gt;Vercel CLI&lt;/a&gt; is used to configure and run these resources locally.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal in the root of the repository and login with the Vercel CLI:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vercel login
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go to the &lt;em&gt;front-end&lt;/em&gt; folder and run:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Go back to the root of the repository and set up the Vercel project by running:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vercel
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Follow the CLI prompts, and select the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setup and deploy: &lt;code&gt;Y&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Scope: &lt;code&gt;&amp;lt;account name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Link to existing project: &lt;code&gt;N&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;What's your project's name? &lt;code&gt;catalyst-pizza-project&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;In which directory is your code located? &lt;code&gt;./front-end&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Want to modify these settings? [y/N] &lt;code&gt;n&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Wait for the deployment to complete.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An environment variable is used in the &lt;em&gt;getAblyToken&lt;/em&gt; function to generate a token for the website to communicate with the Ably realtime service. Add the &lt;em&gt;Ably API token&lt;/em&gt; variable by running &lt;code&gt;vercel env add&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variable name: &lt;code&gt;ABLY_API_KEY&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Variable value: &lt;em&gt;Use the Ably API key obtained from the Ably portal earlier&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Development&lt;/code&gt; as the environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Another environment variable is used in the &lt;em&gt;placeOrder&lt;/em&gt; function to send a request to the &lt;em&gt;PizzaOrderService&lt;/em&gt; that will start the workflow. Add the &lt;em&gt;WORKFLOW_URL&lt;/em&gt; variable by running &lt;code&gt;vercel env add&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variable name: &lt;code&gt;WORKFLOW_URL&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Variable value: &lt;code&gt;http://localhost:5064/workflow/orderReceived&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Development&lt;/code&gt; as the environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;vercel pull&lt;/code&gt; to pull the configuration from Vercel to your local environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;vercel build&lt;/code&gt; to build the website and the serverless functions to ensure there are no errors.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Diagrid &lt;strong&gt;Catalyst&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Diagrid Catalyst provides serverless Dapr APIs that enables developers to quickly build distributed applications with workflow, pub/sub messaging, service invocation, and state management capabilities. Diagrid also provides managed infrastructure for storing data in a key/value store, pub/sub messaging, and workflow, which are all used in this solution.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.diagrid.io/catalyst/references/cli-reference/intro" rel="noopener noreferrer"&gt;Diagrid CLI&lt;/a&gt; is used to configure the resources and run the .NET services locally.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open another terminal in the root of the repository and use the Diagrid CLI to log in to Diagrid:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid login
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new Catalyst project named &lt;em&gt;catalyst-pizza-project&lt;/em&gt; and use the Diagrid managed pub/sub broker &amp;amp; KV store, and enable the managed workflow API:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid project create catalyst-pizza-project &lt;span class="nt"&gt;--deploy-managed-pubsub&lt;/span&gt; &lt;span class="nt"&gt;--deploy-managed-kv&lt;/span&gt; &lt;span class="nt"&gt;--enable-managed-workflow&lt;/span&gt; &lt;span class="nt"&gt;--wait&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To set this project as the default in the CLI run:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid project use catalyst-pizza-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new App ID for the &lt;em&gt;PizzaOrderService&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid appid create pizzaorderservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new App ID for the &lt;em&gt;KitchenService&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid appid create kitchenservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Before continuing, check the App ID status to make sure they are ready:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid appid list
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a pub/sub subscription for the &lt;em&gt;KitchenService&lt;/em&gt; to receive messages from the &lt;em&gt;PizzaOrderService&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid subscription create pizzaorderssub &lt;span class="nt"&gt;--connection&lt;/span&gt; pubsub &lt;span class="nt"&gt;--topic&lt;/span&gt; pizza-orders &lt;span class="nt"&gt;--route&lt;/span&gt; /prepare &lt;span class="nt"&gt;--scopes&lt;/span&gt; kitchenservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a pub/sub subscription for the &lt;em&gt;PizzaOrderService&lt;/em&gt; to receive messages from the &lt;em&gt;KitchenService&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid subscription create preparedorderssub &lt;span class="nt"&gt;--connection&lt;/span&gt; pubsub &lt;span class="nt"&gt;--topic&lt;/span&gt; prepared-orders &lt;span class="nt"&gt;--route&lt;/span&gt; /workflow/orderPrepared &lt;span class="nt"&gt;--scopes&lt;/span&gt; pizzaorderservice
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Verify that creation of the subscriptions is completed:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid subscription list
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run &lt;code&gt;diagrid dev scaffold&lt;/code&gt; to create a new local dev environment file. This creates a yaml file, named &lt;em&gt;dev-.yaml&lt;/em&gt; with the following content:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;catalyst-pizza-project&lt;/span&gt;
&lt;span class="na"&gt;apps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kitchenservice&lt;/span&gt;
&lt;span class="na"&gt;appPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_API_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;diagrid://&amp;lt;dapr_api_token&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_APP_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kitchenservice&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_GRPC_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;grpc_endpoint&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_HTTP_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;http_endpoint&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;workDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kitchenservice&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pizzaorderservice&lt;/span&gt;
&lt;span class="na"&gt;appPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_API_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;diagrid://&amp;lt;dapr_api_token&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_APP_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pizzaorderservice&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_GRPC_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;grpc_endpoint&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;DAPR_HTTP_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;http_endpoint&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;workDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pizzaorderservice&lt;/span&gt;
&lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
&lt;span class="na"&gt;appLogDestination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This file is gitignored since it contains secrets from Diagrid Catalyst. The &lt;em&gt;appPort&lt;/em&gt;, &lt;em&gt;command&lt;/em&gt;, and &lt;em&gt;workDir&lt;/em&gt; attributes still need to be updated as follows: &lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Update the &lt;em&gt;appPort&lt;/em&gt; for the &lt;em&gt;kitchenservice&lt;/em&gt; to &lt;code&gt;5066&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Update the &lt;em&gt;appPort&lt;/em&gt; for the &lt;em&gt;pizzaorderservice&lt;/em&gt; to &lt;code&gt;5064&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Update the &lt;em&gt;command&lt;/em&gt; arguments to &lt;code&gt;["dotnet", "run"]&lt;/code&gt; for both apps.&lt;/li&gt;
&lt;li&gt;Update the &lt;em&gt;workDir&lt;/em&gt; argument to point to &lt;code&gt;back-end/KitchenService&lt;/code&gt; and &lt;code&gt;back-end/PizzaOrderService&lt;/code&gt; respectively.&lt;/li&gt;
&lt;li&gt;Update the &lt;em&gt;appLogDestination&lt;/em&gt; to &lt;code&gt;console&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add an &lt;code&gt;ABLY_API_KEY&lt;/code&gt; environment variable for the &lt;em&gt;pizzaorderservice&lt;/em&gt; appId and set the value to the Ably API key obtained from the Ably portal.&lt;/li&gt;
&lt;li&gt;Save the changes to the file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Update the key/value store to allow shared state&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The two .NET services share the same state in the key/value store to manage inventory and orders. Since this is not the default usage of state stores and services when using Dapr, an attribute needs to be set to enable this. The default behavior is that a key is prefixed with the app ID of the service that is using it. In this case, however, the &lt;em&gt;keyPrefix&lt;/em&gt; is set to &lt;em&gt;name&lt;/em&gt; to make sure both services use the same keys.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to update the managed key/value store:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diagrid connection apply &lt;span class="nt"&gt;-f&lt;/span&gt; ./infra/kv.yml
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This will upload the &lt;em&gt;kv.yml&lt;/em&gt; file to Diagrid and update the configuration of the &lt;em&gt;kvstore&lt;/em&gt; connection so &lt;em&gt;keyPrefix&lt;/em&gt; is set to &lt;em&gt;name&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Inspect the DaprClient configuration
&lt;/h3&gt;

&lt;p&gt;The two .NET services use the Dapr .NET client SDK for workflow, state management and pub/sub messaging. The &lt;em&gt;DaprClient&lt;/em&gt; is configured to use the endpoints provided by Diagrid Catalyst in the &lt;em&gt;&lt;a href="https://github.com/diagrid-labs/catalyst-pizza-demo/blob/main/back-end/PizzaOrderService/Program.cs" rel="noopener noreferrer"&gt;Program.cs&lt;/a&gt;&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DAPR_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;grpcEndpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DAPR_GRPC_ENDPOINT"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpEndpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DAPR_HTTP_ENDPOINT"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDaprClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseDaprApiToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseGrpcEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grpcEndpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseHttpEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpEndpoint&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;To use service invocation with the HTTP API, the &lt;em&gt;HttpClient&lt;/em&gt; is configured with the &lt;em&gt;DAPR_APP_ID&lt;/em&gt; and &lt;em&gt;DAPR_API_TOKEN&lt;/em&gt; environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;appId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DAPR_APP_ID"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetEnvironmentVariable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DAPR_API_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"daprEndpoint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BaseAddress&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpEndpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MediaTypeWithQualityHeaderValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dapr-app-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultRequestHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dapr-api-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiToken&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;h3&gt;
  
  
  Running &amp;amp; testing the solution
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal in the root of the repository.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;To restore and build the .NET projects run:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet build ./back-end/PizzaOrderService
dotnet build ./back-end/KitchenService
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;diagrid dev start&lt;/code&gt; to start the &lt;em&gt;PizzaOrderService&lt;/em&gt; and the &lt;em&gt;KitchenService&lt;/em&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Using another terminal in the root of the repository, run &lt;code&gt;vercel dev&lt;/code&gt; to start the website and the serverless functions locally.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Navigate to the URL provided by the Vercel CLI to view the website.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select some pizzas, place an order, and watch the progress of the workflow in realtime.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Using the Catalyst API explorer&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;You can use the API explorer in the Catalyst web UI to interact with the APIs. If you want to retrieve the order item from the key/value store that has just been processed, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the developer tools console of the browser that is running the demo (a pizza order must be started or completed).&lt;/li&gt;
&lt;li&gt;The order is logged to the console as a JSON object. Copy the &lt;em&gt;OrderId&lt;/em&gt; property value.
&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%2F82q8tifruveh69ym3ek1.png" alt="Console order" width="800" height="199"&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to the &lt;a href="https://catalyst.diagrid.io/" rel="noopener noreferrer"&gt;Catalyst web UI&lt;/a&gt;. Ensure you're in the &lt;em&gt;catalyst-pizza-project&lt;/em&gt; project.
&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%2Fg58vyhxqrhxj63rntjp0.png" alt="Catalyst API explorer" width="800" height="557"&gt;
&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;App IDs&lt;/em&gt; in and click on the &lt;em&gt;&lt;a href="https://catalyst.diagrid.io/app-ids/api-explorer/state-api/" rel="noopener noreferrer"&gt;API explorer&lt;/a&gt;&lt;/em&gt; tab.

&lt;ul&gt;
&lt;li&gt;Select the &lt;em&gt;State API&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Select the &lt;em&gt;pizzaorderservice&lt;/em&gt; as the app ID.&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;GET&lt;/em&gt; as the API operation.&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;kvstore&lt;/em&gt; as the state connection.&lt;/li&gt;
&lt;li&gt;Enter &lt;em&gt;Order-&lt;/em&gt; as the key, where &lt;em&gt;&lt;/em&gt; is substituted with the value copied from the developer tools console.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Send&lt;/em&gt;. The response should show the state of the order item.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What’s next?
&lt;/h2&gt;

&lt;p&gt;Congratulations! You’ve now used the Diagrid Catalyst serverless Dapr APIs for workflow, pub/sub messaging, service invocation, and state management. The ability to use these APIs from anywhere, without the overhead of managing Kubernetes clusters, brings great flexibility to developers on any platform to build distributed applications. The Dapr applications used in this demo can be hosted on any cloud. The demo uses the Diagrid managed infrastructure for key/value storage and pub/sub messaging, but these can be swapped out for other cloud-based resources, similarly to switching open-source Dapr components. You can extend this demo with an alternative pub/sub broker or state store by configuring other &lt;a href="https://docs.diagrid.io/catalyst/concepts/connections" rel="noopener noreferrer"&gt;infrastructure connections&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Any questions or comments about this demo? Join the &lt;a href="https://bit.ly/dapr-discord" rel="noopener noreferrer"&gt;Dapr Discord&lt;/a&gt; and drop us a message in the #diagrid channel!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>dapr</category>
      <category>tutorial</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Dapr as the Ultimate Microservices Patterns Framework</title>
      <dc:creator>Bilgin Ibryam</dc:creator>
      <pubDate>Wed, 27 Sep 2023 09:06:20 +0000</pubDate>
      <link>https://dev.to/diagrid/dapr-as-the-ultimate-microservices-patterns-framework-20bj</link>
      <guid>https://dev.to/diagrid/dapr-as-the-ultimate-microservices-patterns-framework-20bj</guid>
      <description>&lt;p&gt;In the world of software development, microservices architecture stands out as the industry benchmark. This architectural style segments applications into distinct services, each deployable independently and organized around specific business capabilities. Such a design ensures flexibility, scalability, and resilience, with each service typically overseen by a specialized team. However, while this approach offers numerous benefits, it also introduces complexities. To navigate these challenges, developers turn to patterns, drawing parallels to time-tested design patterns from traditional software development, providing solutions for building robust distributed systems. For those seeking a comprehensive guide on these patterns, Chris Richardson’s &lt;a href="https://microservices.io/patterns/index.html" rel="noopener noreferrer"&gt;microservices.io&lt;/a&gt; stands as a trusted repository, rich with insights and best practices from industry experts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Feedd9afboxcjv3xznf5n.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Feedd9afboxcjv3xznf5n.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A pattern language for microservices by Chris Richardson&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, understanding patterns is just one part of the equation. Implementing them in real-world scenarios requires tools and frameworks. Positioned as a premier microservices chassis, &lt;a href="http://dapr.io/" rel="noopener noreferrer"&gt;Dapr&lt;/a&gt; is crafted for creating distributed applications that are secure, resilient, scalable, and observable. It doesn't merely align with the microservices patterns; it amplifies their potential, refining and simplifying their real-world implementation. &lt;/p&gt;

&lt;p&gt;In the subsequent sections, I'll go through the patterns outlined in microservices.io, shedding light on how Dapr helps implement each. While numerous frameworks aim to address the cross-cutting concerns inherent in microservices, Dapr distinguishes itself. Its polyglot nature, sidecar operational mode, and non-restrictive stance on application architecture make it a unique and invaluable asset in the microservices toolkit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Microservice Chassis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When embarking on the development of an application, developers often find themselves investing significant time in addressing cross-cutting concerns such as security, externalized configuration, logging, health checks, metrics, and distributed tracing. While these elements might seem straightforward, defining a curated set of dependencies that will be used in tens or hundreds of services can be a complex endeavor. The challenge amplifies when adopting a microservices architecture, given the multitude of services and the frequent creation of new ones. The &lt;a href="https://microservices.io/patterns/microservice-chassis.html" rel="noopener noreferrer"&gt;Microservice Chassis&lt;/a&gt; pattern offers a solution by proposing the creation of a framework that serves as the foundation for microservices development. This chassis provides reusable build logic and mechanisms to handle these cross-cutting concerns, streamlining the development process.&lt;/p&gt;

&lt;p&gt;As a polyglot framework, Dapr seamlessly handles cross-cutting concerns, allowing developers to focus on core functionalities without getting entangled in complexities. It provides built-in mechanisms for security, configuration management, logging, and more. With features like the Access Control capabilities for security, Configuration API for externalized settings, and health API endpoints for monitoring, Dapr ensures that these foundational concerns are seamlessly integrated, allowing developers to focus on core business logic without getting mired in the intricacies of these concerns. Unlike other frameworks, Dapr doesn't impose constraints on the application, granting developers the freedom to choose their preferred language, runtime, and programming style. In essence, Dapr transforms the theoretical benefits of the Microservice Chassis pattern into tangible results for real-world applications.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sidecar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In microservices, there's often a need to augment services with additional capabilities without modifying the core service logic. The &lt;a href="https://microservices.io/patterns/deployment/sidecar.html" rel="noopener noreferrer"&gt;Sidecar pattern&lt;/a&gt; addresses this requirement. It involves deploying components of an application into separate processes or containers to provide a modular and scalable architecture. The main service runs in one container, and the sidecar service, which extends or enhances the main service, runs in a separate container but in the same network namespace. This ensures that the main service and the sidecar can communicate as if they are in the same process while being isolated from each other. The primary advantage of this pattern is the ability to separate concerns, modularize your application, and ensure that each component is focused on a specific responsibility.&lt;/p&gt;

&lt;p&gt;Dapr is among the most popular implementations of the Sidecar pattern. When integrated into a microservices environment, Dapr runs as a sidecar alongside your service, providing a plethora of additional capabilities without requiring any changes to the main service. This includes features like state management, service-to-service invocation, pub/sub messaging, and more. By leveraging Dapr's sidecar architecture, developers can augment their services with powerful features, ensuring a robust, scalable, and feature-rich microservices ecosystem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Mesh&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As the number of services grows, managing inter-service communication, security, and observability becomes increasingly complex. A Service Mesh is a dedicated infrastructure layer built to handle service-to-service communication in a transparent and technology-agnostic manner. It provides features like load balancing, service discovery, observability, and security without requiring changes to the application code. By offloading these concerns to the Service Mesh, developers can focus on building business logic, while the mesh ensures that services can securely and efficiently communicate with each other.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fja2mwgi1o8lmnzuli8wa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fja2mwgi1o8lmnzuli8wa.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;How Dapr and service meshes compare&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dapr operates as a &lt;a href="https://docs.dapr.io/concepts/service-mesh/" rel="noopener noreferrer"&gt;lightweight service mesh&lt;/a&gt;, providing a network layer that facilitates service discovery and ensures secure service-to-service interactions. While there are overlapping capabilities between Dapr and traditional service meshes, Dapr distinguishes itself by being developer-centric, focusing on building blocks that simplify microservices development. Unlike service meshes which are primarily infrastructure-centric and deal with network concepts like IP and DNS addresses, Dapr offers service discovery and invocation via names, a more developer-friendly approach. Beyond the common features like mTLS encryption, metric collection, and distributed tracing, Dapr introduces application-level building blocks for state management, pub/sub messaging, actors, and more. This ensures that developers gain a comprehensive toolset, not just for networking, but for holistic microservices development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Saga&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the world of microservices, ensuring data consistency across services can be a challenge, especially when each service has its own database. The Saga pattern provides a solution to this challenge. Instead of relying on traditional distributed transactions, the &lt;a href="https://microservices.io/patterns/data/saga.html" rel="noopener noreferrer"&gt;Saga pattern&lt;/a&gt; breaks the transaction into a series of local transactions, each executed within its own service and database. These local transactions are coordinated in a specific sequence to ensure overall data consistency. If one local transaction fails, compensating transactions are executed to revert the changes made by the previous transactions. This approach offers a way to maintain data consistency without the need for distributed transactions, which are often not feasible in microservices architectures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F4iu1ug9pew2vj7uht52s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F4iu1ug9pew2vj7uht52s.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Dapr workflow overview&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dapr offers a concrete solution to implement the Saga pattern through its Workflow API. This API allows developers to sequence local transactions, or implement other &lt;a href="https://www.diagrid.io/blog/in-depth-guide-to-dapr-workflow-patterns" rel="noopener noreferrer"&gt;stateful workflow patterns&lt;/a&gt; ensuring that data remains consistent across services. &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/" rel="noopener noreferrer"&gt;Dapr Workflow API&lt;/a&gt; serves as a foundational tool in this regard, streamlining the process and ensuring reliability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Transactional Outbox&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In microservices architectures, a common challenge arises when a service command needs to update aggregates in the database and simultaneously send messages or events to a message broker. The goal is to ensure atomicity - if the database transaction commits, the messages must be sent; if the database rolls back, the messages must not be sent. Traditional distributed transactions (2PC) are often not feasible or desirable due to various constraints. The &lt;a href="https://www.infoq.com/articles/saga-orchestration-outbox/" rel="noopener noreferrer"&gt;Transactional Outbox&lt;/a&gt; pattern addresses this issue. It suggests that the service stores the message in the database as part of the transaction that updates the business entities. A separate process then retrieves and sends these messages to the message broker. This ensures that messages are sent only if the database transaction commits, preserving data consistency and order of operations.&lt;/p&gt;

&lt;p&gt;Dapr provides a robust solution to this challenge with its &lt;a href="https://github.com/dapr/dapr/issues/4233" rel="noopener noreferrer"&gt;Outbox feature&lt;/a&gt; in StateStore API. This feature allows for atomic updates to the database while also sending messages to the designated broker. By utilizing the StateStore API, developers can seamlessly integrate the Transactional Outbox pattern into their microservices, ensuring data consistency and reliable message delivery across large number of databases and message brokers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Messaging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the realm of microservices, reliable asynchronous communication between services is paramount. Instead of services communicating directly with synchronous calls, they exchange messages via message channels. This &lt;a href="https://microservices.io/patterns/communication-style/messaging.html" rel="noopener noreferrer"&gt;asynchronous mode&lt;/a&gt; of communication decouples services, allowing them to operate independently. It ensures that even if one service is slow or unavailable, others can continue their operations without being directly affected. This approach enhances the system's resilience, scalability, and flexibility. &lt;/p&gt;

&lt;p&gt;Dapr's &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/pubsub/howto-publish-subscribe/" rel="noopener noreferrer"&gt;PubSub API&lt;/a&gt; is tailored to harness the power of asynchronous messaging for inter-service communication. By leveraging this API, developers can easily implement messaging patterns in their microservices architecture. The PubSub API ensures reliable message delivery, supports multiple messaging brokers, and abstracts the complexities of direct broker interactions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request/Reply Interaction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://microservices.io/patterns/communication-style/rpi.html" rel="noopener noreferrer"&gt;Remote Procedure Invocation&lt;/a&gt; (RPI) is a communication style that enables services in a microservices architecture to communicate with each other by invoking methods in a remote service. The primary advantage of RPI is its straightforwardness, allowing for direct, point-to-point communication between services. However, it's essential to manage the associated challenges, such as service discovery, reliability, encryption, to ensure the system remains resilient and secure.&lt;/p&gt;

&lt;p&gt;Dapr addresses the challenges with its &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/" rel="noopener noreferrer"&gt;Service Invocation API&lt;/a&gt;. This API provides an RPI-based protocol tailored for inter-service communication in a microservices setup. By abstracting the underlying complexities, Dapr ensures that services can communicate synchronously without getting entangled in the intricacies of direct service-to-service calls. Moreover, Dapr's Service Invocation API offers built-in features like retries, error handling, and traffic control, ensuring that communications are both reliable and secure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Circuit Breaker&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a microservices architecture, services often collaborate to handle requests. However, when one service synchronously invokes another, there's a risk that the called service might be unavailable or might exhibit high latency, rendering it essentially unusable. Such scenarios can lead to resource exhaustion in the calling service, making it unable to handle other requests. This can further cascade the failure to other services throughout the application. The &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker" rel="noopener noreferrer"&gt;Circuit Breaker pattern&lt;/a&gt; addresses this challenge. It functions similarly to an electrical circuit breaker. When consecutive failures cross a certain threshold, the circuit breaker "trips." For a set timeout period, all attempts to invoke the problematic service fail immediately. After this period, the circuit breaker allows a few test requests. If these succeed, normal operation resumes; if not, the timeout period restarts.&lt;/p&gt;

&lt;p&gt;Dapr offers a concrete solution to this challenge with its &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/#resilience" rel="noopener noreferrer"&gt;Resiliency policy&lt;/a&gt;. This policy ensures that when the failure rate of a call exceeds a certain threshold, the call fails immediately, preventing resource exhaustion and potential cascading failures. By leveraging Dapr's resiliency policy, developers can implement the Circuit Breaker pattern efficiently, ensuring that their microservices architecture remains robust and resilient against unexpected service failures or latencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Access Token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the intricate landscape of microservices, ensuring secure communication and access between services is paramount. The &lt;a href="https://microservices.io/patterns/security/access-token.html" rel="noopener noreferrer"&gt;Access Token pattern&lt;/a&gt; involves issuing tokens to clients, granting them limited access to a service. These tokens encapsulate the information required to determine whether a client is authorized to perform a given operation. The primary advantage of using access tokens is that they provide a way to ensure that only authenticated and authorized clients can access services or specific operations within those services. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fe6ivoxf3oet988fk46qx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fe6ivoxf3oet988fk46qx.png" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Dapr secure communications architecture&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dapr provides a robust mechanism to implement this pattern through its &lt;a href="https://docs.dapr.io/operations/configuration/invoke-allowlist/" rel="noopener noreferrer"&gt;Access Control&lt;/a&gt; capabilities based on &lt;a href="https://spiffe.io/" rel="noopener noreferrer"&gt;SPIFFE  Ids&lt;/a&gt;. With Dapr's Access Control, developers can define and enforce policies that restrict what operations calling applications can perform on the called app. This ensures a fine-grained control over service interactions, making the system more secure and resilient against potential threats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Instance per Container&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://microservices.io/patterns/deployment/service-per-container.html" rel="noopener noreferrer"&gt;Service Instance per Container pattern&lt;/a&gt; deployment strategy, places each service instance in its own container. Containers, being lightweight and isolated, provide an environment where the service can run with its dependencies, ensuring consistency across different stages of deployment. This approach offers several benefits: it ensures isolation, making each service instance independent of others; it provides scalability, as new instances can be spun up quickly; and it enhances &lt;a href="https://www.diagrid.io/blog/practical-portability-principles" rel="noopener noreferrer"&gt;portability&lt;/a&gt;, as each service containts its built-time dependencies.&lt;/p&gt;

&lt;p&gt;Dapr aligns perfectly with this deployment model as it  is designed to operate best in containerized environments. When a service is deployed with Dapr, a Dapr sidecar container runs alongside the service container, enhancing its capabilities without intruding into the service's operations. &lt;a href="https://docs.dapr.io/operations/hosting/kubernetes/kubernetes-overview/" rel="noopener noreferrer"&gt;Dapr's deployment model&lt;/a&gt;, ensures that each service instance, along with its Dapr sidecar, remains isolated in its container, benefiting from the inherent advantages of the Service Instance per Container pattern such as scalable, and resilient microservices deployments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Instance per VM&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In certain deployment scenarios, especially when dealing with large-scale applications or when containers might not be the optimal choice, deploying each service instance on its own Virtual Machine (VM) becomes a viable strategy. The &lt;a href="https://microservices.io/patterns/deployment/service-per-vm.html" rel="noopener noreferrer"&gt;Service Instance per VM pattern&lt;/a&gt; emphasizes this approach. By allocating a dedicated VM for each service instance, you ensure that the service has a dedicated set of resources, leading to predictable performance. This isolation also means that failures or resource contention in one service won't directly impact others. Moreover, VMs provide a higher degree of isolation compared to containers, which can be crucial for certain security or compliance requirements.&lt;/p&gt;

&lt;p&gt;Dapr is versatile and can be seamlessly deployed in a VM-based environment. Whether you're deploying Dapr on its own dedicated VM or using &lt;a href="https://github.com/dapr-sandbox/dapr-ambient" rel="noopener noreferrer"&gt;Dapr Ambient&lt;/a&gt; to share its capabilities among multiple Pods, Dapr ensures that microservices can communicate and operate efficiently. This flexibility means that developers aren't confined to containerized environments and can leverage Dapr's capabilities in VM-based deployments, ensuring that the benefits of Dapr, such as state management, service invocation, and pub/sub messaging, are available regardless of the deployment strategy. &lt;a href="https://docs.dapr.io/operations/hosting/" rel="noopener noreferrer"&gt;Dapr's deployment documentation&lt;/a&gt; provides insights into how it can be integrated into various environments, including VMs, or &lt;a href="https://twitter.com/daprdev/status/1529862546789785602" rel="noopener noreferrer"&gt;the real edge&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Discovery&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In microservices architectures, the dynamic nature of service instances and their locations, especially in containerized environments, presents a challenge: how does a client of a service discover the location of a service instance? Two prevalent patterns address this challenge: &lt;a href="https://microservices.io/patterns/client-side-discovery.html" rel="noopener noreferrer"&gt;Client-side Discovery&lt;/a&gt; and &lt;a href="https://microservices.io/patterns/server-side-discovery.html" rel="noopener noreferrer"&gt;Server-side Discovery&lt;/a&gt;. The former involves clients querying a Service Registry to discover the current locations of service instances, ensuring they always communicate with available and healthy instances. On the other hand, the Server-side Discovery pattern simplifies client code by routing requests via a knowledgeable router, often a load balancer, which interacts with the service registry.&lt;/p&gt;

&lt;p&gt;Dapr's sidecar architecture adeptly addresses both these patterns. While the sidecar operates alongside a service, akin to a client, it isn't embedded within the application. This unique positioning allows it to query a service registry, discovering other service instances' locations, and also act as a router for inter-service calls. By offloading the intricacies of service discovery to the Dapr &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/service-invocation/service-invocation-overview/" rel="noopener noreferrer"&gt;Service Invocation API&lt;/a&gt;, developers can ensure reliable service-to-service communication, even in environments where service locations change dynamically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service Registry&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In dynamic microservices environments, the locations and number of service instances can frequently change. This poses a challenge: how can clients or routers be aware of the current available instances of a service? The &lt;a href="https://microservices.io/patterns/service-registry.html" rel="noopener noreferrer"&gt;Service Registry pattern&lt;/a&gt; offers a solution. It proposes a centralized registry where service instances register themselves upon startup and deregister upon shutdown. This registry acts as a database of services, their instances, and their locations. Clients or routers can then query this registry to discover the current locations of service instances. &lt;/p&gt;

&lt;p&gt;Dapr offers a seamless integration with the Service Registry concept, providing a unified interface to various service registry implementations. Dapr's pluggable &lt;a href="https://docs.dapr.io/reference/components-reference/supported-name-resolution/" rel="noopener noreferrer"&gt;name resolution&lt;/a&gt; components used in Service Invocation API cater to diverse hosting platforms, from Kubernetes, which utilizes its DNS service, to self-hosted machines using mDNS or even HashiCorp's Consul in varied environments. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Self Registration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://microservices.io/patterns/self-registration.html" rel="noopener noreferrer"&gt;Self Registration pattern&lt;/a&gt; ensures that services can discover each other in  inter-service communication. When a service instance starts up, instead of relying on an external agent or system to register it with a service registry, the service instance itself takes the responsibility of registering. This ensures that the service registry always has the most up-to-date information about available service instances. By automating the registration process, the Self Registration pattern reduces manual intervention, potential errors, and ensures that the service registry is always current.&lt;/p&gt;

&lt;p&gt;When a service with a Dapr sidecar is deployed, the Dapr sidecar takes the initiative to register itself with the service registry. This automated process ensures that the service is immediately discoverable by other services in the ecosystem. This not only simplifies the deployment process but also enhances the reliability and efficiency of service-to-service communication in architectures built with Dapr.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3rd Party Registration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In some microservices architectures, not all services or endpoints are created or managed by the same team or entity. There might be third-party services or endpoints that need to be integrated into the system. These third-party services might not follow the same registration patterns as internal services. With the &lt;a href="https://microservices.io/patterns/3rd-party-registration.html" rel="noopener noreferrer"&gt;3rd Party Registration pattern&lt;/a&gt;, instead of the service registering itself (as in Self Registration), an external agent or system is responsible for registering the service with the service registry. This ensures that third-party services, which might not have the capability or permission to register themselves, are still discoverable and can be integrated seamlessly into the system.&lt;/p&gt;

&lt;p&gt;Dapr offers flexibility in this regard too. Even &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/service-invocation/howto-invoke-non-dapr-endpoints/" rel="noopener noreferrer"&gt;non-Dapr 3rd party endpoints&lt;/a&gt; can be registered within the Dapr runtime, ensuring they benefit from service discovery, resiliency, and observability features that Dapr provides. This means that developers can integrate third-party services into their Dapr-enabled microservices architecture without those services being Dapr-aware. This allows a more cohesive and resilient microservices ecosystem, irrespective of the origin of the services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Externalized Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the world of microservices, applications often interact with various infrastructure and third-party services. Examples include service registries, message brokers, databases, payment processors, and more. A significant challenge arises when trying to ensure that a service can run across multiple environments (like dev, test, staging, production) without any modifications. The &lt;a href="https://microservices.io/patterns/externalized-configuration.html" rel="noopener noreferrer"&gt;Externalized Configuration pattern&lt;/a&gt; recommends externalizing all application configurations. This ensures that the service remains environment-agnostic and can adapt to different setups without any code changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8nhlz8oe3fpin93xrktk.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8nhlz8oe3fpin93xrktk.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Dapr secrets stores overview&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dapr provides a robust solution to this challenge with its &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/secrets/secrets-overview/" rel="noopener noreferrer"&gt;Secrets and Configuration APIs&lt;/a&gt;. These APIs allow developers to externalize configurations, including sensitive information like database credentials. Instead of hardcoding configurations or placing them in easily accessible files, Dapr ensures that they are securely stored and can be fetched dynamically when needed. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Health Check API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a microservices architecture, ensuring the health, availability, and self-healing of service instances is paramount. The &lt;a href="https://microservices.io/patterns/observability/health-check-api.html" rel="noopener noreferrer"&gt;Health Check API pattern&lt;/a&gt; proposes that each service should expose an API endpoint (e.g., HTTP /health) that indicates the health status of the service. This endpoint performs various checks, such as the status of connections to infrastructure services, the health of the host (e.g., disk space), and any application-specific logic. By periodically querying this endpoint, monitoring systems, service registries, or load balancers can determine the health of a service instance. This ensures that alerts are generated for unhealthy instances, and requests are only routed to healthy service instances, enhancing the reliability and efficiency of the system.&lt;/p&gt;

&lt;p&gt;Dapr elevates the health check pattern by conducting periodic health checks on your service, ensuring its optimal functioning. Once  &lt;a href="https://docs.dapr.io/operations/resiliency/health-checks/app-health/" rel="noopener noreferrer"&gt;app health checks&lt;/a&gt; are enabled, the Dapr sidecar routinely polls the application. If a health issue is detected, Dapr takes proactive measures: it unsubscribes from all pub/sub subscriptions, halts all input bindings, and short-circuits service-invocation requests, ensuring they aren't forwarded to the application. This comprehensive approach ensures that any potential issues are swiftly identified and mitigated, fostering a robust and resilient system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distributed Tracing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In complex microservices architectures, understanding the flow of requests across multiple services can be challenging. The &lt;a href="https://microservices.io/patterns/observability/distributed-tracing.html" rel="noopener noreferrer"&gt;Distributed Tracing pattern&lt;/a&gt; involves instrumenting services with code that assigns each external request a unique identifier. This identifier is then passed to all services involved in handling the request. By doing so, it becomes possible to trace the journey of a request across various services, recording information such as start time, end time, and other relevant metrics providing invaluable insights into the behavior of the system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fs6243nigohsy948empa4.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fs6243nigohsy948empa4.jpeg" alt="Image description"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Dapr distributed tracing overview&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dapr automatically takes care of creating trace headers and ensures they are captured and forwarded appropriately. With Dapr’s &lt;a href="https://docs.dapr.io/operations/observability/tracing/tracing-overview/" rel="noopener noreferrer"&gt;observability capabilities&lt;/a&gt;, developers don't need to manually instrument their code for tracing; Dapr handles it seamlessly. Moreover, Dapr integrates with popular tracing systems, ensuring that the traces can be visualized and analyzed in a centralized manner. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application Metrics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://microservices.io/patterns/observability/application-metrics.html" rel="noopener noreferrer"&gt;Application Metrics&lt;/a&gt; are a set of quantitative data points that provide insights into the performance, behavior, and health of an application or service. By collecting and analyzing these metrics, developers and operations teams can identify bottlenecks, detect anomalies, and optimize the performance of their services, enabling proactive monitoring and ensuring optimal system health.&lt;/p&gt;

&lt;p&gt;Dapr automatically gathers a wide range of &lt;a href="https://docs.dapr.io/operations/observability/metrics/metrics-overview/" rel="noopener noreferrer"&gt;networking metrics&lt;/a&gt;, capturing data related to request rates, error rates, and latency, among others. Dapr ensures that these metrics are delivered to a centralized metrics service, allowing for comprehensive monitoring and analysis. This means that developers and operations teams can have a unified view of the system's performance, irrespective of the number of services or their complexity. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Serverless Deployment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the evolving landscape of software development, the need for scalable, and cost-effective deployment solutions is growing. &lt;a href="https://microservices.io/patterns/deployment/serverless-deployment.html" rel="noopener noreferrer"&gt;Serverless Deployment&lt;/a&gt; refers to a deployment infrastructure that abstracts away any concept of servers, be it physical, virtual hosts, or containers. The primary advantage is that developers can focus solely on their code, without concerning themselves with the underlying infrastructure. The serverless platform automatically scales services based on the load, ensuring optimal resource utilization.&lt;/p&gt;

&lt;p&gt;Dapr is gearing up for the &lt;a href="https://www.diagrid.io/blog/evolution-of-cloud-computing" rel="noopener noreferrer"&gt;serverless era&lt;/a&gt; too. Soon, developers will be able to access Dapr's rich capabilities as serverless APIs. This means that the vast array of features Dapr offers, from state management to messaging, will be available in a serverless context, ensuring developers get the best of both worlds. By integrating Dapr into serverless environments, developers can ensure more robust, scalable, and feature-rich applications without the complexities of managing the Dapr infrastructure. For the updates on this pattern, follow &lt;a href="https://twitter.com/diagridio" rel="noopener noreferrer"&gt;@diagridio&lt;/a&gt; on Twitter and you can be among the first to try it out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Patterns play a pivotal role in software development, serving as a shared language to communicate common challenges and best practices. They encapsulate proven solutions to recurring problems, ensuring that developers don't have to reinvent the wheel with each new project. However, while patterns provide a conceptual blueprint, they remain abstract ideas. Historically, the challenges of building reliable applications and the implementations of these patterns were addressed using application servers, service buses, and microservices frameworks like Spring Cloud, among other language-specific solutions. But to truly bring these patterns to life in the modern era, we need cloud-native frameworks and chassis like Dapr. Dapr tackles these common engineering challenges by presenting patterns as polyglot APIs, aligning with the cloud-native philosophy and &lt;a href="https://www.diagrid.io/blog/dapr-as-a-10x-platform" rel="noopener noreferrer"&gt;offering benefits&lt;/a&gt; to the whole organization. This ensures that developers can leverage best practices across multiple languages and platforms, streamlining the development process and enhancing application resilience and scalability.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Top 20 Must-Read Software Trends Reports for 2023</title>
      <dc:creator>Bilgin Ibryam</dc:creator>
      <pubDate>Thu, 20 Jul 2023 09:14:09 +0000</pubDate>
      <link>https://dev.to/diagrid/top-20-must-read-software-trends-reports-for-2023-2bf3</link>
      <guid>https://dev.to/diagrid/top-20-must-read-software-trends-reports-for-2023-2bf3</guid>
      <description>&lt;p&gt;In the rapidly evolving software industry, keeping up with new trends, tools, and best practices can be time-consuming. With so much information available, where do you start, and what sources can you trust? I've curated a list of reports that I follow to stay informed and ahead of the curve. These provide insights into everything from programming languages to DevOps, cloud strategy, and security. If you're interested in the latest trends and fascinating posts I come across, &lt;a href="https://twitter.com/bibryam" rel="noopener noreferrer"&gt;follow me&lt;/a&gt; or check out my latest writing on industry trends over at the Diagrid &lt;a href="https://www.diagrid.io/blog" rel="noopener noreferrer"&gt;blog&lt;/a&gt;. I share anything I find insightful and worth reading in the world of cloud and distributed systems. &lt;/p&gt;

&lt;p&gt;Here are the top 20 reports for 2023 (in no particular order) I came across so far:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.tiobe.com/tiobe-index/" rel="noopener noreferrer"&gt;Programming Community Index for June 2023 - TIOBE
&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://redmonk.com/sogrady/2023/05/16/language-rankings-1-23/" rel="noopener noreferrer"&gt;Programming Language Rankings - January 2023 - RedMonk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://survey.stackoverflow.co/2023/" rel="noopener noreferrer"&gt;Developer Survey 2023 - Stack Overflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.infoq.com/articles/cloud-devops-trends-2023/" rel="noopener noreferrer"&gt;DevOps and Cloud Trends Report – July 2023 - InfoQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.infoq.com/podcasts/architecture-trends-report-2023/" rel="noopener noreferrer"&gt;Software Architecture &amp;amp; Design Trends 2023 - InfoQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postman.com/state-of-api/" rel="noopener noreferrer"&gt;State of the API Report 2023 - Postman&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.datadoghq.com/state-of-application-security/" rel="noopener noreferrer"&gt;State of Application Security Report 2023 - DataDog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thoughtworks.com/radar/platforms/dapr" rel="noopener noreferrer"&gt;Technology Radar Vol 28 - Thoughtworks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learning.oreilly.com/library/view/radar-trends-to/9781098156527/ch01.html" rel="noopener noreferrer"&gt;Radar Trends to Watch: June 2023 - O'Reilly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.hashicorp.com/state-of-the-cloud" rel="noopener noreferrer"&gt;State of Cloud Strategy Survey 2023 - HashiCorp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.redhat.com/en/resources/state-kubernetes-security-report-2023" rel="noopener noreferrer"&gt;State of Kubernetes Security Report 2023 - Red Hat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.puppet.com/resources/state-of-platform-engineering" rel="noopener noreferrer"&gt;The State of Platform Engineering Report - PuppetLabs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tanzu.vmware.com/content/ebooks/stateofkubernetes-2023" rel="noopener noreferrer"&gt;State of Kubernetes 2023 - Vmware&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://deno.com/blog/state-of-edge-functions-2023" rel="noopener noreferrer"&gt;The State of Edge Functions 2023 - Deno&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://state-of-data.com/" rel="noopener noreferrer"&gt;State of Data 2023 - AirByte&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.databricks.com/discover/state-of-data-ai" rel="noopener noreferrer"&gt;State of Data + AI 2023 - Databricks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://octoverse.github.com/" rel="noopener noreferrer"&gt;The State of Open Source Software 2022 - Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stateofapis.com/" rel="noopener noreferrer"&gt;State of APIs 2022 - RapidAPI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cncf.io/reports/cncf-annual-survey-2022/" rel="noopener noreferrer"&gt;CNCF Annual Survey 2022 - CNCF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/products/devops-sre/dora-2022-accelerate-state-of-devops-report-now-out" rel="noopener noreferrer"&gt;State of DevOps Report 2022 - Google&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While these reports offer valuable insights, it's important to keep in mind that they can be opinionated. The key to effectively leveraging these resources lies in cross-verifying trends from multiple sources and using them only as a guide for direction rather than absolute truths. &lt;/p&gt;

&lt;p&gt;Are there any reports that should be on this list? Tag me on Twitter and I'll include them, subject to my checks  I'm always keen to explore new sources! Found this list helpful? Go ahead, &lt;a href="https://twitter.com/intent/tweet?text=Top%2020%20Must-Read%20Software%20Trends%20Reports%20by%20%40bibryam&amp;amp;url=https%3A%2F%2Fwww.ofbizian.com%2F2023%2F07%2Ftop-20-must-read-software-reports.html" rel="noopener noreferrer"&gt;share it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Call to action: Are you a &lt;a href="https://dapr.io/" rel="noopener noreferrer"&gt;Dapr&lt;/a&gt; user? Your experience is valuable! Contribute your insights and shape the &lt;a href="https://22146261.hs-sites.com/state-of-dapr-2023-survey?utm_medium=social&amp;amp;utm_source=twitter" rel="noopener noreferrer"&gt;State of Dapr Report&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cloud</category>
      <category>kubernetes</category>
      <category>data</category>
    </item>
    <item>
      <title>Dapr v1.11 Release Highlights</title>
      <dc:creator>Marc Duiker</dc:creator>
      <pubDate>Mon, 19 Jun 2023 12:27:24 +0000</pubDate>
      <link>https://dev.to/diagrid/dapr-v111-release-highlights-46b</link>
      <guid>https://dev.to/diagrid/dapr-v111-release-highlights-46b</guid>
      <description>&lt;p&gt;The Dapr maintainers released a new version of &lt;strong&gt;&lt;a href="https://dapr.io/"&gt;Dapr, the distributed application runtime&lt;/a&gt;&lt;/strong&gt;, last week. This post highlights the major new features and changes for the APIs and components in release 1.11.&lt;/p&gt;

&lt;h2&gt;
  
  
  APIs
&lt;/h2&gt;

&lt;p&gt;Dapr provides a common set of APIs for building microservices quickly and reliably. The new building block APIs, and major upgrades to existing APIs, are described in this section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuration API stable
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VdpW4zpr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dy7r8v52ctan8ncjfopp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VdpW4zpr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dy7r8v52ctan8ncjfopp.png" alt="Dapr Configuration API" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/configuration/configuration-api-overview/"&gt;Configuration building block&lt;/a&gt; allows developers to easily consume application configuration values as key/value pairs. Use this API to retrieve values that are likely to change over time but are coupled to the service. For instance, this API can be used to get App IDs, which are used when invoking other Dapr services. Or to get a component name of a state store when using the State Management API.&lt;/p&gt;

&lt;p&gt;Applications can subscribe to changes of these configuration values, so the apps always use the latest value. The Configuration API was introduced in Dapr v1.5, and it’s great to see it now stable in v1.11.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get a configuration value&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;GET http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0/configuration/&lt;span class="nt"&gt;&amp;lt;storeName&amp;gt;&lt;/span&gt;?key=&lt;span class="nt"&gt;&amp;lt;keyName&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Subscribe to configuration changes&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;GET http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0/configuration/&lt;span class="nt"&gt;&amp;lt;storeName&amp;gt;&lt;/span&gt;/subscribe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Service Invocation for non-Dapr endpoints 🆕
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SdrKIgp7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odkcnl3ywq1xawm8k1gb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SdrKIgp7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/odkcnl3ywq1xawm8k1gb.png" alt="Non-Dapr service invocation" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An exciting new preview feature is the ability to invoke &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/service-invocation/howto-invoke-non-dapr-endpoints/"&gt;non-Dapr endpoints&lt;/a&gt;. This feature enables developers to apply resiliency policies, access control via scoping, header authentication, and middleware to any endpoint. This gives you nearly all the benefits of service invocation when calling any HTTP API. Invoking non-Dapr endpoints can also be very useful when working on existing code bases where Dapr can be introduced incrementally.&lt;/p&gt;

&lt;p&gt;Non-Dapr endpoints can be invoked in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Using an &lt;code&gt;HTTPEndpoint&lt;/code&gt; resource type.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;GET http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0/invoke/&lt;span class="nt"&gt;&amp;lt;HTTPEndpoint-name&amp;gt;&lt;/span&gt;/method/&lt;span class="nt"&gt;&amp;lt;my-method&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The &lt;a href="https://docs.dapr.io/reference/resource-specs/httpendpoints-reference/"&gt;HTTPEndpoint resource&lt;/a&gt; is specified in a yaml file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dapr.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTPEndpoint&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;NAME&amp;gt;&lt;/span&gt;  
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1alpha1&lt;/span&gt;
  &lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-BASEURL&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;# Required. Use "http://" or "https://" prefix.&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Optional&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-A-HEADER-NAME&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-A-HEADER-VALUE&amp;gt;&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-A-HEADER-NAME&amp;gt;&lt;/span&gt;
    &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-SECRET-NAME&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-SECRET-KEY&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Optional&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-SCOPED-APPIDS&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Optional&lt;/span&gt;
  &lt;span class="na"&gt;secretStore&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;REPLACE-WITH-SECRETSTORE&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Using the Fully Qualified Domain Name (FQDN) of the endpoint.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;GET http://localhost:&amp;lt;daprPort&amp;gt;/v1.0/invoke/&amp;lt;FQDN_URL&amp;gt;/method/&amp;lt;my-method&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Workflow updates
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--s5E7Kd7v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o95i0pkp765rbwkuoere.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--s5E7Kd7v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o95i0pkp765rbwkuoere.png" alt="Dapr Workflow management API" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Dapr Workflow management API has been updated to allow workflows to be paused, resumed, and purged. Purging workflows means the workflow state will be deleted. Once a workflow has been completed, it’s a good practice to purge the workflow state to keep the storage requirements to a minimum.  In addition, workflows can now wait for external events. This is particularly useful for business processes that involve human interaction, such as approval workflows, or waiting for callbacks from other long-running processes. &lt;/p&gt;

&lt;p&gt;These are the current Workflow management HTTP API methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;POST http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0-alpha1/workflows/dapr/&lt;span class="nt"&gt;&amp;lt;instanceID&amp;gt;&lt;/span&gt;/start
POST http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0-alpha1/workflows/dapr/&lt;span class="nt"&gt;&amp;lt;instanceID&amp;gt;&lt;/span&gt;/terminate
POST http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0-alpha1/workflows/dapr/&lt;span class="nt"&gt;&amp;lt;instanceID&amp;gt;&lt;/span&gt;/pause
POST http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0-alpha1/workflows/dapr/&lt;span class="nt"&gt;&amp;lt;instanceID&amp;gt;&lt;/span&gt;/resume
POST http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0-alpha1/workflows/dapr/&lt;span class="nt"&gt;&amp;lt;instanceID&amp;gt;&lt;/span&gt;/purge
POST http://localhost:&lt;span class="nt"&gt;&amp;lt;daprPort&amp;gt;&lt;/span&gt;/v1.0-alpha1/workflows/dapr/&lt;span class="nt"&gt;&amp;lt;instanceID&amp;gt;&lt;/span&gt;/raiseEvent/&lt;span class="nt"&gt;&amp;lt;eventName&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the Workflow management API, which is still in preview, has breaking changes compared to v1.10. The changes are outlined in this &lt;a href="https://github.com/dapr/dapr/pull/6218"&gt;GitHub issue&lt;/a&gt;. Another breaking change is the ability to support multiple workflow apps in a single cluster, as mentioned in this &lt;a href="https://github.com/dapr/dapr/issues/6013"&gt;GitHub issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The Dapr Python SDK has been upgraded to allow &lt;a href="https://docs.dapr.io/developing-applications/sdks/python/python-workflow/"&gt;authoring of workflows&lt;/a&gt;, so now workflow applications can be written in either C# or Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cryptography API 🆕
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gHNzyqDz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/344mt6chof11exh2qu80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gHNzyqDz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/344mt6chof11exh2qu80.png" alt="Dapr cryptography API" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/cryptography/cryptography-overview/"&gt;Cryptography API&lt;/a&gt; is a new preview building block that enables developers to encrypt/decrypt data in a safe and consistent way. Cryptography can be used within key vaults or the Dapr sidecar without exposing cryptographic keys to the application. With this new API, a set of new Cryptography components is made available that is listed in the &lt;em&gt;New Components&lt;/em&gt; section.&lt;/p&gt;

&lt;p&gt;These examples show how data can be encrypted and decrypted with the Cryptography API using the Dapr JavaScript SDK:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Encryption&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// When passing data (a buffer or string), `encrypt` returns a Buffer with the encrypted message&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ciphertext&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;encrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plaintext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Name of the Dapr component (required)&lt;/span&gt;
    &lt;span class="na"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mycryptocomponent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Name of the key stored in the component (required)&lt;/span&gt;
    &lt;span class="na"&gt;keyName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mykey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Algorithm used for wrapping the key, which must be supported by the key named above.&lt;/span&gt;
    &lt;span class="c1"&gt;// Options include: "RSA", "AES"&lt;/span&gt;
    &lt;span class="na"&gt;keyWrapAlgorithm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;RSA&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Decryption&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// When passing data as a buffer, `decrypt` returns a Buffer with the decrypted message&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;plaintext&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ciphertext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Only required option is the component name&lt;/span&gt;
    &lt;span class="na"&gt;componentName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mycryptocomponent&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more info on how to use the Cryptography API, see the &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/cryptography/howto-cryptography/"&gt;Dapr docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;p&gt;Dapr decouples the functionality of the common set of APIs with their underlying implementations via &lt;a href="https://docs.dapr.io/concepts/components-concept/"&gt;components&lt;/a&gt;. Components of the same type are interchangeable since they implement the same interface. Release 1.11 contains both new components as improvements to existing components.&lt;/p&gt;

&lt;h3&gt;
  
  
  New components
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bindings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/wasm/"&gt;WASM&lt;/a&gt;: allows invoking applications compiled to &lt;a href="https://developer.mozilla.org/en-US/docs/WebAssembly"&gt;WebAssembly&lt;/a&gt; and run in modern browsers.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/kitex/"&gt;Kitex&lt;/a&gt;: allows invoking Generic Calls in &lt;a href="https://www.cloudwego.io/docs/kitex/overview/"&gt;Kitex&lt;/a&gt;, a high-performance and strong-extensibility Golang RPC framework.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;State Stores&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-etcd/"&gt;etcd&lt;/a&gt;: enables state store functionality using &lt;a href="https://etcd.io/"&gt;etcd&lt;/a&gt;, a distributed, reliable key-value store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cryptography&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-cryptography/azure-key-vault/"&gt;Azure Key Vault&lt;/a&gt;: enables cryptography functionality using &lt;a href="https://azure.microsoft.com/en-us/products/key-vault/"&gt;Azure Key Vault&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-cryptography/local-storage/"&gt;Local storage&lt;/a&gt;: enables cryptography functionality using local files. Supported file formats are PEM, JSON Web Key, and raw key data for symmetric keys.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-cryptography/json-web-key-sets/"&gt;JWKS&lt;/a&gt;: enables cryptography functionality using &lt;a href="https://www.rfc-editor.org/rfc/rfc7517"&gt;JSON Web Key&lt;/a&gt; Sets (JWKS).&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-cryptography/kubernetes-secrets/"&gt;Kubernetes Secrets&lt;/a&gt;: enables cryptography functionality using &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Kubernetes secrets&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component improvements
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Ug1fGiK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btnok059nh0go6vvnzi3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Ug1fGiK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btnok059nh0go6vvnzi3.png" alt="Dapr component lifecycle" width="800" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With 1.11 many components have graduated to the &lt;em&gt;Stable&lt;/em&gt; certification level, which means these can be used in production confidently. All components have a certification level as mentioned in the &lt;strong&gt;&lt;a href="https://docs.dapr.io/operations/components/certification-lifecycle/"&gt;Certification Lifecycle&lt;/a&gt;&lt;/strong&gt;. The &lt;em&gt;Stable&lt;/em&gt; level means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The component must have component &lt;strong&gt;&lt;a href="https://docs.dapr.io/operations/components/certification-lifecycle/#certification-tests"&gt;certification tests&lt;/a&gt;&lt;/strong&gt; validating functionality and resiliency.&lt;/li&gt;
&lt;li&gt;The component is maintained by Dapr maintainers and supported by the community.&lt;/li&gt;
&lt;li&gt;The component is well documented and tested.&lt;/li&gt;
&lt;li&gt;The component has been available as Alpha or Beta for at least 1 minor version release of Dapr runtime prior.&lt;/li&gt;
&lt;li&gt;A maintainer will address component security, core functionality and test issues according to the Dapr support policy and issue a patch release that includes the patched stable component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following components have progressed to &lt;em&gt;Stable&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bindings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/s3/"&gt;AWS S3&lt;/a&gt;: output binding to store data in AWS S3 buckets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-rate-limit/"&gt;Rate limit&lt;/a&gt;: restricts the maximum allowed HTTP requests per second.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-middleware/middleware-bearer/"&gt;OpenID Connect&lt;/a&gt;: verifies a bearer token using &lt;a href="https://openid.net/connect/"&gt;OpenID Connect&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pub/Sub&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-gcp-pubsub/"&gt;GCP Pub/Sub&lt;/a&gt;: enables pub/sub messaging using Google Cloud Platform &lt;a href="https://cloud.google.com/pubsub/"&gt;Pub/Sub&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;State stores&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-firestore/"&gt;GCP Firestore&lt;/a&gt;: enables state store functionality using Google Cloud Platform &lt;a href="https://cloud.google.com/firestore/"&gt;Firestore&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-sqlite/"&gt;SQLite&lt;/a&gt;: enables state store functionality using &lt;a href="https://www.sqlite.org/index.html"&gt;SQLite 3&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configuration stores&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-configuration-stores/postgresql-configuration-store/"&gt;PostgreSQL&lt;/a&gt;: enables configuration store functionality using &lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-configuration-stores/redis-configuration-store/"&gt;Redis&lt;/a&gt;: enables configuration store functionality using &lt;a href="https://redis.io/"&gt;Redis&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're using any of these components and have a demo to show, drop a link in the &lt;em&gt;#show-and-tell&lt;/em&gt; channel on the &lt;strong&gt;&lt;a href="http://bit.ly/dapr-discord"&gt;Dapr Discord&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;*&lt;strong&gt;&lt;em&gt;Azure components authentication improvements&lt;/em&gt;&lt;/strong&gt;*&lt;/p&gt;

&lt;p&gt;Azure AD authentication has been added to the &lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/storagequeues/"&gt;Azure Storage Queues binding&lt;/a&gt; and the &lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-sqlserver/"&gt;SQL Server state store&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All Azure components that support Azure AD now support authentication using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview"&gt;Azure AD Workload Identity&lt;/a&gt; on Kubernetes&lt;/li&gt;
&lt;li&gt;Azure CLI &lt;a href="https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli"&gt;credentials&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is next?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This post is not a complete list of features and changes released in version 1.11. Read the official &lt;strong&gt;&lt;a href="https://blog.dapr.io/posts/2023/06/12/dapr-v1.11-is-now-available/"&gt;Dapr release notes&lt;/a&gt;&lt;/strong&gt; for more information. The release notes also contain information on how to upgrade to this latest version.&lt;/p&gt;

&lt;p&gt;Excited about these features and want to learn more? I'll cover the new features in more detail in future posts. Until then, join the &lt;strong&gt;&lt;a href="http://bit.ly/dapr-discord"&gt;Dapr Discord&lt;/a&gt;&lt;/strong&gt; to connect with thousands of Dapr users.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cloudnative</category>
      <category>cloud</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Creating a Dapr pluggable component for Supabase</title>
      <dc:creator>Marc Duiker</dc:creator>
      <pubDate>Tue, 25 Apr 2023 14:56:41 +0000</pubDate>
      <link>https://dev.to/diagrid/creating-a-dapr-pluggable-component-for-supabase-32kj</link>
      <guid>https://dev.to/diagrid/creating-a-dapr-pluggable-component-for-supabase-32kj</guid>
      <description>&lt;p&gt;In this post, you’ll learn how to use the Dapr pluggable component .NET SDK and the Supabase C# library to build a pluggable state store component that uses Supabase tables.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you prefer watching a video instead of reading this blog post, head over to YouTube and watch this:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/L0RRnWfGCzo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dapr, the open-source, portable, event-driven runtime for building distributed applications, comes with over &lt;a href="https://docs.dapr.io/concepts/components-concept/"&gt;100 built-in components&lt;/a&gt; which can be used to integrate resources across many clouds or on-premise systems. If those components are not enough, or you want to create something custom for your specific needs, you can create a &lt;a href="https://docs.dapr.io/operations/components/pluggable-components/pluggable-components-overview/"&gt;pluggable component&lt;/a&gt;. The Dapr building block APIs that are pluggable are: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State management&lt;/li&gt;
&lt;li&gt;Publish and subscribe&lt;/li&gt;
&lt;li&gt;Input and output bindings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since Dapr &lt;a href="https://www.diagrid.io/blog/dapr-1-10-release-highlights"&gt;release 1.10&lt;/a&gt; there are &lt;a href="https://docs.dapr.io/developing-applications/develop-components/pluggable-components/pluggable-components-sdks/pluggable-components-dotnet/"&gt;pluggable component SDKs&lt;/a&gt; available that make it easier to create them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://supabase.com/"&gt;Supabase&lt;/a&gt; is a popular, open-source, Firebase alternative that offers Postgres databases, authentication, Edge functions, real-time messaging and file storage. The Supabase community has released a &lt;a href="https://github.com/supabase-community/supabase-csharp"&gt;C#/.NET library&lt;/a&gt; that developers can use to integrate their .NET apps with the Supabase platform. For managing Postgres databases, the library uses the &lt;a href="https://github.com/supabase-community/postgrest-csharp"&gt;postgrest-csharp&lt;/a&gt; library that allows developers to use strongly typed models and LINQ queries.&lt;/p&gt;

&lt;p&gt;This post describes the steps required to make a proof of concept Dapr pluggable component using .NET 7 that combines both the Dapr pluggable component .NET SDK and the Supabase C# library. The result is an ASP.NET application, that is run locally, and tested by making HTTP requests to interact with the state stored in a Supabase table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z3XLZdzX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/089owyntaiwxeiunwhtz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z3XLZdzX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/089owyntaiwxeiunwhtz.png" alt="Image description" width="800" height="277"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dotnet.microsoft.com/download/dotnet/7.0"&gt;.NET 7 SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.dapr.io/getting-started/install-dapr-cli/"&gt;Dapr CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A Supabase account, you can &lt;a href="https://app.supabase.com/sign-up"&gt;sign up&lt;/a&gt; for free.&lt;/li&gt;
&lt;li&gt;Supported OS: macOS, Linux, &lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install"&gt;WSL&lt;/a&gt; on Windows.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Creating the DaprPluggableSupabase .NET project
&lt;/h2&gt;

&lt;p&gt;If you’re more interested in using the pluggable component instead of creating it from scratch, continue with section &lt;strong&gt;3. Supabase Setup&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Clone &lt;a href="https://github.com/diagrid-labs/dapr-pluggable-component-supabase"&gt;this repo&lt;/a&gt;, open a terminal and navigate to the &lt;code&gt;src&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;The cloned repository already contains an &lt;a href="http://ASP.NET"&gt;ASP.NET&lt;/a&gt; app in the &lt;code&gt;DaprPluggableSupabase&lt;/code&gt; folder that is the pluggable component. You can use this as a reference. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new .NET web project, and make sure to use a different name than &lt;code&gt;DaprPluggableSupabase&lt;/code&gt; (e.g. &lt;code&gt;MyDaprPluggableSupabase&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet new web --name MyDaprPluggableSupabase&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change to the &lt;code&gt;MyDaprPluggableSupabase&lt;/code&gt; folder and add the &lt;code&gt;Dapr.PluggableComponents.AspNetCore&lt;/code&gt; and &lt;code&gt;supabase-csharp&lt;/code&gt; NuGet packages:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd MyDaprPluggableSupabase&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package Dapr.PluggableComponents.AspNetCore&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet add package supabase-csharp&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new class file in the &lt;code&gt;MyDaprPluggableSupabase&lt;/code&gt; folder named &lt;code&gt;KeyValue.cs&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the following content to this new class:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Postgrest.Attributes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Postgrest.Models&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DaprPluggableSupabase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dapr_state_store"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;KeyValue&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;PrimaryKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="nf"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"created_at"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="nf"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="nf"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;KeyValue&lt;/code&gt; class defines the data type that will be stored in the Supabase table. It inherits from &lt;code&gt;BaseModel&lt;/code&gt;, which is part of the Supabase postgrest-csharp library. This library uses attributes that can be used to decorate the class and properties, which makes it easier for developers to use strongly typed models when interacting with the database table. Dapr state stores are key/value stores and Postgres databases are object-relational data stores. Since this is not a perfect match, the Dapr key/value-based state management API is mapped to this &lt;code&gt;KeyValue&lt;/code&gt; model to fit the Postgres schema.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new class file in the &lt;code&gt;MyDaprPluggableSupabase&lt;/code&gt; folder named &lt;code&gt;SupabaseStateStore.cs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add the following content to this new class:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Dapr.PluggableComponents.Components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Dapr.PluggableComponents.Components.StateStore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;DaprPluggableSupabase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SupabaseStateStore&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IStateStore&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;disable&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="n"&gt;_supabaseClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;

        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;PROJECT_APIKEY_KEYWORD&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"projectApiKey"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;PROJECT_URL_KEYWORD&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"projectUrl"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;DeleteAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StateStoreDeleteRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_supabaseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;StateStoreGetResponse&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StateStoreGetRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;KeyValue&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetKV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&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="n"&gt;kv&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;valueAsBytes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StateStoreGetResponse&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;valueAsBytes&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StateStoreGetResponse&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;InitAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MetadataRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_URL_KEYWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;projectUrl&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Missing required property \"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PROJECT_URL_KEYWORD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\" in component file."&lt;/span&gt;&lt;span class="p"&gt;);&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="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PROJECT_APIKEY_KEYWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;projectApiKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;InvalidOperationException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"Missing required property \"&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;PROJECT_APIKEY_KEYWORD&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;\" in component file."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;_supabaseClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projectUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;projectApiKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_supabaseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;InitializeAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;SetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;StateStoreSetRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;newKV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;KeyValue&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;CreatedAt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UtcNow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

            &lt;span class="n"&gt;KeyValue&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;existingKV&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;GetKV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&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="n"&gt;existingKV&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;newKV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;existingKV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_supabaseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;Update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newKV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&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="k"&gt;else&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_supabaseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;().&lt;/span&gt;&lt;span class="nf"&gt;Insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newKV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyValue&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;GetKV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CancellationToken&lt;/span&gt; &lt;span class="n"&gt;cancellationToken&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_supabaseClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;From&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyValue&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;disable&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;object&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Single&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cancellationToken&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the class inherits from &lt;code&gt;IStateStore&lt;/code&gt;, this is the interface that all Dapr state store components implement. The interface contains three methods that require implementing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DeleteAsync&lt;/code&gt;, this will delete a value from the store.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GetAsync&lt;/code&gt;, this will retrieve a value from the store.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SetAsync&lt;/code&gt;, this will insert/update a value to the store.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All methods use the Supabase C# client to manage the data via the &lt;code&gt;KeyValue&lt;/code&gt; model.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;IStateStore&lt;/code&gt; interface inherits the &lt;code&gt;IPluggableComponent&lt;/code&gt; interface that contains only one method, &lt;code&gt;InitAsync&lt;/code&gt;, and this method used to initialize the state store. In this case, it is used to create an instance of the &lt;code&gt;Supabase.Client&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update the existing &lt;code&gt;Program.cs&lt;/code&gt; file with the following content:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;Dapr.PluggableComponents&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;DaprPluggableSupabase&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CreateBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DaprPluggableComponentsApplication&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;RegisterService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"supabase"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;serviceBuilder&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;serviceBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RegisterStateStore&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SupabaseStateStore&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This class defines the application as a &lt;code&gt;DaprPluggableComponentApplication&lt;/code&gt; and registers the &lt;code&gt;SupabaseStateStore&lt;/code&gt; as a new state store that will communicate with Dapr applications via a Unix domain socket named &lt;code&gt;supabase&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Build the application to make sure there are no compilation errors:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet build&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  3. &lt;strong&gt;Supabase Setup&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The pluggable component will interact with a Supabase Postgres table. This section describes how to create the table using the Supabase dashboard and where to find the project URL and API key required in the next section.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login to Supabase and create a new project.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a new database table with the following specifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;dapr_state_store&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Primary Key: &lt;code&gt;id&lt;/code&gt;, (int8, not null)&lt;/li&gt;
&lt;li&gt;Columns:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;created_at&lt;/code&gt; (timestamptz, not null)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;key&lt;/code&gt; (text, not null)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt; (text, null)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RLS (Row Level Security) is disabled&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that for production use, &lt;a href="https://supabase.com/docs/guides/auth/row-level-security"&gt;Row Level Security (RLS)&lt;/a&gt; should be enabled, and access policies should be added to the table.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Table definition:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dapr_state_store&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;bigint&lt;/span&gt; &lt;span class="k"&gt;generated&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;identity&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;timestamp&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt; &lt;span class="k"&gt;zone&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="k"&gt;constraint&lt;/span&gt; &lt;span class="n"&gt;dapr_state_store_pkey&lt;/span&gt; &lt;span class="k"&gt;primary&lt;/span&gt; &lt;span class="k"&gt;key&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;constraint&lt;/span&gt; &lt;span class="n"&gt;dapr_state_store_id_key&lt;/span&gt; &lt;span class="k"&gt;unique&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;tablespace&lt;/span&gt; &lt;span class="n"&gt;pg_default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You'll need the Supabase project URL and public API key to configure the Dapr component file in the next section. This information is found in the Supabase dashboard in the project &lt;code&gt;Settings &amp;gt; API&lt;/code&gt; tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;strong&gt;Update the Dapr pluggable Supabase component file&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Dapr uses a modular design where functionality is delivered as a &lt;a href="https://docs.dapr.io/concepts/components-concept/"&gt;component&lt;/a&gt;. A component file contains the specification of a component, including the name, the component type, and related metadata that is specific to connecting with the underlying resource. The component file for the Supabase state store looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dapr.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Component&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pluggable-supabase&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;state.supabase&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;projectUrl&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;projectApiKey&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value of the &lt;code&gt;spec.type&lt;/code&gt; field, &lt;code&gt;state.supabase&lt;/code&gt;, consists of two parts: the component type (&lt;code&gt;state&lt;/code&gt;), and the socket name (&lt;code&gt;supabase&lt;/code&gt;). The socket name needs to match with the socket name argument provided in the &lt;code&gt;RegisterService&lt;/code&gt; method in the &lt;code&gt;Program.cs&lt;/code&gt; class of section 2. A template of this component file is available in the repository, follow these steps to update the file, so it can be used locally.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the &lt;code&gt;resources&lt;/code&gt; folder in this repository.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Rename the &lt;code&gt;resources\pluggableSupabase.yml.template&lt;/code&gt; file to &lt;code&gt;resources\pluggableSupabase.yml&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The pluggableSupabase.yml file is added to .gitignore, so it won't be accidentally committed to source control for this demo app. For production use, the yaml files should be checked into source control and &lt;a href="https://docs.dapr.io/operations/components/component-secrets/"&gt;secret store references&lt;/a&gt; should be used, instead of plain text values.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Open the &lt;code&gt;pluggableSupabase.yml&lt;/code&gt; file and update the values for &lt;code&gt;projectUrl&lt;/code&gt; and &lt;code&gt;projectApiKey&lt;/code&gt; obtained in the previous section.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Save the file and copy it to the user's Dapr components folder: &lt;code&gt;~/.dapr/components&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When the Dapr CLI is run, all the component files in this folder will be loaded, so the pluggable Supabase component should be available.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  5. &lt;strong&gt;Run the DaprPluggableSupabase application&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Regular Dapr components are part of the Dapr runtime and don't require additional processes to run. Pluggable components, however, are not part of the Dapr runtime and need to be run separately, which is done in this section.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;DaprPluggableSupabase&lt;/code&gt; project in this repo implements the Dapr state store interface and uses the &lt;a href="https://github.com/supabase-community/supabase-csharp"&gt;Supabase C# library&lt;/a&gt; to access a Supabase table. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open a terminal and navigate to the 

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;src/DaprPluggableSupabase&lt;/code&gt; folder to use the included pluggable component project 
&lt;em&gt;or&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/MyDaprPluggableSupabase&lt;/code&gt; folder to use the pluggable component project you’ve just created yourself.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Build the project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet build&lt;/code&gt;&lt;/p&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Run the project:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dotnet run&lt;/code&gt;&lt;/p&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6. &lt;strong&gt;Run the Dapr process and test the Supabase state store&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a new terminal and use the Dapr CLI to run the Dapr process&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dapr run --app-id myapp --dapr-http-port 3500&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Expected output:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The output should contain an INFO message that the pluggable-supabase component is loaded:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;INFO[0000] component loaded. name: pluggable-supabase, type: state.supabase/v1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The log should end with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ℹ️  Dapr sidecar is up and running.&lt;br&gt;
✅  You're up and running! Dapr logs will appear here.&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set a new state by making a POST request to the state management endpoint:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl --request POST --url http://localhost:3500/v1.0/state/pluggable-supabase --header 'content-type: application/json' --data '[{"key": "key1","value": "This is stored in Supabase!"}]'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Expected output:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HTTP 204 No Content&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Retrieve the new state using a GET request:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl --request GET --url http://localhost:3500/v1.0/state/pluggable-supabase/key1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Expected output:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;HTTP 200 OK&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"This is stored in Supabase!"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or have a look at the Supabase dashboard to see the new state record in the table.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You've now successfully created and used the Dapr pluggable Supabase state store component. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;This proof of concept implementation of a pluggable component demonstrates how the Dapr pluggable component .NET SDK makes it easier for developers to create their custom components. You can use &lt;a href="https://github.com/diagrid-labs/dapr-pluggable-component-supabase"&gt;this solution&lt;/a&gt; as a starting point, and expand it with additional state management API features, such as entity tag (ETag), metadata, and options, as described in the &lt;a href="https://docs.dapr.io/reference/api/state_api"&gt;specification&lt;/a&gt;. A requirement for using this pluggable component in a real project is to &lt;a href="https://docs.dapr.io/operations/hosting/"&gt;deploy it&lt;/a&gt; to a (managed) Kubernetes environment or use it in a self-hosted mode.&lt;/p&gt;

&lt;p&gt;The Supabase platform offers a great variety of useful back-end features, and with the set of client libraries it’s very accessible to a diverse group of developers. Take a look at &lt;a href="https://supabase.com/launch-week"&gt;Supabase Launch Week&lt;/a&gt; to read about their latest feature announcements.&lt;/p&gt;

&lt;p&gt;From my perspective, I’d like to explore further how Dapr can integrate with other Supabase features. It would also be great to see a Supabase state store as a built-in component that’s available in the Dapr runtime without the need of running the pluggable component separately. I also hope the proposed &lt;a href="https://github.com/dapr/dapr/issues/5146"&gt;DocumentStore building block&lt;/a&gt; will get some traction this year, since this will pair up very nicely with Supabase and other PostgreSQL stores. &lt;/p&gt;

&lt;p&gt;Do you have any questions or comments about this blog post or the code? Join the &lt;strong&gt;&lt;a href="https://bit.ly/dapr-discord"&gt;Dapr discord&lt;/a&gt;&lt;/strong&gt; and post a message in the &lt;code&gt;#pluggable-components&lt;/code&gt; channel. Have you made something with Dapr? Post a message in the &lt;code&gt;#show-and-tell&lt;/code&gt; channel, we love to see your creations!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>cloud</category>
      <category>tutorial</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>Create event-driven applications with Cloudflare queues and Dapr</title>
      <dc:creator>Marc Duiker</dc:creator>
      <pubDate>Thu, 09 Mar 2023 14:20:59 +0000</pubDate>
      <link>https://dev.to/diagrid/create-event-driven-applications-with-cloudflare-queues-and-dapr-3an8</link>
      <guid>https://dev.to/diagrid/create-event-driven-applications-with-cloudflare-queues-and-dapr-3an8</guid>
      <description>&lt;p&gt;In this post, you’ll learn how to build a cloud to edge event-driven application with Dapr and Cloudflare. You’ll learn how to create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Cloudflare queue.&lt;/li&gt;
&lt;li&gt;A consumer Cloudflare worker (in TypeScript) that reads messages from the queue.&lt;/li&gt;
&lt;li&gt;A producer Dapr app (in TypeScript) that uses the Cloudflare Queue binding to publish messages to the queue.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hNKtXXn0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o4co04m0zqy3cis62o3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hNKtXXn0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o4co04m0zqy3cis62o3m.png" alt="Sending messages from a Dapr app to Cloudflare Queues" width="800" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-driven applications FTW 🚀
&lt;/h2&gt;

&lt;p&gt;Event-driven applications are becoming increasingly popular. The decoupling of event producers and event consumers makes applications more resilient and flexible, since they allow independent implementation and scaling of the applications. &lt;/p&gt;

&lt;p&gt;Cloudflare recently announced &lt;a href="https://developers.cloudflare.com/queues/"&gt;Queues&lt;/a&gt;, allowing developers to send and receive messages with guaranteed delivery and integrating with Cloudflare Workers, a fast edge computing platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dapr.io/"&gt;Dapr&lt;/a&gt;, the open-source distributed application runtime, is often used in event-driven applications. Dapr provides a set of standardized API building blocks that simplify microservice development. By using the &lt;a href="https://docs.dapr.io/developing-applications/building-blocks/bindings/bindings-overview/"&gt;Bindings building block&lt;/a&gt;, developers can use input, and output bindings, and either trigger their apps or invoke other resources without having to learn resource-specific SDKs for these resources. With &lt;a href="https://www.diagrid.io/blog/dapr-1-10-release-highlights"&gt;Dapr release 1.10&lt;/a&gt;, a &lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/cloudflare-queues/"&gt;new binding&lt;/a&gt; is provided that allows developers to publish messages to Cloudflare Queues. Because of the common set of APIs that Dapr offers, developers from any background can use the binding to publish messages to Cloudflare Queues without needing to know the Cloudflare SDKs or adding that dependency to their codebase.&lt;/p&gt;

&lt;p&gt;Let’s start building an event-driven app and see the binding in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;The following is required to run this sample:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clone &lt;a href="https://github.com/diagrid-labs/dapr-cloudflare-queues"&gt;this repository&lt;/a&gt; to your local machine.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://docs.dapr.io/getting-started/install-dapr-cli/"&gt;Dapr CLI&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Use the Dapr CLI to install the &lt;a href="https://docs.dapr.io/getting-started/install-dapr-selfhost/"&gt;Dapr runtime locally&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dapr init&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install &lt;a href="https://nodejs.org/en/download/"&gt;Node.js&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install &lt;a href="https://developers.cloudflare.com/workers/wrangler/install-and-update/"&gt;Cloudflare Wrangler&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure you're on a Cloudflare paid plan, since that is required to use Cloudflare queues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Enable Queues in the Cloudflare dashboard.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dashboard &amp;gt; Workers &amp;gt; Queues&lt;/li&gt;
&lt;li&gt;Enable Queues Beta&lt;/li&gt;
&lt;li&gt;You should see a confirmation in the dashboard that queues are enabled.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t7U2fY_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gum1sg2hv31xt9xn1jc9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t7U2fY_3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gum1sg2hv31xt9xn1jc9.png" alt="Enabled Cloudflare Queues" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the applications
&lt;/h2&gt;

&lt;p&gt;The solution consists of three parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A Cloudflare queue&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;consumer&lt;/em&gt; Cloudflare worker that reads messages from the queue.&lt;/li&gt;
&lt;li&gt;A &lt;em&gt;producer&lt;/em&gt; Dapr app that will publish messages to the queue.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Create a Cloudflare queue
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open a terminal and use the wrangler CLI to login to Cloudflare:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wrangler login&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Follow the instructions in the browser to login to Cloudflare.&lt;/p&gt;

&lt;p&gt;The response in the terminal should end with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Successfully logged in.&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create the Cloudflare queue using the wrangler CLI:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wrangler queues create dapr-messages&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The response in the terminal should end with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Created queue dapr-messages.&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Create a consumer Cloudflare worker
&lt;/h2&gt;

&lt;p&gt;You can either create a new &lt;em&gt;consumer&lt;/em&gt; worker by following steps 1-3, or use the &lt;a href="https://github.com/diagrid-labs/dapr-cloudflare-queues/tree/main/consumer"&gt;existing &lt;em&gt;consumer&lt;/em&gt; worker&lt;/a&gt; in this repository and continue from step 4.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In the root folder, create a worker to consume messages:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wrangler init consumer&lt;/code&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create package.json: &lt;code&gt;Y&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use TypeScript: &lt;code&gt;Y&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create worker: &lt;code&gt;Fetch handler&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write tests: &lt;code&gt;N&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A new folder named &lt;em&gt;consumer&lt;/em&gt; will be created which contains the worker.&lt;/p&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Update the &lt;em&gt;consumer/src/index.ts&lt;/em&gt; file to:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="na"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MessageBatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Env&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="k"&gt;void&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;let&lt;/span&gt; &lt;span class="nx"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;messages&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;/li&gt;
&lt;li&gt;

&lt;p&gt;Add the following lines to the &lt;em&gt;consumer/wrangler.toml&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[[queues.consumers]]&lt;/span&gt;
&lt;span class="py"&gt;queue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"dapr-messages"&lt;/span&gt;
&lt;span class="py"&gt;max_batch_size&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Ensure that you're in the &lt;em&gt;consumer&lt;/em&gt; folder and install the dependencies:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd consumer&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/p&gt;


&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Publish the &lt;em&gt;consumer&lt;/em&gt; worker:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wrangler publish&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The response in the terminal should end with:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;Published consumer &lt;span class="o"&gt;(&lt;/span&gt;... sec&lt;span class="o"&gt;)&lt;/span&gt;
  https://consumer.&amp;lt;SUBDOMAIN&amp;gt;.workers.dev
  Consumer &lt;span class="k"&gt;for &lt;/span&gt;dapr-messages
Current Deployment ID: &amp;lt;DEPLOYMENT_ID&amp;gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;Start a tail to read the log of the consumer worker:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;wrangler tail&lt;/code&gt;&lt;/p&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  3. Configure the producer Dapr app
&lt;/h2&gt;

&lt;p&gt;The Cloudflare Dapr binding uses a Cloudflare worker to publish messages, since only Cloudflare workers can access the queue.&lt;/p&gt;

&lt;p&gt;There are two options for this worker:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dapr provisions the worker.&lt;/li&gt;
&lt;li&gt;You use a pre-provisioned Cloudflare worker.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sample uses option 1. Read the &lt;a href="https://v1-10.docs.dapr.io/reference/components-reference/supported-bindings/cloudflare-queues/#configuring-the-worker"&gt;Cloudflare Queues binding spec&lt;/a&gt; and choose &lt;em&gt;Manually provision the Worker script&lt;/em&gt; if you want to go for option 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a binding file
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Rename the &lt;code&gt;producer/resources/binding.yaml.template&lt;/code&gt; to &lt;code&gt;producer/resources/binding.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;code&gt;binding.yaml&lt;/code&gt; file and inspect its content.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dapr.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Component&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloudflare-queues&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bindings.cloudflare.queues&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="c1"&gt;# Increase the initTimeout if Dapr is managing the Worker for youinitTimeout: "120s"&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="c1"&gt;# Name of the existing Cloudflare Queue (required)- name: queueName&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dapr-messages"&lt;/span&gt;
&lt;span class="c1"&gt;# Name of the Worker (required)- name: workerName&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dapr-message-worker"&lt;/span&gt;
&lt;span class="c1"&gt;# PEM-encoded private Ed25519 key (required)- name: key&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;-----BEGIN PRIVATE KEY-----&lt;/span&gt;
        &lt;span class="s"&gt;MC4CAQ...&lt;/span&gt;
        &lt;span class="s"&gt;-----END PRIVATE KEY-----&lt;/span&gt;
&lt;span class="c1"&gt;# Cloudflare account ID (required to have Dapr manage the Worker)- name: cfAccountID&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# API token for Cloudflare (required to have Dapr manage the Worker)- name: cfAPIToken&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;span class="c1"&gt;# URL of the Worker (required if the Worker has been pre-created outside of Dapr)- name: workerUrl&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The &lt;code&gt;metadata.name&lt;/code&gt;, &lt;code&gt;spec.metadata.queueName&lt;/code&gt; and &lt;code&gt;spec.metadata.workerName&lt;/code&gt; values have already been set. Ensure that the &lt;code&gt;queueName&lt;/code&gt; matches the &lt;code&gt;queue&lt;/code&gt; setting in the &lt;em&gt;consumer&lt;/em&gt; worker &lt;code&gt;wrangler.toml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Values for &lt;code&gt;spec.metadata.key&lt;/code&gt;, &lt;code&gt;spec.metadata.cfAccountID&lt;/code&gt;, and &lt;code&gt;spec.metadata.cfAPIToken&lt;/code&gt; still need to be provided.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Follow &lt;a href="https://v1-10.docs.dapr.io/reference/components-reference/supported-bindings/cloudflare-queues/#generate-an-ed25519-key-pair"&gt;these instructions&lt;/a&gt; in the Dapr docs to set the value for &lt;code&gt;spec.metadata.key&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Cloudflare account ID should go in the &lt;code&gt;spec.metadata.cfAccountID&lt;/code&gt; field. You can find the account ID in the Cloudflare dashboard URL: &lt;code&gt;https://dash.cloudflare.com/&amp;lt;ACCOUNT_ID&amp;gt;/workers/overview&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A Cloudflare API token should go in the &lt;code&gt;spec.metadata.cfAPIToken&lt;/code&gt; field. It can be generated as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Cloudflare dashboard, go to the Workers page.&lt;/li&gt;
&lt;li&gt;Click the &lt;em&gt;API tokens&lt;/em&gt; link&lt;/li&gt;
&lt;li&gt;Click the &lt;em&gt;Create token&lt;/em&gt; button&lt;/li&gt;
&lt;li&gt;Click the &lt;em&gt;Use template&lt;/em&gt; button for Edit Cloudflare Workers&lt;/li&gt;
&lt;li&gt;Update the permissions to only contain:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Account&lt;/em&gt; | &lt;em&gt;Worker Scripts&lt;/em&gt; | &lt;em&gt;Edit&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Update the Account Resources to only contain:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Include&lt;/em&gt; | &lt;em&gt;&amp;lt;YOUR ACCOUNT&amp;gt;&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Set a time to live (TTL) for the token, the shorter, the better, if you're just testing.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now the binding file is complete. The file is gitignored, so the secrets won't be committed to the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inspect the Node app
&lt;/h3&gt;

&lt;p&gt;Let's have a look at the Dapr app that will send messages to the Cloudflare queue.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Inspect the &lt;code&gt;producer/index.ts&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DaprClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@dapr/dapr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Common settingsconst daprHost = "http://localhost";&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;daprPort&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DAPR_HTTP_PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3500&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Starting...&lt;/span&gt;&lt;span class="dl"&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;bindingName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudflare-queues&lt;/span&gt;&lt;span class="dl"&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;bindingOperation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;publish&lt;/span&gt;&lt;span class="dl"&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;DaprClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;daprHost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;daprPort&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello World &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;binding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bindingName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bindingOperation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&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="nx"&gt;response&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="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sleep&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Completed.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&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="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;main&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="o"&gt;=&amp;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="nx"&gt;error&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="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&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;p&gt;Note that the &lt;code&gt;bindingName&lt;/code&gt; is set to &lt;code&gt;cloudflare-queues&lt;/code&gt; and matches the value of the &lt;code&gt;metadata.name&lt;/code&gt; in the &lt;code&gt;binding.yaml&lt;/code&gt;. The &lt;code&gt;bindingOperation&lt;/code&gt; is set to &lt;code&gt;publish&lt;/code&gt; (&lt;code&gt;create&lt;/code&gt; could be used as an alias).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Run the producer app
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open a new terminal window and navigate to the &lt;code&gt;producer&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the dependencies:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the following command to start the producer app:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dapr run &lt;span class="nt"&gt;--app-id&lt;/span&gt; producer &lt;span class="nt"&gt;--resources-path&lt;/span&gt; ./resources &lt;span class="nt"&gt;--&lt;/span&gt; npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The terminal that logs the tail of the consumer app should show a log statement for each of the ten messages sent:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Unknown Event - Ok @ 17/02/2023, 11:22:50
  &lt;span class="o"&gt;(&lt;/span&gt;log&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[{&lt;/span&gt;&lt;span class="s2"&gt;"body"&lt;/span&gt;:&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Hello World 1&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;,&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"2023-02-17T10:22:50.556Z"&lt;/span&gt;,&lt;span class="s2"&gt;"id"&lt;/span&gt;:&lt;span class="s2"&gt;"8f6293d9d04001e3f2a12be5c47acde2"&lt;/span&gt;&lt;span class="o"&gt;}]&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jM08LsYc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0sjnrtrncscu92dmqc4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jM08LsYc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0sjnrtrncscu92dmqc4.gif" alt="wrangler log tail of the consumer" width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congratulations! You’ve now sent messages from a Dapr app to a Cloudflare Queue (&amp;amp; Worker) via the Cloudflare Queues binding! 🎉 Hungry for more Dapr bindings? Have a look at the long list of &lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/"&gt;supported bindings&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Any questions or comments about this blog post or the code? Join the &lt;a href="https://aka.ms/dapr-discord"&gt;Dapr discord&lt;/a&gt; and post a message in the &lt;code&gt;#components-contrib&lt;/code&gt; channel. Have you made something with Cloudflare and Dapr? Post a message in the &lt;code&gt;#show-and-tell&lt;/code&gt; channel, we love to see your creations!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>cloud</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dapr 1.10 release highlights</title>
      <dc:creator>Marc Duiker</dc:creator>
      <pubDate>Tue, 21 Feb 2023 08:23:41 +0000</pubDate>
      <link>https://dev.to/diagrid/dapr-110-release-highlights-2429</link>
      <guid>https://dev.to/diagrid/dapr-110-release-highlights-2429</guid>
      <description>&lt;p&gt;The Dapr maintainers released a new version of &lt;a href="https://dapr.io/"&gt;Dapr, the distributed application runtime&lt;/a&gt; 🎉. This post highlights the major new features and changes in release 1.10, the first one in 2023.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workflows
&lt;/h2&gt;

&lt;p&gt;My favorite new feature is the &lt;a href="https://v1-10.docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/"&gt;workflow engine and building block&lt;/a&gt;. This feature enables developers to orchestrate business logic for messaging and state management across various microservices. Workflows are suitable for multistep processes such as order processing, HR onboarding, and coordinating software updates across many devices because workflows are stateful and support long-running services. Workflow patterns such as &lt;em&gt;chaining&lt;/em&gt; and &lt;em&gt;fan-out/fan-in&lt;/em&gt; are written in code to execute tasks reliably. The built-in workflow engine is durable and resilient and can either be used via HTTP/gRPC, as well as the .NET SDK (other languages will follow).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N7Uvai89--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sx7514gazytzhced1yhr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N7Uvai89--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sx7514gazytzhced1yhr.png" alt="Dapr Workflow" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read more about the Workflow building block in the &lt;a href="https://v1-10.docs.dapr.io/developing-applications/building-blocks/workflow/workflow-overview/"&gt;Dapr docs&lt;/a&gt; or try out the &lt;a href="https://docs.dapr.io/getting-started/quickstarts/workflow-quickstart/"&gt;Workflow Quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pluggable Components SDKs
&lt;/h2&gt;

&lt;p&gt;Dapr provides a long list of &lt;a href="https://docs.dapr.io/concepts/components-concept/"&gt;built-in components&lt;/a&gt; to work with. But if this isn’t enough, Pluggable Components allows developers to create their own components for their specific integration requirements. Pluggable Components can be written in any language that supports the gRPC protocol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--49ANZixh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nk6p2pftq9zyns1f05hr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--49ANZixh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nk6p2pftq9zyns1f05hr.png" alt="Pluggable Components" width="548" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With release 1.10, developers can create components using the Pluggable Components SDKs in .NET, Java, and Go instead of using the gRPC protocol directly. This will enable many developers to create new components, both public and private ones.&lt;/p&gt;

&lt;p&gt;Read more about Pluggable Components in the &lt;a href="https://docs.dapr.io/operations/components/pluggable-components/pluggable-components-overview/"&gt;Dapr docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running multiple Dapr apps
&lt;/h2&gt;

&lt;p&gt;Running a microservices system locally can be challenging, since multiple apps need to be up and running. Developers would need to call the &lt;code&gt;dapr run&lt;/code&gt; command for each microservice separately, which gets a bit tedious.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bQ28Ub3B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sxsvyznyk1amq7oh4wty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bQ28Ub3B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sxsvyznyk1amq7oh4wty.png" alt="Multi-app run" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With release 1.10, another big developer productivity improvement has been achieved. Developers can now run multiple apps with just one command and a multi-app template file.  The &lt;code&gt;dapr run -f &amp;lt;path&amp;gt;&lt;/code&gt;  command is used to run Dapr locally, where &lt;em&gt;&lt;/em&gt; points to a directory that contains a &lt;em&gt;dapr.yaml&lt;/em&gt; file, the multi-app template, that contains the properties to run the apps. More information on how to structure the multi-app template can be found in the &lt;a href="https://docs.dapr.io/developing-applications/local-development/multi-app-dapr-run/multi-app-template/"&gt;Dapr docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Components
&lt;/h2&gt;

&lt;p&gt;Dapr uses a modular design where functionality is delivered as a component.  Release 1.10 contains both new components as improvements to existing components.&lt;/p&gt;

&lt;h3&gt;
  
  
  New Components
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-sqlite/"&gt;SQLite&lt;/a&gt;: Enables state store functionality using &lt;a href="https://www.sqlite.org/index.html"&gt;SQL Lite 3&lt;/a&gt;. SQLite is a small, fast and self-contained SQL database engine, great for local development and running on small devices.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-cloudflare-workerskv/"&gt;Cloudflare KV&lt;/a&gt;: Enables state store functionality using &lt;a href="https://developers.cloudflare.com/workers/runtime-apis/kv"&gt;Cloudflare Workers KV&lt;/a&gt;. Workers KV is a global, low-latency, key-value data store that is ideal for high read volumes.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/cloudflare-queues/"&gt;Cloudflare Queues binding&lt;/a&gt;: Enables publishing message to &lt;a href="https://developers.cloudflare.com/queues/"&gt;Cloudflare queues&lt;/a&gt;. Cloudflare queues allow developers to send and receive messages with guaranteed delivery. It integrates with Cloudflare Workers and offers at-least once delivery and message batching.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/kubemq/"&gt;KubeMQ binding&lt;/a&gt;: Supports input and output bindings for &lt;a href="https://kubemq.io/"&gt;KubeMQ&lt;/a&gt;, an enterprise-grade Kubernetes-based message broker and message queue that is scalable, highly available, and secure&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-kubemq/"&gt;KubeMQ&lt;/a&gt;: Enables pub/sub messaging using &lt;a href="https://kubemq.io/"&gt;KubeMQ&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-solace-amqp/"&gt;Solace AMQP&lt;/a&gt;: Enables pub/sub messaging using &lt;a href="https://solace.com/products/event-broker/software/"&gt;Solace PubSub+ Event Broker&lt;/a&gt;. The Solace Event Broker is a container-based message broker that enables real-time data distribution between applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Component Improvements
&lt;/h3&gt;

&lt;p&gt;With 1.10 many components have graduated to the &lt;em&gt;Stable&lt;/em&gt; certification level. All components have a certification level as mentioned in the &lt;a href="https://docs.dapr.io/operations/components/certification-lifecycle/"&gt;Certification Lifecycle&lt;/a&gt;. The &lt;em&gt;Stable&lt;/em&gt; level means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The component must have component &lt;a href="https://docs.dapr.io/operations/components/certification-lifecycle/#certification-tests"&gt;certification tests&lt;/a&gt; validating functionality and resiliency.&lt;/li&gt;
&lt;li&gt;The component is maintained by Dapr maintainers and supported by the community.&lt;/li&gt;
&lt;li&gt;The component is well documented and tested.&lt;/li&gt;
&lt;li&gt;The component has been available as Alpha or Beta for at least 1 minor version release of Dapr runtime prior.&lt;/li&gt;
&lt;li&gt;A maintainer will address component security, core functionality and test issues according to the Dapr support policy and issue a patch release that includes the patched stable component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following components have progressed to &lt;em&gt;Stable&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-aws-snssqs/"&gt;AWS SQS/SNS&lt;/a&gt;: Enables pub/sub messaging using AWS &lt;a href="https://aws.amazon.com/sqs/"&gt;SQS&lt;/a&gt; or &lt;a href="https://aws.amazon.com/sns/"&gt;SNS&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-dynamodb/"&gt;AWS DynamoDB&lt;/a&gt;: Enables state store functionality using &lt;a href="https://aws.amazon.com/dynamodb/"&gt;AWS DynamoDB&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-cockroachdb/"&gt;CockroachDB&lt;/a&gt;: Enables state store functionality using &lt;a href="https://www.cockroachlabs.com/product/"&gt;CockroachDB&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-state-stores/setup-mysql/"&gt;MySQL&lt;/a&gt;: Enables state store functionality using &lt;a href="https://www.mysql.com/"&gt;MySQL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-secret-stores/hashicorp-vault/"&gt;HashiCorp Vault&lt;/a&gt;: Enables secret store functionality using &lt;a href="https://www.hashicorp.com/products/vault"&gt;HashiCorp Vault&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-pubsub/setup-pulsar/"&gt;Pulsar&lt;/a&gt;: Enables pub/sub messaging using &lt;a href="https://pulsar.apache.org/"&gt;Apache Pulsar&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/servicebusqueues/"&gt;Azure Service Bus Queues binding&lt;/a&gt;: supports input and output binding to &lt;a href="https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-queues-topics-subscriptions"&gt;Azure Service Bus Queues&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.dapr.io/reference/components-reference/supported-bindings/cron/"&gt;Cron binding&lt;/a&gt;: Enables scheduled task execution via &lt;a href="https://en.wikipedia.org/wiki/Cron"&gt;cron expressions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re using any of these components and have a demo to show, drop a link in the &lt;em&gt;#show-and-tell&lt;/em&gt; channel on the &lt;a href="https://discord.com/invite/ptHhX6jc34"&gt;Dapr Discord&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is next?
&lt;/h2&gt;

&lt;p&gt;This post is not a complete list of features and changes released in version 1.10. Read the &lt;a href="https://blog.dapr.io/posts/2023/02/16/dapr-v1.10-is-now-available/"&gt;official Dapr release notes&lt;/a&gt; for more information. The release notes also contain information on how to upgrade to this latest version.&lt;/p&gt;

&lt;p&gt;Excited about these features and want to learn more? I’ll cover the new features in more detail in future posts. Until then, join the &lt;a href="https://discord.com/invite/ptHhX6jc34"&gt;Dapr Discord&lt;/a&gt; to connect with thousands of Dapr users.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cloudnative</category>
      <category>architecture</category>
      <category>eventdriven</category>
    </item>
  </channel>
</rss>
