<?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: Svix</title>
    <description>The latest articles on DEV Community by Svix (@svixhq).</description>
    <link>https://dev.to/svixhq</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F903421%2F7cb8521a-a203-4e5b-b2ae-7bec6b9e9c36.png</url>
      <title>DEV Community: Svix</title>
      <link>https://dev.to/svixhq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/svixhq"/>
    <language>en</language>
    <item>
      <title>APIs are a Contract. Don't Break Them.</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Tue, 06 Feb 2024 13:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/apis-are-an-unbreakable-contract-2l1k</link>
      <guid>https://dev.to/svixhq/apis-are-an-unbreakable-contract-2l1k</guid>
      <description>&lt;p&gt;APIs are an unbreakable contract between your service and its customers. While humans can (reluctantly) adjust to drastic UI redesigns, API clients will just stop working with even the slightest change. This is why it's important for API providers and API consumers to agree on how to interact.&lt;/p&gt;

&lt;p&gt;Because this contract can't change unilaterally, being precise and strict is extremely important. Undefined behavior is de-facto defined behavior, and if you are too lenient with what you accept, you will forever be stuck supporting all the unpredictable ways people use your APIs.&lt;/p&gt;

&lt;p&gt;When talking about APIs I often hear people quoting the robustness principle: "be conservative in what you send, be liberal in what you accept". I strongly disagree with this principle, as it leads to fragile, insecure, and inefficient systems.&lt;/p&gt;

&lt;p&gt;For example, consider an API endpoint that expects a query parameter called &lt;code&gt;sort&lt;/code&gt; which according to the docs accepts the values &lt;code&gt;asc&lt;/code&gt; for ascending, and &lt;code&gt;desc&lt;/code&gt; for descending.&lt;/p&gt;

&lt;p&gt;Here is an example psuedo-code implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;list_items&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&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="nx"&gt;sort&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;asc&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;return&lt;/span&gt; &lt;span class="nf"&gt;fetch_sorted_asc&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;return&lt;/span&gt; &lt;span class="nf"&gt;fetch_sorted_desc&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;This function, as you can see accepts &lt;code&gt;sort&lt;/code&gt; as a string, and then checks the value to sort the data accordingly. The problem is that the input is not validated to be one of two values, so in practice &lt;code&gt;Desc&lt;/code&gt;, &lt;code&gt;descending&lt;/code&gt;, and &lt;code&gt;dscnding&lt;/code&gt; will all lead to the input correctly being sorted in a descending manner. Which in turn means that anyone that uses this API incorrectly like this will see it working correctly and therefore will start relying on this behavior.&lt;/p&gt;

&lt;p&gt;This means we are now stuck supporting endless variations of &lt;code&gt;desc&lt;/code&gt; until the end of time without even realizing. Which most likely also means we are going to break some implementations by accident when we change this code without even realizing it.&lt;/p&gt;

&lt;p&gt;Additionally, it's easy to become more lenient later, but it's impossible to become more strict. So if you start more strict you keep your options open, if you start more lenient you don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formulating the contract
&lt;/h2&gt;

&lt;p&gt;As I said above, APIs are a contract, which is why it's important to formulate that contract as well as humanly (computerly?) possible. Therefore having an OpenAPI spec for your API goes a long way. Preferably also having validation that the actual implementation is compatible with the spec.&lt;/p&gt;

&lt;p&gt;You should also be as specific as you can with both the definition, and therefore the validation of the fields. For example, the &lt;a href="https://www.svix.com"&gt;Svix&lt;/a&gt; user defined IDs (&lt;code&gt;uid&lt;/code&gt;s) follow a specific pattern: they have to be between 1 and 256 characters long, and match this regex &lt;code&gt;^[a-zA-Z0-9\-_.]+$&lt;/code&gt;. This is both documented in the spec and validated at runtime.&lt;/p&gt;

&lt;p&gt;The advantage of having it documented in the spec, is that you can generate rich and specific documentation for your customers. Your customers don't need to guess what values are allowed for a &lt;code&gt;uid&lt;/code&gt;, they can see it right there.&lt;/p&gt;

&lt;p&gt;Validating the spec at runtime, when API calls are made, means that even if someone made a mistake and accidentally passed the wrong value, this will be caught immediately and they would be able to rectify the mistake.&lt;/p&gt;

&lt;p&gt;Another advantage of having an OpenAPI spec, is that your API consumers can also generate validation on their end. Having schemas type checked on their end at compile time, so issues never hit production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tagging IDs with their type
&lt;/h2&gt;

&lt;p&gt;Many APIs use &lt;code&gt;uuid&lt;/code&gt;s internally as their ID representation, which they then expose in their APIs. Those &lt;code&gt;uuid&lt;/code&gt;s usually look something like this: &lt;code&gt;12455207-ab83-5602-8d93-67999e204cff&lt;/code&gt;. First of all, I think the base62 representation (not a typo, it's 62) is much nicer: &lt;code&gt;2bd6zcCxa8v3fx1nj9XVlQp3WR3&lt;/code&gt;. It's more concise and more aesthetically pleasing, but to each their own.&lt;/p&gt;

&lt;p&gt;The problem with using &lt;code&gt;uuid&lt;/code&gt;s, whether in their standard string representation or the base62 one, is that they are just generic IDs. This means that you can accidentally use an ID in the wrong place (which will usually return a &lt;code&gt;404&lt;/code&gt;) and be very confused on why it doesn't work.&lt;/p&gt;

&lt;p&gt;A much better system is to tag IDs with their type, so the example above when tagged would look like this: &lt;code&gt;app_2bd6zcCxa8v3fx1nj9XVlQp3WR3&lt;/code&gt; for an "application" and &lt;code&gt;msg_2bd6zcCxa8v3fx1nj9XVlQp3WR3&lt;/code&gt; for a message.&lt;/p&gt;

&lt;p&gt;Tagged IDs mean that you can have strict validation in your API and great examples in your docs that show exactly the kind of ID that's expected. Passing the wrong ID will no longer result in a &lt;code&gt;404&lt;/code&gt;, but rather with a validation error that explains exactly what's going on.&lt;/p&gt;

&lt;p&gt;This makes debugging much much easier, but it also makes support much easier. Because it makes it very obvious when someone is accidentally passing the wrong IDs.&lt;/p&gt;

&lt;h2&gt;
  
  
  It's a wrap
&lt;/h2&gt;

&lt;p&gt;This post was cross-posted from the &lt;a href="https://5x9s.svix.com/"&gt;the 5x9s newsletter&lt;/a&gt;. Subscribe for more content like this sent directly to your inbox.&lt;/p&gt;




&lt;p&gt;For more content like this, make sure to follow us on &lt;a href="https://twitter.com/SvixHQ"&gt;Twitter&lt;/a&gt;, &lt;a href="https://github.com/svix"&gt;Github&lt;/a&gt; or &lt;a href="https://www.svix.com/blog/rss/"&gt;RSS&lt;/a&gt; for the latest updates for the &lt;a href="https://www.svix.com"&gt;Svix webhook service&lt;/a&gt;, or join the discussion on &lt;a href="https://www.svix.com/slack/"&gt;our community Slack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>technical</category>
      <category>5x9s</category>
    </item>
    <item>
      <title>The State of Webhooks 2023</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Wed, 18 Oct 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/the-state-of-webhooks-2023-239d</link>
      <guid>https://dev.to/svixhq/the-state-of-webhooks-2023-239d</guid>
      <description>&lt;p&gt;As software continues to eat the world, an increasing emphasis on real-time information, seamless integrations, and automation has propelled webhooks to the forefront of modern application architectures.&lt;br&gt;
Webhooks, are now a core component in driving real-time event based workflows across platforms. You can &lt;a href="https://www.svix.com/state-of-webhooks/"&gt;check out the State of Webhooks here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Special thanks to Ojus Save from Zoom, Judy Vander Sluis from Intuit, Sharon Yelenik from Cloudinary, Sarah Edwards from Github, and Kanad Gupta from ReadMe for collaborating with us on our webhook documentation reviews.&lt;br&gt;
It really helped us understand how people think about documenting their webook APIs and informed a lot of our decisions when creating this report.&lt;/p&gt;

&lt;p&gt;In this comprehensive report on the State of Webhooks in 2023, we looked at over 100 of the top API providers, examining how they've embraced and implemented webhooks, and analyzing the diverse ways these implementations have taken form.&lt;br&gt;
Are most leading API providers on board with best practices?&lt;br&gt;
Beyond mere adoption, how have they optimized, secured, and enriched their webhook offerings to cater to the needs of today’s developers and businesses?&lt;/p&gt;

&lt;p&gt;Recognizing that ground truths often emerge in real-world use-cases, we've tapped into our own customer base, compiling an intriguing cache of statistics that shed light on the realities of webhook deliveries.&lt;br&gt;
How often do they falter in the wild? How many retries do these messages generally need before they successfully land in their destined applications?&lt;br&gt;
These firsthand statistics not only offer a clear picture of current delivery success rates but also demonstrate the value of implementing best practices.&lt;/p&gt;

&lt;p&gt;Webhooks are more than mere HTTP callbacks; they embody a commitment to proactive, real-time communication in a world that's growing ever more interconnected.&lt;br&gt;
With this report, we invite you to explore the current state of webhooks, and hope to spur continued adoption of webhook best practices.&lt;/p&gt;

&lt;p&gt;You can &lt;a href="https://www.svix.com/state-of-webhooks/"&gt;get access to the full report here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>discuss</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Strong static typing, a hill I'm willing to die on...</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Wed, 04 Oct 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/strong-static-typing-a-hill-im-willing-to-die-on-2903</link>
      <guid>https://dev.to/svixhq/strong-static-typing-a-hill-im-willing-to-die-on-2903</guid>
      <description>&lt;p&gt;I've been writing software for over 20 years, and with every day that goes by I grow more certain that strong static typing is not just a good idea, but also almost always the right choice.&lt;/p&gt;

&lt;p&gt;There are definitely uses for untyped languages (or language variants), for example they are much nicer when using a REPL, or for throwaway scripts in environments that are already hopelessly untyped (e.g. the shell). In almost every other case, however, strong typing is strongly preferred.&lt;/p&gt;

&lt;p&gt;There are advantages to not using types, such as a faster development speed, but they pale in comparison to all of the advantages. To that I say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Writing software without types lets you go at full speed. Full speed towards the cliff.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The question around strong static typing is simple: would you rather work a bit more and get invariants checked at compile-time (or type-checking time for non-compiled languages), or work a bit less and have them be enforced at runtime, or even worse not enforced even at runtime (JavaScript, I'm looking at you... &lt;code&gt;1 + "2" == 12&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Getting errors at runtime is a terrible idea. First, it means that you won't always catch them during development. Second, when you do catch them, it will happen in a customer facing manner. Yes, tests help, but writing tests for every possible mistyped function parameter is impossible given the endless possibilities. Even if you could, having types is much easier than testing for wrong types.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types lead to less bugs
&lt;/h2&gt;

&lt;p&gt;Types also offer annotations to code that benefit both humans and machines. Having types is a way to more strictly define the contract between different pieces of code.&lt;/p&gt;

&lt;p&gt;Consider the following four examples. They all do exactly the same thing just with varying level of contract definition.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Params: Name (a string) and age (a number).&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting1&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;params&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&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="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Params: Name (a string) and age (a number).&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&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="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first one doesn't even define the number of parameters, so it's hard to know what it does without reading the docs. I believe most people will agree the first one is an abomination and wouldn't write code like that. Though it's a very similar idea to typing, it's about defining the contract between the caller and the callee.&lt;/p&gt;

&lt;p&gt;As for the second and the third, because of the typing, the third will need less documentation. The code is simpler, but admittedly, the advantages are fairly limited. Well, until you actually change this function...&lt;/p&gt;

&lt;p&gt;In both the second and the third functions, the author assumes the age is a number. So it is absolutely fine to change the code as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Params: Name (a string) and age (a number).&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; will turn &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;age&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="s2"&gt; next year!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&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="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; will turn &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;age&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="s2"&gt; next year!`&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 problem is that some of the places that use this code accept user input which was collected from an HTML input (so always a string). Which will result in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;20&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John will turn 201 next year!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While the typed version will correctly fail to compile because this function excepts age to be a number, not a string.&lt;/p&gt;

&lt;p&gt;Having the contract between a caller and callee is important for a codebase, so that callers can know when callees change. This is especially important for an open source library, where the callers and the callees are not written by the same group of people. Without this contract it's impossible to know how things change when they do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types lead to a better development experience
&lt;/h2&gt;

&lt;p&gt;Typing can also be used by IDEs and other development tools to vastly improve the development experience. You get notified as you code if any of your expectations are wrong. This significantly reduces cognitive load. You no longer need to remember the types of all the variables and the function in the context. The compiler will be there with you and tell you when something is wrong.&lt;/p&gt;

&lt;p&gt;This also leads to a very nice additional benefit: easier refactoring. You can trust the compiler to let you know whether a change you make (e.g. the change in our example above) will break assumptions made elsewhere in the code or not.&lt;/p&gt;

&lt;p&gt;Types also make it much easier to onboard new engineers to a codebase or library:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They can follow the type definitions to understand where things are used.&lt;/li&gt;
&lt;li&gt;It's much easier to tinker with things as changes will trigger a compile error.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's consider the following changes to our above code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; will turn &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&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="s2"&gt; next year!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; will turn &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&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="s2"&gt; next year!`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&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;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&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&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;birthdayGreeting2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;birthdayGreeting3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&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;It's very easy to see (or use your IDE to find) all the places where &lt;code&gt;Person&lt;/code&gt; is used. You can see it's initiated in &lt;code&gt;main&lt;/code&gt; and you can see it's used by &lt;code&gt;birthdayGreeting3&lt;/code&gt;. However, in order to know it's used in &lt;code&gt;birthdayGreeting2&lt;/code&gt;, you'd need to read the entire codebase.&lt;/p&gt;

&lt;p&gt;The flip side of this is also that when looking at &lt;code&gt;birthdayGreeting2&lt;/code&gt;, it's hard to know that it expects a &lt;code&gt;Person&lt;/code&gt; as a parameter. Some of these things can be solved by exhaustive documentation, but: (1) why bother if you can achieve more with types? (2) documentation goes stale, here the code is the documentation.&lt;/p&gt;

&lt;p&gt;It's very similar to how you wouldn't write code like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// a is a person&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;birthdayGreeting2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; will turn &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&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="s2"&gt; next year!`&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;You would want to use useful variable names. Typing is the same, it's just variable names on steriods.&lt;/p&gt;

&lt;h2&gt;
  
  
  We encode everything in the type system
&lt;/h2&gt;

&lt;p&gt;At Svix we love types. In fact, we try to encode as much information as we possibly can in the type system, so that all of the errors that can be caught at compile time will be caught at compile time; and also to squeeze that extra mileage of developer experience improvements.&lt;/p&gt;

&lt;p&gt;For example, Redis is a string based protocol with no inherent typing. We use Redis for caching (among other things). The problem is that all of our nice typing benefits will be lost at the Redis layer, and bugs can happen.&lt;/p&gt;

&lt;p&gt;Consider the following piece of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"p123"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"person-{id}"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"preson-{id}"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a couple of bugs in the snippet:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There's a typo in the second key name.&lt;/li&gt;
&lt;li&gt;We are trying to load a person into a pet type.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To avoid such issues we do two things at Svix. The first is that we require the key to be of a certain type (not a generic string), and to create this type you need to call a specific fuction. The second thing we do, is force pairing a key to a value.&lt;/p&gt;

&lt;p&gt;So the above example would look something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;PersonCacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;PersonCacheKey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&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="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&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="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;PetCacheKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"p123"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Person&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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="s"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;PersonCacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="c1"&gt;// Compilation will fail on the next line&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;PersonCacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is already much better, and makes it impossible to get either of the previously mentioned bugs. Though we can do even better!&lt;/p&gt;

&lt;p&gt;Consider the following function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_something&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="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;person&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;PersonCacheKey&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a couple of problems with it. The first is that it's not very clear which &lt;code&gt;id&lt;/code&gt; it should be for. Is it a person? A pet? It's very easy to accidentally call it with the wrong one, like in the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pet&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- should be pet.owner!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second is that we are losing the discoverability. It's a bit hard to know that a Pet has a relationship to a Person.&lt;/p&gt;

&lt;p&gt;So at Svix, we have a special type for each &lt;code&gt;id&lt;/code&gt; to ensure that there are no mistakes. The adjusted code looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;PersonId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;PetId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&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;PersonId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Pet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&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;PetId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PersonId&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;This is indeed much better than our previous example.&lt;/p&gt;

&lt;p&gt;There is still one issue. If we accept the &lt;code&gt;id&lt;/code&gt;s from the API, how do we know that they are valid? All of the pet &lt;code&gt;id&lt;/code&gt;s in Svix, for example, are prefixed with &lt;code&gt;pet_&lt;/code&gt; and are then followed by a Ksuid like so: &lt;code&gt;pet_25SVqQSCVpGZh5SmuV0A7X0E3rw&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We want to be able to tell our customers that they pass the wrong &lt;code&gt;id&lt;/code&gt; in the API, e.g. they pass a person &lt;code&gt;id&lt;/code&gt; when a pet one is expected. One simple solution for this is to validate this (duh...) but it can be easy to forget to validate it everywhere that it's used.&lt;/p&gt;

&lt;p&gt;So we enforce that a &lt;code&gt;PetId&lt;/code&gt; can never be created without first being validated. This way we know that all of the code paths that create a &lt;code&gt;PetId&lt;/code&gt; first make sure it's valid. This means that when we return a &lt;code&gt;404 Not Found&lt;/code&gt; to a customer because a pet isn't found in the database, we can be sure it was actually a valid &lt;code&gt;id&lt;/code&gt; that wasn't found in the database. If it wasn't a valid &lt;code&gt;id&lt;/code&gt;, we would have already returned a &lt;code&gt;422&lt;/code&gt; or &lt;code&gt;400&lt;/code&gt; when it was passed to the API handlers.&lt;/p&gt;

&lt;h2&gt;
  
  
  So why doesn't everyone like types?
&lt;/h2&gt;

&lt;p&gt;The main topic of argument against types are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Development speed&lt;/li&gt;
&lt;li&gt;Learning curve and types complexity&lt;/li&gt;
&lt;li&gt;The amount of effort and boilerplate required&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First of all, I'd argue that even if all of the above were true, the advantages mentioned above are well worth the trouble. Though I also don't agree with all of the above.&lt;/p&gt;

&lt;p&gt;The first one is development speed. Prototyping without types is definitely much faster. You can comment out pieces of the code and won't have a compiler complain to you. You can set the wrong values for some of the fields until you're ready to figure out the right ones, etc.&lt;/p&gt;

&lt;p&gt;Though like I said above: "Writing software without types lets you go at full speed. Full speed towards the cliff." The problem is that this is just aggressive and unnecessary technical debt. You'll pay it mulitple times over when you need to debug why your code doesn't work (either locally, in the test suite, or in production).&lt;/p&gt;

&lt;p&gt;As for the learning curve: Yes, learning more things takes time. Though I'd say that most people don't need to be typing experts. They can just get by with very simple type expressions, and ask if they ever hit a wall. However, if you keep things simple, you'll probably rarely if ever hit one.&lt;/p&gt;

&lt;p&gt;Additionally, people are already required to learn how to code, learn frameworks (React, Axum, etc.), and so many other things. I don't think the learning burden is as significant as it's made out to be.&lt;/p&gt;

&lt;p&gt;Last, but not least, regarding the learning curve: I strongly believe that the benefits of a lessened learning curve by not having to know types, are much less than the benefits of using the type script to onboard on a specific codebase. Especially since learning types is a one time cost.&lt;/p&gt;

&lt;p&gt;The last point is about the amount of effort and boilerplate required to use types in your codebase. I strongly believe that the amount of effort is actually less than the amount of effort required by not writing types.&lt;/p&gt;

&lt;p&gt;Not using types requires significant documentation and testing in order to reach even a basic level of sanity. Documentation can go stale, and so can testing; and either way they require more effort than just adding the right types. Reading code with types is also easier, because you get the types inline instead of in the function documentation where it's in an inconsistent format with a lot of added noise.&lt;/p&gt;

&lt;p&gt;Yes, typing can be a pain in languages that don't support inference, e.g Java can be tedious:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; It seems that Java has type inference nowadays. Thanks &lt;code&gt;pron&lt;/code&gt; for &lt;a href="https://news.ycombinator.com/item?id=37765419"&gt;the correction on HN&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newPerson&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newPerson&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Person&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;makeChild&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while other languages that have inference (like Rust), are much nicer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_person&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;new_person&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So having the right tools definitely helps.&lt;/p&gt;

&lt;p&gt;Speaking of tools, in order to reap the benefits of your typing, you probably need to use a code editor (or IDE) that supports modern code completion that is language aware.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;I can see both side of the arguments on many topics, such as &lt;code&gt;vim&lt;/code&gt; vs. &lt;code&gt;emacs&lt;/code&gt;, tabs vs. spaces, and even much more controversial ones. Though in this case, the costs are so low compared to the benefits that I just don't understand why anyone would ever choose not to use types.&lt;/p&gt;

&lt;p&gt;I'd love to know what I'm missing, but until then: Strong typing is a hill I'm willing to die on.&lt;/p&gt;




&lt;p&gt;For more content like this, make sure to follow us on &lt;a href="https://twitter.com/SvixHQ"&gt;Twitter&lt;/a&gt;, &lt;a href="https://github.com/svix"&gt;Github&lt;/a&gt; or &lt;a href="https://www.svix.com/blog/rss/"&gt;RSS&lt;/a&gt; for the latest updates for the &lt;a href="https://www.svix.com"&gt;Svix webhook service&lt;/a&gt;, or join the discussion on &lt;a href="https://www.svix.com/slack/"&gt;our community Slack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typing</category>
      <category>rust</category>
      <category>typescript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Fixing Webhooks With New Funding From Andreessen Horowitz</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Wed, 22 Feb 2023 12:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/fixing-webhooks-with-new-funding-from-andreessen-horowitz-3pka</link>
      <guid>https://dev.to/svixhq/fixing-webhooks-with-new-funding-from-andreessen-horowitz-3pka</guid>
      <description>&lt;p&gt;We are very excited to announce that we have raised a new round of funding led by &lt;a href="https://a16z.com/"&gt;Andreessen Horowitz&lt;/a&gt; (a16z) with participation from &lt;a href="https://www.ycombinator.com/continuity"&gt;Y Combinator Continuity&lt;/a&gt; and existing investors.&lt;/p&gt;

&lt;p&gt;This fundraise comes at the back of our impressive growth, as we are now sending &lt;strong&gt;billions of webhooks a year&lt;/strong&gt; for some of the most sophisticated engineering teams at companies both large (Fortune 500) and small (startups). Companies like &lt;strong&gt;Brex&lt;/strong&gt;, &lt;strong&gt;Lob&lt;/strong&gt;, &lt;strong&gt;Benchling&lt;/strong&gt;, &lt;strong&gt;Bizzabo&lt;/strong&gt;, and &lt;strong&gt;LTSE Equity&lt;/strong&gt;, all use Svix to power their webhooks.&lt;/p&gt;

&lt;p&gt;We have gotten to know the a16z and YC Continuity teams well over the last few months, and we are excited to be working with them for years to come, as well as continuing working with our amazing existing investors: &lt;a href="https://aleph.vc/"&gt;Aleph&lt;/a&gt;, &lt;a href="https://www.ycombinator.com/"&gt;Y Combinator&lt;/a&gt;, Andrew Miklas, Yuri Sagalov (&lt;a href="https://www.wayfinder.com/"&gt;Wayfinder&lt;/a&gt;), Kevin Mahaffey (&lt;a href="https://snr.vc/"&gt;SNR&lt;/a&gt;), Christopher Golda (&lt;a href="https://rogue.capital/"&gt;Rogue Capital&lt;/a&gt;), Jason Warner, Ian Storm Taylor, Kurt Mackey, Hana Mohan, Holly Dunlap, and Michael Orland.&lt;/p&gt;

&lt;p&gt;Today is an important milestone towards our mission of making server-to-server communication easy, secure, and reliable. So I want to take this opportunity to thank the team for getting us to where we are today. I love waking up every day and being surrounded by such smart and passionate individuals, serving amazing customers, and working with the best investors out there!&lt;/p&gt;

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

&lt;p&gt;To talk about Svix, we first need to talk about webhooks. Webhooks are how services communicate; you can think of them as sort of a reverse API, or as the API equivalent of push notifications. They power most of the products that we all use on a daily basis. For example: when you make a payment online, a webhook is sent from the payment provider to the merchant; when you push a commit to Github, a webhook is sent to the CI providers to trigger a build; and when you use a workflow automation tool like Zapier, the workflows are almost always triggered by webhooks.&lt;/p&gt;

&lt;p&gt;Webhooks have also become an increasingly common customer demand, both by API providers and more importantly API consumers. We see this trend across all industries, where customers expect webhooks in order to be able to take action on events without having to poll for updates. It makes sense, as webhooks enable building workflows on top of products, turning them from useful into critical infrastructure, increasing both stickiness and usefulness.&lt;/p&gt;

&lt;p&gt;The problem, however, is that building a reliable webhook service requires a lot more engineering time, resources, and ongoing maintenance than you first expect. Which is especially painful, as it diverts valuable resources from improving the product, to reinventing infrastructure. We believe webhooks will follow a similar development pattern to other internet infrastructure—like email, web servers, and CMS—which only a few teams build on their own.&lt;/p&gt;

&lt;p&gt;That's where Svix comes in. With Svix, companies can build a secure, reliable, and scalable webhooks solution in minutes. Saving them a lot of time, effort, and money, while focusing on their business and increasing team happiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking back
&lt;/h2&gt;

&lt;p&gt;Milestones are always a great time to reflect on the past. Looking back, I'm really proud of the product we have built, and how we've been supporting our customers. It never gets old to hear "wow, that was really fast" when we release a new feature shortly after discussing it with a customer. We deserve some credit for that (and we work hard to earn it), but a lot of the credit also belongs to our customers. We are very lucky to have such engaged customers, and doubly lucky to have them be developers. We often get very detailed feedback, specced out feature requests, and of course pull requests for both the code and the docs.&lt;/p&gt;

&lt;p&gt;I think this has also been the secret to why our customers love the product so much. It's because they helped us build it every step of the way, and they still do to this day. The same way it never gets old to hear about how fast we move with product development, it also never gets old to hear about how people use the product and how much they love it.&lt;/p&gt;

&lt;blockquote&gt;
  {/* prettier-ignore */}
  &lt;q&gt;This is by far one of the best products I've ever worked with. It does one thing but it does it
    so well that I can't imagine ever doing my own webhooks solution.&lt;/q&gt;
  &lt;span&gt;— Senior Engineering Manager at Bizzabo&lt;/span&gt;
&lt;/blockquote&gt;

&lt;p&gt;One super important but often under appreciated aspect of what we do, is the relentless focus on stability and scalability. We spend a lot of time and work really hard to ensure that we provide our customers with a robust service at the scale they need. Whether it's the quality of the code, the test suite, the infrastructure, or our SDLC, we always hold ourselves to the highest standards. It's one of those things that no one notices when you do your job well, but are essential for any infrastructure service.&lt;/p&gt;

&lt;p&gt;At the end of the day, however, it's not about what we have built, but rather how we are affecting the lives of our customers. Thanks to Svix our customers can offer a state of the art webhooks solution to their customers at a fraction of the time and cost it would take to build it themselves, with no ongoing maintenance, while getting constant improvements and automatic scaling. All of this while freeing their engineering teams to work on their core business. For more on that, you can see what &lt;a href="https://www.lob.com/blog/lob-webhooks-the-remix"&gt;Lob had to say about their experience switching to Svix&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking forward
&lt;/h2&gt;

&lt;p&gt;We have identified an important (and growing) problem, built a great product to solve it, and started a whole new vertical along the way (webhooks as a service). This is why our customers, our investors, and the Svix team are so excited about webhooks and what we are building.&lt;/p&gt;

&lt;p&gt;We have an opportunity to become an industry defining company, which is why we have decided to raise this round of funding, and partner with some of the best investors out there. We are going to use the money and the support of our new investors, to further expand the team and improve the product to keep up with the demand and our ever evolving customer needs.  We have a lot of exciting stuff lined up, and we can't wait to share them!&lt;/p&gt;

&lt;p&gt;If you are passionate about developer tools, webhooks, and server-to-server communication and you think you may be a good fit, please check out our &lt;a href="https://www.svix.com/careers/"&gt;careers page&lt;/a&gt;. If your company is looking to start sending webhooks, or upgrade their existing legacy solution, &lt;a href="https://www.svix.com/"&gt;try us out&lt;/a&gt; or &lt;a href="https://docs.svix.com/"&gt;check out our docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;💬 Join our Slack &lt;a href="https://www.svix.com/slack"&gt;https://www.svix.com/slack&lt;/a&gt; &lt;br&gt;&lt;br&gt;
📄 Check out our docs &lt;a href="https://docs.svix.com/"&gt;https://docs.svix.com/&lt;/a&gt; &lt;br&gt;&lt;br&gt;
🪝 Start sending webhooks in minutes &lt;a href="https://www.svix.com"&gt;https://www.svix.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>news</category>
    </item>
    <item>
      <title>The What, Why, and How of Payload Transformations</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Wed, 08 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/the-what-why-and-how-of-payload-transformations-3i1a</link>
      <guid>https://dev.to/svixhq/the-what-why-and-how-of-payload-transformations-3i1a</guid>
      <description>&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%2F9sbwvfii8ludr1atd6jq.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%2F9sbwvfii8ludr1atd6jq.png" alt="Transformations" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't already know, we've had a new feature available in beta for some time now called&lt;br&gt;
&lt;strong&gt;Transformations&lt;/strong&gt;. I was one of the primary contributors to this feature, and am hoping to explain&lt;br&gt;
to you what they are, why they're useful, and how to use them.&lt;/p&gt;

&lt;p&gt;Later, in another post, I'll be doing a deeper dive into how transformations work under the hood for&lt;br&gt;
those of you interested in the implementation.&lt;/p&gt;
&lt;h2&gt;
  
  
  A scenario to start
&lt;/h2&gt;

&lt;p&gt;Say you're a Svix customer who offers an e-commerce software suite. Your customers are small, online&lt;br&gt;
businesses who sell and send out physical goods. You use Svix to notify these customers of important&lt;br&gt;
events: a new order has been made, a support ticket has been sent, a charge-back has occurred, etc.&lt;/p&gt;

&lt;p&gt;All of these events need to be routed to various places in specific formats. This can be done by&lt;br&gt;
defining event type filters per endpoint. Then you, the company offering this e-commerce system, can&lt;br&gt;
dispatch an &lt;code&gt;order.created&lt;/code&gt; event, a &lt;code&gt;ticket.created&lt;/code&gt; event, or an &lt;code&gt;order.chargeback&lt;/code&gt; event.&lt;/p&gt;

&lt;p&gt;However, what do you do when your customers need the same event sent in different formats? For&lt;br&gt;
example, your customer may need the &lt;code&gt;order.created&lt;/code&gt; event sent to several places.&lt;/p&gt;

&lt;p&gt;They may have an application tracking inventory and another application keeping statistics on the&lt;br&gt;
types of products sold.&lt;/p&gt;

&lt;p&gt;But, they might also need its details sent via email, in a human readable format, to their&lt;br&gt;
warehouse, such that the product is shipped promptly.&lt;/p&gt;

&lt;p&gt;The naive solution is to offer more event types. Even then, the default webhook format may not match&lt;br&gt;
your customers' exact needs. There could be too many combinations of formats and events to manage as&lt;br&gt;
a Svix customer. And then, you may not want to create event types just for one of your customers who&lt;br&gt;
wants to do something a bit different.&lt;/p&gt;

&lt;p&gt;With transformations, it's so much easier for your customers to make these customizations.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introducing transformations
&lt;/h2&gt;

&lt;p&gt;In brief, transformations allow your consumers to apply changes to messages before they're&lt;br&gt;
dispatched to any given endpoint. These changes -- or transformations -- are defined via JavaScript&lt;br&gt;
code that runs per message dispatched for any endpoint where the feature is enabled.&lt;/p&gt;

&lt;p&gt;In the scenario presented above, a customer can very easily alter the content of each&lt;br&gt;
&lt;code&gt;order.created&lt;/code&gt; message per destination using a short JS function. Say that the email dispatch of&lt;br&gt;
order details can be made with an HTTP request: then your customer, with no work on your end, can&lt;br&gt;
just define a transformation to reorganize the payload into that of the email service's required&lt;br&gt;
schema.&lt;/p&gt;
&lt;h3&gt;
  
  
  Let's get this out of the way -- the cost
&lt;/h3&gt;

&lt;p&gt;Good news: Transformations are free and unlimited.&lt;/p&gt;

&lt;p&gt;Bad news: Transformations are free and unlimited for &lt;em&gt;now&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Since the feature is in beta, we don't want to charge any additional costs for something that may&lt;br&gt;
not work entirely as expected. But because this is quite a computationally expensive feature to&lt;br&gt;
offer, transformations will incur some additional cost once we're confident the feature is&lt;br&gt;
production-ready. The pricing has not been finalized, but we intend for it to be very reasonable.&lt;/p&gt;
&lt;h3&gt;
  
  
  Limitations
&lt;/h3&gt;

&lt;p&gt;We have added a few restrictions in order to ensure that &lt;em&gt;all&lt;/em&gt; customer data is secure and isolated,&lt;br&gt;
as well as some limitations that ensure that everyone can use the feature.&lt;/p&gt;

&lt;p&gt;For one, precautions exist so you cannot access any:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Files&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Networking&lt;/li&gt;
&lt;li&gt;Generally any other I/O&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This shouldn't come up in non-malicious use of the feature very often, but it's definitely a needed&lt;br&gt;
restriction.&lt;/p&gt;

&lt;p&gt;Additionally, there are hard limits on RAM use and processing time, so no loops that run a million&lt;br&gt;
times. We wanted to make sure nobody can starve other customers' access to this feature.&lt;/p&gt;

&lt;p&gt;Finally, you may not use &lt;code&gt;async&lt;/code&gt; JavaScript in these scripts. No changes you make should be IO&lt;br&gt;
bound, though, so we hope that's not too much of a problem.&lt;/p&gt;

&lt;p&gt;In the future these restrictions may be eased, but only after I can ensure nothing is easily abused.&lt;/p&gt;

&lt;p&gt;Beside these three points, you can manipulate the webhook dispatched in practically any way you'd&lt;br&gt;
ever want.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to use them
&lt;/h2&gt;

&lt;p&gt;You can see the docs on this feature &lt;a href="https://docs.svix.com/transformations" rel="noopener noreferrer"&gt;here&lt;/a&gt; for a more&lt;br&gt;
concise explanation, but I have some examples that may help you understand the feature.&lt;/p&gt;

&lt;p&gt;First of all, transformations have to be explicitly enabled per environment. You can get there via&lt;br&gt;
the Settings page on the dashboard. Under the &lt;strong&gt;Settings&lt;/strong&gt; section, enter the &lt;strong&gt;General Settings&lt;/strong&gt;&lt;br&gt;
page. From there, you can simply flip the &lt;strong&gt;Enable Transformations&lt;/strong&gt; switch to turn it on or off.&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%2Fmg6v83sld7uchxn4q0ld.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%2Fmg6v83sld7uchxn4q0ld.png" alt="Image description" width="800" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the app portal, assuming your organization allows transformations as mentioned above, your&lt;br&gt;
customers can enter the &lt;strong&gt;Endpoints&lt;/strong&gt; section, click on any endpoint, then enter the &lt;strong&gt;Advanced&lt;/strong&gt;&lt;br&gt;
tab. In this tab, you can enable and edit a transformation for that one endpoint.&lt;/p&gt;

&lt;p&gt;This brings up an entire JavaScript editor with validation and test events so consumers can ensure&lt;br&gt;
their transformations work &lt;em&gt;just&lt;/em&gt; how they want the transformations to work.&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%2Fj8r482ydmcafnm1sfdsq.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%2Fj8r482ydmcafnm1sfdsq.png" alt="Image description" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From this editor you are given the following template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * @param webhook the webhook object
 * @param webhook.method destination method. Allowed values: "POST", "PUT"
 * @param webhook.url current destination address
 * @param webhook.eventType current webhook Event Type
 * @param webhook.payload JSON payload
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// modify the webhook object...&lt;/span&gt;

  &lt;span class="c1"&gt;// and return it&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time a webhook is sent through an endpoint which has a script defined, the &lt;code&gt;handler&lt;/code&gt; function&lt;br&gt;
in the script is called. This function is expected to take one argument -- called &lt;code&gt;webhook&lt;/code&gt; in the&lt;br&gt;
example above. As it states in the comment, this &lt;code&gt;webhook&lt;/code&gt; variable has the following members:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;method&lt;/code&gt; -- The HTTP method the webhook should be sent with. It can be either POST or PUT, but
this defaults to POST.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt; -- The URL to send the webhook too. This can be any arbitrary URL that meets the
requirements of the environment (so no plain HTTP requests if &lt;strong&gt;HTTPS Only Endpoints&lt;/strong&gt; is
enabled in the general settings page).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eventType&lt;/code&gt; -- If you're familiar at all with Svix, you know that each message is associated
with an "event type" -- an arbitrary identifier that is used to mark the type of webhook. You
can change it here to further modify the final payload.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payload&lt;/code&gt; -- Finally, the actual message contents can be programmatically manipulated. The
payload, however, be a valid JSON value both before and after transformations are applied.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;webhook&lt;/code&gt; value given to the handler is mutable, and it should be returned after the requisite&lt;br&gt;
alterations have been made.&lt;/p&gt;
&lt;h3&gt;
  
  
  Examples
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Example 1
&lt;/h4&gt;

&lt;p&gt;Say you're a client of the hypothetical e-commerce service mentioned earlier. You want to send a&lt;br&gt;
notification to some service every time an order has been paid for so you can know what product to&lt;br&gt;
package and ship.&lt;/p&gt;

&lt;p&gt;But there's a catch: this service expects that the product ID be encoded in the URL's path instead&lt;br&gt;
of in the payload.&lt;/p&gt;

&lt;p&gt;Well, luckily, with transformations, you don't need your own server to forward requests that need&lt;br&gt;
this tiny adjustment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webhook&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="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invoice.paid&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://a-valid.url/product/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/purchased/&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;return&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Example 2
&lt;/h4&gt;

&lt;p&gt;Need more invasive manipulations to the payload? Consider this example: You're again the client of&lt;br&gt;
the hypothetical e-commerce service. This time, you want a message sent via a company-wide chat app&lt;br&gt;
to a channel that sends a message every time you get an order, but it also has to scrub PII from the&lt;br&gt;
data.&lt;/p&gt;

&lt;p&gt;The format this chat application requires may be vastly different from the schema of the webhook the&lt;br&gt;
e-commerce service sends. Say the default webhook looks something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"product_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prod_abc123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"address_1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123 Fake St"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"address_2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RealCityName"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"IAmRunningOutOfFakeNames"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"zip_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12345-6789"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But then the chat application requires this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"channel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"some-message"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Transformations are to the rescue again! Combined with our custom headers feature allowing you to&lt;br&gt;
send any API tokens needed with the request, you could use the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;webhook&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="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;invoice.paid&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;we-got-an-order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;first_name&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; just ordered &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; products!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Transformations allow your customers to modify select aspects of a webhook, such as the payload and&lt;br&gt;
URL, before it's dispatched by writing short JavaScript functions,which can be written in the app&lt;br&gt;
portal or updated with the Svix API.&lt;/p&gt;

&lt;p&gt;Transformations are an excellent feature for interfacing with different applications, enabling your&lt;br&gt;
customers to send events directly to an external service's API in whatever format the service&lt;br&gt;
requires.&lt;/p&gt;

&lt;p&gt;This can vastly speed up your customers' workflow. No longer do they have to hack together a&lt;br&gt;
functional web server just to receive a webhook and forward it after making minute alterations.&lt;br&gt;
Instead, they can just write one function and forget about the rest.&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>technical</category>
      <category>features</category>
    </item>
    <item>
      <title>Zero Downtime Secret Rotation for Webhooks</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/zero-downtime-secret-rotation-for-webhooks-5elk</link>
      <guid>https://dev.to/svixhq/zero-downtime-secret-rotation-for-webhooks-5elk</guid>
      <description>&lt;p&gt;Before we talk about zero downtime secret rotations for webhooks, we need to explain what are webhook secrets and what they are used for.&lt;/p&gt;

&lt;p&gt;Because of the way webhooks work, attackers can impersonate services by simply sending a fake webhook to an endpoint. Think about it: it's just an HTTP POST from an unknown source. In order to prevent it, Svix, and many popular webhook providers sign every webhook and its metadata with a unique secret key. This is called a webhook secret.&lt;/p&gt;

&lt;p&gt;You can read more on how Svix signs webhooks in the &lt;a href="https://docs.svix.com/receiving/verifying-payloads/how-manual" rel="noopener noreferrer"&gt;verifying webhooks&lt;/a&gt; section of the docs, and we also previously wrote a post about &lt;a href="https://www.svix.com/blog/securely-verifying-signature-hashes/" rel="noopener noreferrer"&gt;securely verifying signature hashes&lt;/a&gt; in case you find this interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Secret rotation
&lt;/h2&gt;

&lt;p&gt;The secret, as the name implies, needs to be secret in order to work effectively. If the secret is ever compromised, or suspected to be compromised, it needs to be rotated (replaced by a new one) in order to ensure the security of the signature scheme.&lt;/p&gt;

&lt;p&gt;Rotating the webhook secret is a simple operation, usually it can be done by just a click of a button. The problem is that the moment you rotate the key, webhooks will be signed with the new key, which means that your endpoints verifying the signature using the old key will fail to verify the webhooks. This is because the key used to sign and the key used to verify are now different.&lt;/p&gt;

&lt;p&gt;This is easy to solve, you just need to update the verifying endpoint to use the new key and everything will work again. The problem is that webhooks will fail from when you rotate, until the secret is updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zero downtime secret rotation
&lt;/h2&gt;

&lt;p&gt;The solution to this problem is simple: sign the webhooks with both the old key, and the new key for a set period of time to give your webhook consumers enough time to update their endpoints to use the new key.&lt;/p&gt;

&lt;p&gt;You may ask yourself: isn't this a security issue? The key is compromised, an evil attacker may have access to it. While that's true, and why you need to rotate the secret in the first place, signing the payload with the compromised secret is not actually a security issue. This is because it doesn't give an attacker any advantage. The compromise of a key can let an attacker create fake payloads and sign them to trick the verifiers, but having a legitimate payload signed doesn't cause an issue.&lt;/p&gt;

&lt;p&gt;OK, so how do we do it? As we said above the solution is just to support signing the same message with multiple keys. This can be done in many different ways, but the easiest way (and &lt;a href="https://docs.svix.com/receiving/verifying-payloads/how-manual" rel="noopener noreferrer"&gt;what Svix does&lt;/a&gt;), is to pass multiple space-delimited signatures in the &lt;code&gt;webhook-signature&lt;/code&gt; header like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE= v1,bm9ldHUjKzFob2VudXRob2VodWUzMjRvdWVvdW9ldQo=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, your consumer can just try and match each signature and as long as one of them matches, consider it a success. Here's an example JavaScript snippet that does it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signatures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signatureHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&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;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;signatures&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="nf"&gt;timingSafeEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedSignature&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="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;// Verification succeeded&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;// Verification failed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we saw, making sure your webhook secret can be safely rotated with zero downtime is fairly easy, you just need to make sure to design your signature scheme to support it in the first place. Alternatively, you can just use &lt;a href="https://www.svix.com" rel="noopener noreferrer"&gt;the Svix service&lt;/a&gt; which takes care of everything for you, or use any of &lt;a href="https://github.com/svix/svix-webhooks" rel="noopener noreferrer"&gt;the Svix libraries&lt;/a&gt; to implement secure signing and verification.&lt;/p&gt;




&lt;p&gt;For more content like this, make sure to follow us on &lt;a href="https://twitter.com/SvixHQ" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://github.com/svix" rel="noopener noreferrer"&gt;Github&lt;/a&gt; or &lt;a href="https://www.svix.com/blog/rss/" rel="noopener noreferrer"&gt;RSS&lt;/a&gt; for the latest updates for the &lt;a href="https://www.svix.com" rel="noopener noreferrer"&gt;Svix webhook service&lt;/a&gt;, or join the discussion on &lt;a href="https://www.svix.com/slack/" rel="noopener noreferrer"&gt;our community Slack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>discuss</category>
      <category>programming</category>
    </item>
    <item>
      <title>Open Source Webhooks as a Service Written in Rust</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Thu, 29 Dec 2022 18:05:56 +0000</pubDate>
      <link>https://dev.to/svixhq/open-source-webhooks-as-a-service-written-in-rust-1ij9</link>
      <guid>https://dev.to/svixhq/open-source-webhooks-as-a-service-written-in-rust-1ij9</guid>
      <description>&lt;p&gt;Sending one webhook is trivial. It's just a POST request to an API endpoint. But sending webhooks at scale is so much harder. At Svix we make it easy for anyone to offer a world class webhook experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/svix/svix-webhooks" rel="noopener noreferrer"&gt;https://github.com/svix/svix-webhooks&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
    </item>
    <item>
      <title>Using ngrok's new built-in Svix support</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Mon, 07 Nov 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/using-ngroks-new-built-in-svix-support-1521</link>
      <guid>https://dev.to/svixhq/using-ngroks-new-built-in-svix-support-1521</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--usXpaoby--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jaixdv89cbl614a98nlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--usXpaoby--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jaixdv89cbl614a98nlt.png" alt="" width="880" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ngrok.com"&gt;ngrok&lt;/a&gt; is a staple tool for many developers that creates tunnels between networks. It is often used to expose a port on localhost to the public Internet, but with ngrok Cloud Edge it can be also be used to secure traffic from the Internet to production cloud environments.&lt;/p&gt;

&lt;p&gt;In this tutorial, we will learn how to verify Svix webhook requests using ngrok both for local development and on ngrok Cloud Edge.&lt;/p&gt;

&lt;p&gt;This tutorial assumes you are already familiar with the &lt;a href="https://www.svix.com"&gt;Svix webhooks service&lt;/a&gt; and &lt;a href="https://ngrok.com"&gt;ngrok&lt;/a&gt;. If this is your first time using Svix,&lt;br&gt;
we recommend you first check out our &lt;a href="https://docs.svix.com/overview"&gt;quickstart documentaion&lt;/a&gt;. We also recommend checking out ngrok's documentation on &lt;a href="https://ngrok.com/docs/cloud-edge/modules/webhook"&gt;Webhook Verification&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Verify Svix webhooks locally with ngrok CLI
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Install ngrok
&lt;/h3&gt;

&lt;p&gt;If you haven't already, &lt;a href="https://ngrok.com/download"&gt;install the ngrok CLI&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create a tunnel
&lt;/h3&gt;

&lt;p&gt;First, create an endpoint in the Consumer App Portal, and copy the endpoint's Signing Signature. Then, run the following ngrok command on your computer's terminal, replacing SIGNATURE with the Signing Signature you just copied:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 3000 &lt;span class="nt"&gt;--verify-webhook&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;svix &lt;span class="nt"&gt;--verify-webhook-secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;SIGNATURE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run the command, ngrok should generate URL that looks like &lt;code&gt;https://d7f4c8296c55.ngrok.io&lt;/code&gt;. Copy that URL, and set it as the URL for the Svix endpoint you previously created in the Consumer App Portal.&lt;/p&gt;

&lt;p&gt;Assuming you are running a service on port 3000, all Svix webhooks to your endpoint will now be forwarded to that local service by ngrok. And because you configured the &lt;code&gt;--verify-webhook&lt;/code&gt; and &lt;code&gt;--verify-webhook-secret&lt;/code&gt; options, ngrok will only forward verified Svix webhooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify Svix Webhooks on ngrok Cloud Edge
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create a Svix endpoint
&lt;/h3&gt;

&lt;p&gt;Create an endpoint in the consumer application portal. You'll need the newly-created endpoint's Signing Signature later on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign up for ngrok
&lt;/h3&gt;

&lt;p&gt;Create an account on &lt;a href="https://ngrok.com"&gt;ngrok.com&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an ngrok Edge
&lt;/h3&gt;

&lt;p&gt;Login to the &lt;a href="https://dashboard.ngrok.com"&gt;ngrok dashboard&lt;/a&gt;. Using the menu on the left, expand "Cloud Edge" and choose "Edges." Create an edge by clicking the "New edge" button and choose "HTTPS Edge".&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure ngrok Edge
&lt;/h3&gt;

&lt;p&gt;On the Edge configuration page, find and click the "Webhooks Verification" menu item, and click "Begin Setup." Choose Svix as your webhook provider:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y2EBSDtT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xk2q9hwgzdrrqum0rlpb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y2EBSDtT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xk2q9hwgzdrrqum0rlpb.png" alt="Selecting Svix as the Webhook Provider" width="880" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And paste your endpoint's Signing Signature from Svix as the webhook signing key:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lzBEAS3t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5pjr4xaj1qjo3y6j45nq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lzBEAS3t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5pjr4xaj1qjo3y6j45nq.png" alt="Entering your Svix endpoint's Signing Key" width="880" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why Your Product Needs Webhooks</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Tue, 01 Nov 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/why-your-product-needs-webhooks-275j</link>
      <guid>https://dev.to/svixhq/why-your-product-needs-webhooks-275j</guid>
      <description>&lt;h1&gt;
  
  
  Why Your Product Needs Webhooks
&lt;/h1&gt;

&lt;p&gt;Every product gets to the point where they’re missing a key element to continue their growth trajectory. Integrations.&lt;/p&gt;

&lt;p&gt;Integrations with other popular services helps you acquire customers as well as retain them as your product becomes embedded in their workflows. However, creating these integrations consumes valuable engineering resources. It’s especially painful when you need to keep diverting engineering time to the next integration (and the next, and the next, and…).&lt;/p&gt;

&lt;p&gt;Enter webhooks.&lt;/p&gt;

&lt;p&gt;Webhooks, also known as http callbacks, are a way for APIs to notify applications that a specific event has occurred without receiving a request. Webhooks allow users to specify an endpoint URL where they can receive real time event updates, eliminating the need to send constant requests to their API providers looking for updates.&lt;/p&gt;

&lt;p&gt;For example, &lt;a href="https://zapier.com/developer/documentation/v2/polling/"&gt;Zapier, a popular low/no code integration platform, hates API polling&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Polling is the process of repeatedly hitting the same endpoint looking for new data. We don't like doing this (it's wasteful), vendors don't like us doing it (again, it's wasteful) and users dislike it (they have to wait a maximum interval to trigger on new data). However, it is the one method that is ubiquitous, so we support it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many products have APIs with a well documented webhook service that make receiving real time updates a breeze for their users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why webhooks
&lt;/h2&gt;

&lt;p&gt;Here are 4 reasons why your procuct should offer a webhooks feature and stop relying on API polling:&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy Integration
&lt;/h3&gt;

&lt;p&gt;Webhooks enable both custom integrations as well as listing on no-code integration platforms.&lt;/p&gt;

&lt;p&gt;Zapier, IFTTT, and Make (formerly Integromat) are very common ways for products to tap into a network of app integrations. All can work using API polling and webhooks (and we already know how we feel about polling).&lt;/p&gt;

&lt;p&gt;Integrating with these workflow automation platforms has many benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrate with 3000+ apps currently available in the marketplace&lt;/li&gt;
&lt;li&gt;Continued integration with any new apps added&lt;/li&gt;
&lt;li&gt;Increased visibility to 3.5M+ users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In addition to no code platforms, custom integrations become more straightforward and repeatable. For example, Hubspot has a public API with full documentation on how to set up a webhook listener to receive event notifications from them so that anyone can integrate with their platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resource Efficiency
&lt;/h3&gt;

&lt;p&gt;Generally, a webhook system will be much less resource intensive than a polling system. This is true for both the API provider and consumer.&lt;/p&gt;

&lt;p&gt;For example, if 1,000 users poll the API every 5 seconds, your API will have to be able to handle up to 1,000 requests per second depending on the timing of the requests.&lt;/p&gt;

&lt;p&gt;Zapier estimates that only 1.5% of polling requests find an update. That implies that with a webhook solution, you would only need to send up to 15 responses per second while your users don’t have to send any requests at all. That’s &amp;lt; 1% of the resources needed to sustain the polling solution (1000 requests + 1000 responses)&lt;/p&gt;

&lt;h3&gt;
  
  
  Real Time Updates
&lt;/h3&gt;

&lt;p&gt;Our example scenario only accounts for 1 request every 5 seconds. What if customers want updates in real time? They would need to send requests at least every second if not more. This increases the load on your system and increases operational complexity.&lt;/p&gt;

&lt;p&gt;If the API provider has rate limits, you could end up blocked from sending requests due to exceeding the specified rate limit. If the customer has implemented polling at frequencies that do not exceed the rate limit, they end up polling so infrequently that their updates are stale. This can decrease customer satisfaction and lead to churn as your API could no longer be a valid solution.&lt;/p&gt;

&lt;p&gt;Webhooks get around this issue by sending updates right as events happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer Experience
&lt;/h3&gt;

&lt;p&gt;Webhooks also offer a better customer experience. Forcing your users to integrate by constantly polling your API for updates means they need to do the heavy lifting of maintaining and comparing state to determine if there have been any changes and what actually changed.&lt;/p&gt;

&lt;p&gt;Think about the common situation of a family on a road trip. Polling is equivalent to the kids constantly asking “Are we there yet?” and the parents having to say “no” over and over again. Wouldn’t you prefer the webhook scenario where the kids are quietly reading a book or watching a movie while the parents get to drive in peace?&lt;/p&gt;

&lt;h3&gt;
  
  
  Developers Expect Webhooks
&lt;/h3&gt;

&lt;p&gt;Major API providers like Stripe and Plaid have very well documented webhook features and they are becoming commonplace with the most popular APIs. That means developers are used to working with the webhook pattern as they are fairly simple for customers to ingest. The more others start offering webhooks, the more developers will expect webhooks to integrate with.&lt;/p&gt;

&lt;p&gt;Webhooks also improve the developer experience. In fact, &lt;a href="https://zapier.wufoo.com/reports/polling-or-webhooks/"&gt;a poll conducted by Wufoo&lt;/a&gt; (now part of SurveyMonkey) found that 82% of developers preferred working with webhooks over API polling.&lt;/p&gt;

&lt;h2&gt;
  
  
  So Why Aren’t More People Offering Webhooks?
&lt;/h2&gt;

&lt;p&gt;There are some cases where polling would be a preferable solution to webhooks. When updates are more frequent than the polling interval, it’s more efficient to get the updates in batches via polling. If you also don’t need the updates in real time, polling is a great solution.&lt;/p&gt;

&lt;p&gt;The main thing blocking most people from offering webhooks is that they’re really hard to implement at scale. Unreliable user endpoints means you need automatic retries to ensure deliverability. Monitoring deliverability and notifying customers when their endpoints are broken is critical. There are many potential security vulnerabilites like server-side request forgery (SSRF) and replay attacks that you need to defend against. You need a way for your users to authenticate webhook events to ensure they are coming from you. You need a UI for your users to test, debug, and monitor their implementations.&lt;/p&gt;

&lt;p&gt;You can see all the potential components and services needed to build a secure, reliable and scalable webhook system in this architecture diagram we created for Svix:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--duWD9VPt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/./webhook-architecture-diagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--duWD9VPt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/v1/./webhook-architecture-diagram.png" alt="Webhook architecture diagram" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even a well funded Silicon Valley startup with a strong engineering culture like Lob &lt;a href="https://www.lob.com/blog/lob-webhooks-the-remix"&gt;recently revamped their system&lt;/a&gt; using our webhook service. They were able to successfully simplify the design to make their engineers’ lives easier, increase scalability, add features to their dashboard, and make their cost structure more predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Webhooks are becoming a must have feature for products that want to integrate with other systems. They’re very efficient for providing real time updates, provide an excellent developer experience and enable a lot of possible integrations through no/low code platforms like Zapier and Make.&lt;/p&gt;

&lt;p&gt;Although they can be difficult to implement at scale, there are open source and hosted solutions that make it simple to offer a great webhook experience for your users.&lt;/p&gt;

</description>
      <category>webhooks</category>
    </item>
    <item>
      <title>Securely Verifying Signature Hashes</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Mon, 24 Oct 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/securely-verifying-signature-hashes-4f8k</link>
      <guid>https://dev.to/svixhq/securely-verifying-signature-hashes-4f8k</guid>
      <description>&lt;p&gt;A friend of mine shared &lt;a href="https://cve.report/CVE-2022-43412"&gt;this link&lt;/a&gt; with me the other day, about CVE-2022-43412. The issue disclosed in that CVE is that Jenkins Generic Webhook Trigger Plugin was using a non-constant time comparison function when checking whether the provided and expected webhook token are equal. In addition to the aforementioned CVE, there's also CVE-2022-43411 which is the same issue, but with the Jenkins Gitlab plugin.&lt;/p&gt;

&lt;p&gt;In this blog post we will explain how the attack works, how to avoid similar problems, and what we do at Svix to protect our customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the issue?
&lt;/h2&gt;

&lt;p&gt;The issue described in this CVE uses a timing-based side-channel to execute an oracle attack against the hash verification. Or in more plain words: this attack uses the fact that the comparison may take different times based on the content, to construct a valid signature even without knowing the key.&lt;/p&gt;

&lt;p&gt;I know this may sound a bit like magic, because it kinda is. Though let's demystify it by using a more simple example, and go from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's a timing based oracle attack?
&lt;/h2&gt;

&lt;p&gt;Let's assume it's not a webhook, but rather a website, and the website is password protect. The developer who built this website is new to security, so they implemented the security as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Secret123&lt;/span&gt;&lt;span class="dl"&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;user_provided_password&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;password&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Wrong password!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's take a deeper look at the comparison in the condition. Because we used a high level function we can just use &lt;code&gt;!=&lt;/code&gt; between strings, but what goes on behind the scenes is that it actually compares them character by character (well, it's actually more complex than that, but let's assume that for simplicity).&lt;/p&gt;

&lt;p&gt;So a naive implementation may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This function assumes the strings are the same length for simplicity&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;is_equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&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;let&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;0&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Can stop iteration, strings are not equal!&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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;return&lt;/span&gt; &lt;span class="kc"&gt;true&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 the sake of example, let's assume our computer is very slow, and doing &lt;code&gt;a[i] != b[i]&lt;/code&gt; takes a second each time.&lt;/p&gt;

&lt;p&gt;Looking at the above then, we can see that if the user passes &lt;code&gt;Bad123123&lt;/code&gt; as the password, it'll try to compare the first characters (&lt;code&gt;B&lt;/code&gt; vs &lt;code&gt;S&lt;/code&gt;), and because they are unequal, the comparison will stop early so the whole function will take 1 second. Though if we pass &lt;code&gt;Secret123&lt;/code&gt; the whole function will take 9 seconds, as it compared all of the characters.&lt;/p&gt;

&lt;p&gt;This means that we found an observable change of behavior based on the password. Now let's use that to our advantage.&lt;/p&gt;

&lt;p&gt;We know that this function is slower the more characters are correct. So we can just start feeding it with characters one after the other and measure the time it took. We try &lt;code&gt;A&lt;/code&gt; and get 1 second, &lt;code&gt;B&lt;/code&gt; and get one second, all the way to &lt;code&gt;S&lt;/code&gt;, where it all of a sudden takes two seconds. This lets us find the first character.&lt;/p&gt;

&lt;p&gt;Now we can do the same for the second character. We pass &lt;code&gt;Sa&lt;/code&gt; which takes 2 seconds, and then &lt;code&gt;Sb&lt;/code&gt;, which takes two seconds, all the way to &lt;code&gt;Se&lt;/code&gt; which then takes 3 seconds, so our password starts with &lt;code&gt;Se&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;We can repeat this process until we found the full password.&lt;/p&gt;

&lt;p&gt;What's the correct way to solve it then? Well to make comparison constant time!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This function assumes the strings are the same length for simplicity&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;is_equal_constant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&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;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&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;let&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;0&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="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="c1"&gt;// Putting the &amp;amp;&amp;amp; ret at the end, to avoid short-circuiting&lt;/span&gt;
        &lt;span class="nx"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;ret&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="kc"&gt;true&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;With this function the comparison always takes the same time, which protects us from timing attacks!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; do not use the function above in production as it may not work as expected. Optimizing compilers may change the behavior, so it's important to use purpose-built comparison functions that are provided by your system libraries which already protect against that.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it apply to webhooks?
&lt;/h2&gt;

&lt;p&gt;Webhooks don't really have a password, but they have signatures, which in a way are dynamic passwords. With webhooks you pass a specific payload, e.g:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bar"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and sign it with a secret key in a way that only someone with the key could have signed it. The signature (e.g. &lt;code&gt;Vh082qUqkhY7WiBWktRKRbP6/c+EYoqtHi/dMULaTKc=&lt;/code&gt;) is then passed with the payload, and the receiver can verify the sender has the secret key, and thus that the webhook can be trusted.&lt;/p&gt;

&lt;p&gt;An attacker may want to send a malicious webhook, but without the secret key, the signature will not match, and the webhook will be rejected.&lt;/p&gt;

&lt;p&gt;The aforementioned timing attack can't help us with recovering the secret key used for the signature, but it can help us find a valid signature!&lt;/p&gt;

&lt;p&gt;So similarly to how we managed to find the password, we can find a valid signature for the malicious payload, and then just use this valid signature as a proof we know the key and get the webhook validated. Even though we never actually learned the key!&lt;/p&gt;

&lt;h1&gt;
  
  
  How does Svix solve it?
&lt;/h1&gt;

&lt;p&gt;Security is hard. The reason for that is that security issues are not always obvious. If you have a bug in your code, you will usually notice it while testing it. Though with security, you don't always know what to test for.&lt;/p&gt;

&lt;p&gt;This issue, unfortunately can't be mitigated on the sender side (Svix), it has to be mitigated on the client side. So we can't blanket solve it on our end.&lt;/p&gt;

&lt;p&gt;We have however created a set of &lt;a href="https://github.com/svix/svix-webhooks"&gt;open source webhook signature libraries&lt;/a&gt;, that make it easy both to sign and verify webhook signatures correctly. Svix customers can offer them to their customers to ensure they have a great developer experience and stay secure. Additionally, non Svix customers can also reuse them, both for signing, and verification to ensure that they offer their customers an easy and secure experience.&lt;/p&gt;




&lt;p&gt;For more content like this, make sure to follow us on &lt;a href="https://twitter.com/SvixHQ"&gt;Twitter&lt;/a&gt;, &lt;a href="https://github.com/svix"&gt;Github&lt;/a&gt; or &lt;a href="https://www.svix.com/blog/rss/"&gt;RSS&lt;/a&gt; for the latest updates for the &lt;a href="https://www.svix.com"&gt;Svix webhook service&lt;/a&gt;, or join the discussion on &lt;a href="https://www.svix.com/slack/"&gt;our community Slack&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>security</category>
      <category>technical</category>
      <category>webhooks</category>
    </item>
    <item>
      <title>Comparing Popular Webhook Providers</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Tue, 18 Oct 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/comparing-popular-webhook-providers-op7</link>
      <guid>https://dev.to/svixhq/comparing-popular-webhook-providers-op7</guid>
      <description>&lt;p&gt;After reviewing a few of the most popular webhook providers, we found some interesting trends about which features are most and least common.&lt;/p&gt;

&lt;h1&gt;
  
  
  Comparing the most popular webhook providers
&lt;/h1&gt;

&lt;p&gt;We’ve published a series of articles reviewing various webhook providers and have noticed some interesting commonalities and differences between them. In each of the articles, we take a look through the webhook provider’s documentation to see if and how they implement 7 key features we believe help make a great webhook experience: automatic retries, exponential backoff, signature verification, manual retries, event types, multiple endpoint support, and log visibility. We tried to select the most popular webhooks by looking at google search volumes. Here is the summary table of our reviews:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Automatic Retries&lt;/th&gt;
&lt;th&gt;Exponential Backoff&lt;/th&gt;
&lt;th&gt;Signature Verification&lt;/th&gt;
&lt;th&gt;Manual Retries&lt;/th&gt;
&lt;th&gt;Event Types&lt;/th&gt;
&lt;th&gt;Multiple Endpoints&lt;/th&gt;
&lt;th&gt;Log Visibility&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Shopify&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PagerDuty&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BitBucket&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DataDog&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CircleCI&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⬜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Svix&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Only Stripe, GitLab, and Svix check all 7 boxes (since Svix is a webhook service, all Svix customers check all the boxes as well 😉). Shopify came close with 6 boxes but the rest have at best 3. A round of applause for Stripe, GitLab, and Shopify please!&lt;/p&gt;

&lt;p&gt;All 7 offer multiple endpoint support so it seems we’re past the days of having to receive all your webhooks on one endpoint. Event Types were offered by 6 of the companies and DataDog had a unique design for filtering which events you receive via custom variables. This combination of features can make webhook consumers’ lives much easier by allowing them to have separate endpoints for each Event Type. It makes debugging failing endpoints much easier and adds resilience to their system so one failing endpoint doesn’t block all webhook messages from being received.&lt;/p&gt;

&lt;p&gt;Signature verification was offered by most companies which was a relief as its a critical component to offering a secure webhook service. We suspect this will become table stakes for any webhook provider very soon.&lt;/p&gt;

&lt;p&gt;Manual retries were only offered by Stripe and GitLab and was the only feature not offered by Shopify. This suggests it's the most “nice to have” feature on our list. It does make developers’ lives a lot easier when they’re debugging a failing endpoint and can trigger a retry manually instead of having to wait for the retry schedule.&lt;/p&gt;

&lt;p&gt;We were most surprised by the retry policies (combination of having automatic retries sent with an exponential backoff schedule). A few of the companies that we didn’t give checkmarks to actually do offer retries but the number of retries are very low. It felt like they were just trying to check a box instead of focusing on how a retry policy can improve the developer experience. We believe having a retry policy that sends retries over at least a 24h span is necessary to give developers time to fix their failing endpoints before they get disabled.&lt;/p&gt;

&lt;p&gt;Overall, we were very impressed with some webhook services and a bit disappointed by others. This just goes to show how difficult webhooks can be to implement at the scale some of these products have acheived.&lt;/p&gt;

</description>
      <category>webhooks</category>
    </item>
    <item>
      <title>Introducing React Hooks in the Svix React library</title>
      <dc:creator>Svix</dc:creator>
      <pubDate>Tue, 27 Sep 2022 00:00:00 +0000</pubDate>
      <link>https://dev.to/svixhq/introducing-react-hooks-in-the-svix-react-library-2mae</link>
      <guid>https://dev.to/svixhq/introducing-react-hooks-in-the-svix-react-library-2mae</guid>
      <description>&lt;p&gt;The Svix React library now ships with custom React Hooks to make building a custom Portal easier than ever.&lt;/p&gt;

&lt;p&gt;At Svix, we value developer experience. That's why we release our client library across a wide range of languages, including JavaScript/TypeScript. And today, we're bolstering our tooling in one of the most popular JavaScript frontend frameworks: React.&lt;/p&gt;

&lt;p&gt;Many Svix users are interested in building custom, branded Consumer App Portals for their customers, usually by theming and embedding our hosted Consumer App Portal as an iFrame. This works great for most use-cases, but sometimes our users need more granular control over structure and appearance, and decide to build their own Portal as a part of their existing application. We're excited to offer a new, powerful tool to help ease development in this area: React Hooks, available in the latest version of our &lt;code&gt;svix-react&lt;/code&gt; library.&lt;/p&gt;

&lt;p&gt;The hooks are an abstraction over our standard JavaScript client library, and cover all the core functionality required to replicate the Consumer App Portal. They have been designed to manage concerns like pagination and reloading, so you can just focus on building an awesome UI.&lt;/p&gt;

&lt;p&gt;Getting started is easy. Install or update the &lt;code&gt;svix-react&lt;/code&gt; library (and &lt;code&gt;svix&lt;/code&gt;, which is a peer dependency)...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i svix-react@latest svix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and wrap your app in the SvixProvider component:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SvixProvider&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="s1"&gt;svix-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;App&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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SvixProvider&lt;/span&gt; &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;appId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/** your app's components **/&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;SvixProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;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;Great. Now, you can use our hooks for things like listing event types, rotating an endpoint key, and examining message attempts. Here's an example of using the hooks to list endpoints:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEndpoints&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="s1"&gt;svix-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;ListEndpoints&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;endpoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useEndpoints&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;An error has occurred&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;endpoint&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasPrevPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prevPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Previous Page
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasNextPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextPage&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Next Page
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;As you can see, the &lt;code&gt;useEndpoints&lt;/code&gt; hook handles fetching the data and maintaining various state, including pagination, errors, and loading.&lt;/p&gt;

&lt;p&gt;Under the hood, our hooks are strongly typed using TypeScript. The &lt;code&gt;useEndpoints&lt;/code&gt; hook returns a generic interface that's reused by other similar hooks, so you can expect the same properties and functionality across all hooks returning list data.&lt;/p&gt;

&lt;p&gt;In the same way, hooks fetching a single item also share a common interface. Here's an example of the &lt;code&gt;useEndpoint&lt;/code&gt; hook:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEndpoint&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="s1"&gt;svix-react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Endpoint&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;endpointId&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;endpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useEndpoint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;endpointId&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;An error has occurred&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;endpoint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;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;All of the new hooks, along with their input options and response objects, are documented in the &lt;code&gt;svix-react&lt;/code&gt; library's &lt;a href="https://www.npmjs.com/package/svix-react"&gt;README&lt;/a&gt;. If your app requires functionality not provided by one of the hooks, there's an escape hatch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;svix&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useSvix&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;useSvix&lt;/code&gt; hook gives you access to the underlying Svix instance being used to make API calls, as if you were using the &lt;code&gt;svix&lt;/code&gt; library directly. The &lt;code&gt;SvixProvider&lt;/code&gt; component has already configured it with your token and appId, so it's ready for use immediately.&lt;/p&gt;

&lt;p&gt;This update to our React library will make custom Consumer App Portal development easier and quicker than ever. We can't wait to see what you build with it!&lt;/p&gt;

</description>
      <category>news</category>
      <category>product</category>
    </item>
  </channel>
</rss>
