<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Loren 🤓</title>
    <description>The latest articles on Forem by Loren 🤓 (@lorendsr).</description>
    <link>https://forem.com/lorendsr</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%2F165278%2F839df171-cf9e-4d37-bb29-d93484781345.jpg</url>
      <title>Forem: Loren 🤓</title>
      <link>https://forem.com/lorendsr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/lorendsr"/>
    <language>en</language>
    <item>
      <title>Time-Travel Debugging Production Code</title>
      <dc:creator>Loren 🤓</dc:creator>
      <pubDate>Tue, 08 Aug 2023 07:03:31 +0000</pubDate>
      <link>https://forem.com/temporalio/time-travel-debugging-production-code-4m6o</link>
      <guid>https://forem.com/temporalio/time-travel-debugging-production-code-4m6o</guid>
      <description>&lt;p&gt;In this post, I’ll give an overview of time travel debugging (what it is, its history, how it’s implemented) and show how it relates to debugging your production code.&lt;/p&gt;

&lt;p&gt;Normally, when we use debuggers, we set a breakpoint on a line of code, we run our code, execution pauses on our breakpoint, we look at values of variables and maybe the call stack, and then we manually step forward through our code's execution. In &lt;em&gt;time-travel debugging&lt;/em&gt;, also known as &lt;em&gt;reverse debugging&lt;/em&gt;, we can step backward as well as forward. This is powerful because debugging is an exercise in figuring out what happened: traditional debuggers are good at telling you what your program is doing right now, whereas time-travel debuggers let you see what happened. You can wind back to any line of code that executed and see the full program state at any point in your program’s history.&lt;/p&gt;

&lt;h2&gt;
  
  
  History and current state
&lt;/h2&gt;

&lt;p&gt;It all started with Smalltalk-76, developed in 1976 at &lt;a href="https://en.wikipedia.org/wiki/PARC_(company)"&gt;Xerox PARC&lt;/a&gt;. (&lt;a href="https://en.wikipedia.org/wiki/Graphical_user_interface"&gt;Everything&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/Computer_mouse"&gt;started&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/Ethernet"&gt;at&lt;/a&gt; &lt;a href="https://en.wikipedia.org/wiki/WYSIWYG"&gt;PARC&lt;/a&gt; 😄.) It had the ability to retrospectively inspect checkpointed places in execution. Around 1980, MIT added a "retrograde motion" command to its &lt;a href="https://en.wikipedia.org/wiki/Dynamic_debugging_technique"&gt;DDT debugger&lt;/a&gt;, which gave a limited ability to move backward through execution. In a 1995 paper, MIT researchers released ZStep 95, the first true reverse debugger, which recorded all operations as they were performed and supported stepping backward, reverting the system to the previous state. However, it was a research tool and not widely adopted outside academia. &lt;/p&gt;

&lt;p&gt;ODB, the &lt;a href="https://omniscientdebugger.github.io/ODBUserManual.html"&gt;Omniscient Debugger&lt;/a&gt;, was a Java reverse debugger that was introduced in 2003, marking the first instance of time-travel debugging in a widely used programming language. &lt;a href="https://en.wikipedia.org/wiki/GNU_Debugger"&gt;GDB&lt;/a&gt; (perhaps the most well-known command-line debugger, used mostly with C/C++) added it in 2009.&lt;/p&gt;

&lt;p&gt;Now, time-travel debugging is available for &lt;a href="https://github.com/rr-debugger/rr/wiki/Related-work"&gt;many&lt;/a&gt; languages, platforms, and IDEs, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.replay.io/"&gt;Replay&lt;/a&gt; for JavaScript in Chrome, Firefox, and Node, and &lt;a href="https://wallabyjs.com/docs/intro/time-travel-debugger.html"&gt;Wallaby&lt;/a&gt; for tests in Node&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/time-travel-debugging-overview"&gt;WinDbg&lt;/a&gt; for Windows applications&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rr-project.org/"&gt;rr&lt;/a&gt; for C, C++, Rust, Go, and others on Linux&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://undo.io/"&gt;Undo&lt;/a&gt; for C, C++, Java, Kotlin, Rust, and Go on Linux&lt;/li&gt;
&lt;li&gt;Various extensions (often rr- or Undo-based) for Visual Studio, VS Code, JetBrains IDEs, Emacs, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation techniques
&lt;/h2&gt;

&lt;p&gt;There are three main approaches to implementing time-travel debugging:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Record &amp;amp; Replay&lt;/strong&gt;: Record all non-deterministic inputs to a program during its execution. Then, during the debug phase, the program can be deterministically replayed using the recorded inputs in order to reconstruct any prior state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snapshotting&lt;/strong&gt;: Periodically take snapshots of a program's entire state. During debugging, the program can be rolled back to these saved states. This method can be memory-intensive because it involves storing the entire state of the program at multiple points in time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instrumentation&lt;/strong&gt;: Add extra code to the program that logs changes in its state. This extra code allows the debugger to step the program backwards by reverting changes. However, this approach can significantly slow down the program's execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;rr uses the first (the rr name stands for Record and Replay), as does &lt;a href="https://docs.replay.io/learn-more/contribute/replay-for-new-contributors#5130506fb24843ab86fe79d11f02261b"&gt;Replay&lt;/a&gt;. WinDbg uses the first two, and Undo uses all three (see &lt;a href="https://undo.io/resources/liverecorder-vs-rr/"&gt;how it differs from rr&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Time-traveling in production
&lt;/h2&gt;

&lt;p&gt;Traditionally, running a debugger in prod doesn't make much sense. Sure, we could SSH into a prod machine and start the process handling requests with a debugger and a breakpoint, but once we hit the breakpoint, we're delaying responses to all current requests and unable to respond to new requests. Also, debugging non-trivial issues is an iterative process: we get a clue, we keep looking and find more clues; discovery of each clue is typically rerunning the program and reproducing the failure. So, instead of debugging in production, what we do is replicate on our dev machine whatever issue we're investigating and use a debugger locally (or, more often, add log statements 😄), and re-run as many times as required to figure it out. Replicating takes time (and in some cases a &lt;em&gt;lot&lt;/em&gt; of time, and in some cases infinite time), so it would be really useful if we didn't have to.&lt;/p&gt;

&lt;p&gt;While running traditional debuggers doesn't make sense, time-travel debuggers can record a process execution on one machine and replay it on another machine. So we can record (or snapshot or instrument) production and replay it on our dev machine for debugging (depending on the tool, our machine may need to have the same CPU instruction set as prod). However, the recording step generally doesn't make sense to use in prod given the high amount of overhead—if we set up recording and then have to use ten times as many servers to handle the same load, whoever &lt;a href="https://www.linkedin.com/in/kevin-laughlin-4133166/"&gt;pays our AWS bill&lt;/a&gt; will not be happy 😁.&lt;/p&gt;

&lt;p&gt;But there are a couple scenarios in which it does make sense:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Undo only slows down execution &lt;a href="https://undo.io/solutions/products/live-recorder/#section-133"&gt;2–5x&lt;/a&gt;, so while we don't want to leave it on just in case, we can &lt;a href="https://twitter.com/gregthelaw/status/1654558923242762243"&gt;turn it on temporarily&lt;/a&gt; on a subset of prod processes for hard-to-repro bugs until we have captured the bug happening, and then we turn it off.&lt;/li&gt;
&lt;li&gt;When we're already recording the execution of a program in the normal course of operation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rest of this post is about #2, which is a way of running programs called &lt;em&gt;durable execution&lt;/em&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Durable execution
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What's that?
&lt;/h3&gt;

&lt;p&gt;First, a brief backstory. After Amazon (one of the first large adopters of microservices) decided that using message queues to communicate between services was not the way to go (hear the story first-hand &lt;a href="https://www.youtube.com/watch?v=wIpz4ioK0gI"&gt;here&lt;/a&gt;), they started using orchestration. And once they realized defining orchestration logic in YAML/JSON wasn't a good developer experience, they created &lt;a href="https://docs.aws.amazon.com/amazonswf/latest/developerguide/swf-welcome.html"&gt;AWS Simple Workfow Service&lt;/a&gt; to define logic in code. This technique of backing code by an orchestration engine is called durable execution, and it spread to &lt;a href="https://learn.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-overview?tabs=csharp-inproc"&gt;Azure Durable Functions&lt;/a&gt;, &lt;a href="https://cadenceworkflow.io/"&gt;Cadence&lt;/a&gt; (used at Uber for &lt;a href="https://www.uber.com/blog/announcing-cadence/"&gt;&amp;gt; 1,000 services&lt;/a&gt;), and &lt;a href="https://temporal.io/"&gt;Temporal&lt;/a&gt; (used by Stripe, Netflix, Datadog, Snap, Coinbase, and many more).&lt;/p&gt;

&lt;p&gt;Durable execution runs code durably—recording each step in a database, so that when anything fails, it can be retried from the same step. The machine running the function can even lose power before it gets to line 10, and another process is guaranteed to pick up executing at line 10, with all variables and threads intact.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; It does this with a form of record &amp;amp; replay: all input from the outside is recorded, so when the second process picks up the partially-executed function, it can replay the code (in a side-effect–free manner) with the recorded input in order to get the code into the right state by line 10.&lt;/p&gt;

&lt;p&gt;Durable execution's flavor of record &amp;amp; replay doesn't use high-overhead methods like &lt;a href="https://undo.io/resources/liverecorder-vs-rr/"&gt;software JIT binary translation&lt;/a&gt;, snapshotting, or instrumentation. It also doesn't require special hardware. It does require one constraint: durable code must be deterministic (i.e., given the same input, it must take the same code path). So it can't do things that might have different results at different times, like use the network or disk. However, it can call other functions that are run normally (&lt;a href="https://twitter.com/DominikTornow/status/1582370919258783744"&gt;"volatile functions"&lt;/a&gt;, as we like to call them 😄), and while each step of those functions isn't persisted, the functions are automatically retried on transient failures (like a service being down).&lt;/p&gt;

&lt;p&gt;Only the steps that require interacting with the outside world (like calling a volatile function, or calling &lt;code&gt;sleep('30 days')&lt;/code&gt;, which stores a timer in the database) are persisted. Their results are also persisted, so that when you replay the durable function that died on line 10, if it previously called the volatile function on line 5 that returned "foo", during replay, "foo" will immediately be returned (instead of the volatile function getting called again). While yes, it adds latency to be saving things to the database, Temporal supports extremely high throughput (tested up to a million recorded steps per second). And in addition to function recoverability and automatic retries, it comes with &lt;a href="https://temporal.io/blog/building-reliable-distributed-systems-in-node"&gt;many more benefits&lt;/a&gt;, including extraordinary visibility into and debuggability of production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Debugging prod
&lt;/h3&gt;

&lt;p&gt;With durable execution, we can read through the steps that every single durable function took in production. We can also download the execution’s history, checkout the version of the code that's running in prod, and pass the file to a replayer (Temporal has runtimes for Go, Java, JavaScript, Python, .NET, and PHP) so we can see in a debugger exactly what the code did during that production function execution. Read &lt;a href="https://temporal.io/blog/temporal-for-vs-code"&gt;this post&lt;/a&gt; or watch &lt;a href="https://www.youtube.com/watch?v=3IjQde9HMNY"&gt;this video&lt;/a&gt; to see an example in VS Code.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Being able to debug any past production code is a huge step up from the other option (finding a bug, trying to repro locally, failing, turning on Undo recording in prod until it happens again, turning it off, &lt;em&gt;then&lt;/em&gt; debugging locally). It's also a (sometimes necessary) step up distributed tracing.&lt;/p&gt;




&lt;p&gt;I hope you found this post interesting! If you'd like to learn more about durable execution, I recommend reading:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://temporal.io/blog/building-reliable-distributed-systems-in-node"&gt;Building reliable distributed systems&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://temporal.io/blog/building-reliable-distributed-systems-in-node-js-part-2"&gt;How durable execution works&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and watching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=wIpz4ioK0gI"&gt;Introduction to Temporal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=6lSuDRRFgyY"&gt;Why durable execution changes everything&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks to Greg Law, Jason Laster, Chad Retz, and Fitz for reviewing drafts of this post.&lt;/em&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Technically, it doesn't have line-by-line granularity. It only records certain steps that the code takes—read on for more info ☺️. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The astute reader may note that our extension uses the default VS Code debugger, which doesn’t have a back button 😄. I transitioned from talking about TTD to methods of debugging production code via recording, so while Temporal doesn’t have TTD yet, it does record all the non-deterministic inputs to the program and is able to replay execution, so it’s definitely possible to implement. Upvote &lt;a href="https://github.com/temporalio/vscode-debugger-extension/issues/51"&gt;this issue&lt;/a&gt; or comment if you have thoughts on implementation! ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>programming</category>
      <category>debugging</category>
      <category>temporal</category>
    </item>
    <item>
      <title>Sync GitHub Repos to Notion</title>
      <dc:creator>Loren 🤓</dc:creator>
      <pubDate>Wed, 07 Jun 2023 00:50:43 +0000</pubDate>
      <link>https://forem.com/temporalio/sync-github-repos-to-notion-142n</link>
      <guid>https://forem.com/temporalio/sync-github-repos-to-notion-142n</guid>
      <description>&lt;p&gt;At our last company hackathon, I built an internal tool that syncs your GitHub org's list of repositories to a &lt;a href="https://www.notion.so/help/intro-to-databases"&gt;Notion database&lt;/a&gt;. It takes this list:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/orgs/temporalio/repositories?type=public&amp;amp;language=typescript"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2Kbgml2Ezvi3O1rpOkLblh/89202c83425e50f00e5124a9ec71e48f/repos-on-github.png" alt="List of organization repositories on GitHub"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and makes this database in Notion:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/2U90h7mGTnofG9ZoPFU8x3/0acfba827d6287bc17d203c9ee819596/notion-db.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2U90h7mGTnofG9ZoPFU8x3/0acfba827d6287bc17d203c9ee819596/notion-db.png" alt="Notion DB with the same repositories"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The database serves two purposes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tracking which team owns each repo, and whom to contact if you have a question. A couple of questions that come up are: 

&lt;ul&gt;
&lt;li&gt;"This sample repo is out of date—who is responsible for it?"&lt;/li&gt;
&lt;li&gt;"A user asked about their issue/PR on this repo—which team is responsible for responding?"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The two manually updated columns we use for this are "Owner" with team multi-select and "POC" with Person multi-select.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tracking which GitHub teams or individuals have which roles on each repo. It's hard to view all that info on github.com, and having it all in one table makes it much easier. The columns we use for this are:

&lt;ul&gt;
&lt;li&gt;Teams admins&lt;/li&gt;
&lt;li&gt;Individual admins&lt;/li&gt;
&lt;li&gt;Teams with role: maintainer&lt;/li&gt;
&lt;li&gt;Individuals with role: maintainer&lt;/li&gt;
&lt;li&gt;Teams with role: write&lt;/li&gt;
&lt;li&gt;Individuals with role: write&lt;/li&gt;
&lt;li&gt;Teams with role: triage&lt;/li&gt;
&lt;li&gt;Individuals with role: triage&lt;/li&gt;
&lt;li&gt;Teams with role: read&lt;/li&gt;
&lt;li&gt;Individuals with role: read&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Since, by default, everyone in the org has write access to all repos, org members are excluded from the "Individuals with role: write" column to reduce noise.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sync function
&lt;/h3&gt;

&lt;p&gt;The one-way sync is implemented with TypeScript code (&lt;a href="https://github.com/temporalio/github-repo-notion-sync"&gt;&lt;code&gt;temporalio/github-repo-notion-sync&lt;/code&gt;&lt;/a&gt;) that fetches data from the &lt;a href="https://docs.github.com/en/rest?apiVersion=2022-11-28"&gt;GitHub API&lt;/a&gt; and then updates the Notion database via Notion's &lt;a href="https://www.npmjs.com/package/@notionhq/client"&gt;client library&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/090f0967601157b25329dcfa148db3bc635cc2db/sync.ts#L15-L25"&gt;&lt;code&gt;sync.ts&lt;/code&gt;&lt;/a&gt;&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;syncGithubToNotion&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;repos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;teammates&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;getRepos&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;getTeammates&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;withContributors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;addContributors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;repos&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;completeRepos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;addCollaboratorsAndTeams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;withContributors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;teammates&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;transformedRepos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;transformRepos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;completeRepos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;updateNotion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transformedRepos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;syncGithubtoNotion&lt;/code&gt; function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetches the org's repos (&lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/157c21bf061c06e6c527f335bdd20bbc01f7b764/github.ts#L61-L67"&gt;&lt;code&gt;getRepos&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fetches the org's members (&lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/157c21bf061c06e6c527f335bdd20bbc01f7b764/github.ts#L69-L74"&gt;&lt;code&gt;getTeammates&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fetches each repo's contributors (&lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/157c21bf061c06e6c527f335bdd20bbc01f7b764/github.ts#L79-L102"&gt;&lt;code&gt;addContributors&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/157c21bf061c06e6c527f335bdd20bbc01f7b764/github.ts#L104-L169"&gt;Merges&lt;/a&gt; and &lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/157c21bf061c06e6c527f335bdd20bbc01f7b764/sync.ts#L27-L40"&gt;transforms&lt;/a&gt; the above data into the format needed&lt;/li&gt;
&lt;li&gt;Updates the Notion db with the &lt;code&gt;notion.pages.create&lt;/code&gt; and &lt;code&gt;notion.pages.update&lt;/code&gt; functions (&lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/090f0967601157b25329dcfa148db3bc635cc2db/notion.ts#L51-L66"&gt;&lt;code&gt;updateNotion&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running on a schedule
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;syncGithubToNotion&lt;/code&gt; runs daily at 6am. The easiest way to set that up would have been a GitHub Action that runs on a schedule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run daily at 6am&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;6&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v3&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;NOTION_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.NOTION_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GH_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm start&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, GitHub Action crons aren't very reliable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/swyx/status/1552989710301151233"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2a8RtM8VFdUAF7Z2YfCBpA/45a376f71e0b2389f3166699e9b8131b/theo-swyx-tweets.png" alt="Theo and swyx tweets on reliability"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I used a new feature of Temporal called &lt;a href="https://temporal.io/blog/temporal-v1-20-lighter-and-simpler-development-environments-and-improved#the-new-way-to-schedule-workflows-in-temporal"&gt;Schedules&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/github-repo-notion-sync/blob/cadda19a53f15deae8b375c760ef546dc23afb61/create-or-update-schedule.ts"&gt;&lt;code&gt;create-or-update-schedule.ts&lt;/code&gt;&lt;/a&gt;&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;startWorkflow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;workflowType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;syncGithubToNotion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;taskQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;workflowId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gh-notion-sync,
  },
  scheduleId: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;gh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;notion&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,
  spec: {
    calendars: [
      {
        comment: &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt; &lt;span class="nx"&gt;at&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="nx"&gt;am&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,
        hour: 6,
      },
    ],
  },
})
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reliably runs &lt;code&gt;syncGithubToNotion&lt;/code&gt; daily at 6am, and since &lt;code&gt;syncGithubToNotion&lt;/code&gt; is a &lt;a href="https://temporal.io/blog/building-reliable-distributed-systems-in-node"&gt;durable function&lt;/a&gt;, if any part of it fails, that part will automatically be retried until it succeeds, and then the function will continue. It's run so reliably that even if the machine loses power on the third line of the function, the function will continue to run starting from the third line on another machine. &lt;em&gt;Perhaps&lt;/em&gt; that's more reliability than I needed for this particular internal tool, but it was a good use case for Temporal Schedules and demo of durable functions, which you can use for your application's backend code if you want it to run extremely reliably! 😃 (For an intro and explanation of how, see &lt;a href="https://temporal.io/blog/building-reliable-distributed-systems-in-node"&gt;Building Reliable Distributed Systems in Node&lt;/a&gt;.)&lt;/p&gt;




&lt;p&gt;So that was my project 🤓. It's &lt;a href="https://github.com/temporalio/github-repo-notion-sync"&gt;open source&lt;/a&gt; if you'd like to adapt it for your company. Let me know if you have any questions—I'd be happy to help out. I'm &lt;a href="https://twitter.com/lorendsr"&gt;@lorendsr&lt;/a&gt; on Twitter and &lt;a class="mentioned-user" href="https://dev.to/loren"&gt;@loren&lt;/a&gt; on our &lt;a href="https://t.mp/slack"&gt;community Slack&lt;/a&gt; 💃.&lt;/p&gt;

</description>
      <category>cron</category>
      <category>notion</category>
      <category>github</category>
      <category>temporal</category>
    </item>
    <item>
      <title>Temporal for VS Code</title>
      <dc:creator>Loren 🤓</dc:creator>
      <pubDate>Thu, 25 May 2023 19:53:02 +0000</pubDate>
      <link>https://forem.com/temporalio/temporal-for-vs-code-4lcb</link>
      <guid>https://forem.com/temporalio/temporal-for-vs-code-4lcb</guid>
      <description>&lt;p&gt;We just published the first version of our &lt;a href="https://marketplace.visualstudio.com/items?itemName=temporal-technologies.temporalio"&gt;extension for Visual Studio Code&lt;/a&gt;. It currently improves the debugging experience for TypeScript workflows, and in the future we'd like to add other languages, features, and editors. If you have ideas, open an issue in the &lt;a href="https://github.com/temporalio/vscode-debugger-extension"&gt;GitHub repo&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/3ta47lMdxjiUMfU7BjuNOu/18233120dee0bcd59f81376106f46509/vscode.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/3ta47lMdxjiUMfU7BjuNOu/18233120dee0bcd59f81376106f46509/vscode.png" alt="Temporal for VS Code banner"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you run a workflow function, each step it takes is stored as an event in a list called the Event History. You can download a workflow's Event History from the UI or CLI in a JSON file and have an SDK &lt;a href="https://docs.temporal.io/application-development/testing?lang=typescript#replay"&gt;replay it&lt;/a&gt;: the Worker calls your workflow function with the same arguments, the Worker immediately resolves activity calls with the return values recorded in Event History, and other things like timers going off and signals arriving happen in the same order they did previously. You can set breakpoints in workflow code to see what happened during the workflow's original execution. This ability to replay what happened during a function's execution, whether it's on a coworker's machine or in production, is a great help to debugging.&lt;/p&gt;

&lt;p&gt;The Temporal VS Code extension improves the experience of replay debugging. It allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Load Event Histories by Workflow Id or JSON file&lt;/li&gt;
&lt;li&gt;Easily start a replay&lt;/li&gt;
&lt;li&gt;Set breakpoints on events, not just code&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;To learn how to use the extension, watch &lt;a href="https://www.youtube.com/watch?v=3IjQde9HMNY"&gt;this video&lt;/a&gt; or follow these steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install &lt;a href="https://marketplace.visualstudio.com/items?itemName=temporal-technologies.temporalio"&gt;the extension&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you don’t already have a TypeScript workflow, create a project if you don't already have one:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nv"&gt;$ &lt;/span&gt;npx @temporalio/create@latest replay-demo &lt;span class="nt"&gt;--sample&lt;/span&gt; vscode-debugger
  &lt;span class="nv"&gt;$ &lt;/span&gt;code replay-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;If you do have a TypeScript workflow, add a file at &lt;code&gt;src/debug-replayer.ts&lt;/code&gt; (or can configure an alternate location):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;startDebugReplayer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@temporalio/worker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

  &lt;span class="nx"&gt;startDebugReplayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;workflowsPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./workflows&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And edit the &lt;code&gt;'./workflows'&lt;/code&gt; path to match the location of your workflows file. This file is run the by the extension whenever you start a debug session. &lt;a href="https://typescript.temporal.io/api/namespaces/worker#startdebugreplayer"&gt;&lt;code&gt;startDebugReplayer&lt;/code&gt;&lt;/a&gt; creates a &lt;a href="https://docs.temporal.io/dev-guide/typescript/testing?lang=typescript#replay"&gt;replay Worker&lt;/a&gt; (like &lt;a href="https://typescript.temporal.io/api/classes/worker.Worker#runreplayhistory"&gt;&lt;code&gt;Worker.runReplayHistory&lt;/code&gt;&lt;/a&gt; does) and communicates with the extension.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;Temporal: Open Panel&lt;/code&gt; (use &lt;code&gt;Cmd/Ctrl-Shift-P&lt;/code&gt; to open Command Palette)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/2G1v2wbMR053JyGmYx7yH6/7a9532b99a92f9ac5279f8c04af47136/open-panel.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2G1v2wbMR053JyGmYx7yH6/7a9532b99a92f9ac5279f8c04af47136/open-panel.png" alt="Temporal: Open Panel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enter a Workflow Id or choose a history JSON file (if you have the &lt;code&gt;replay-demo&lt;/code&gt; project open, use &lt;a href="//assets.temporal.io/debugger-plugin-demo-event-history.json"&gt;this file&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/2t0PEQ5n2cWcqT6tfQZhEC/7f5e60bc50bf00256f6ec4c1d5dec996/choose-file.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2t0PEQ5n2cWcqT6tfQZhEC/7f5e60bc50bf00256f6ec4c1d5dec996/choose-file.png" alt="MAIN tab of Temporal Panel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;code&gt;Start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The Workflow Execution will start replaying and hit a breakpoint set on the first event:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/3KXvrYFx4Uspi1aBGb9OBc/e01e77633d1f9f5efe62267f04268c0c/started.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/3KXvrYFx4Uspi1aBGb9OBc/e01e77633d1f9f5efe62267f04268c0c/started.png" alt="Debug session paused on the first line of the Workflow function"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set breakpoints in code or on history events. Here's a breakpoint on the &lt;code&gt;sleep&lt;/code&gt; line as well as the history event when the verify Signal arrives:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/71Tu0Kd3AQlIPUrHaoaKNK/59fbc2b9d4c1088111bda55145864a54/set-breakpoints.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/71Tu0Kd3AQlIPUrHaoaKNK/59fbc2b9d4c1088111bda55145864a54/set-breakpoints.png" alt="Red breakpoint dots on a line of code and an event in the HISTORY tab of the Temporal Panel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hit play or step forward. In this example, after you hit play, it goes until the &lt;code&gt;WorkflowExecutionSignaled&lt;/code&gt; event, pausing on the first line of the Signal handler:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/c8BL8aKfFaKhhXZ4qjWzJ/56bf202a9605d74032a11a1b14767d02/first-play.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/c8BL8aKfFaKhhXZ4qjWzJ/56bf202a9605d74032a11a1b14767d02/first-play.png" alt="Paused on first line of the Signal handler"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you hit play again, it stops on the &lt;code&gt;sleep&lt;/code&gt; line:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/75q5t3IXKBVPmG9WZ9aijg/03c9f23b8549d803ace2a71f9f0c4d57/second-play.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/75q5t3IXKBVPmG9WZ9aijg/03c9f23b8549d803ace2a71f9f0c4d57/second-play.png" alt="Paused on sleep('1 day')"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see on the top left that the &lt;code&gt;verified&lt;/code&gt; variable is now &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To restart from the beginning, click the green restart icon:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/14i9m2jSJun0yjnbLGTufe/34b68d8f70d57f8eceff3154bc7cb74b/restart.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/14i9m2jSJun0yjnbLGTufe/34b68d8f70d57f8eceff3154bc7cb74b/restart.png" alt="Top bar of debugging icons"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If instead of restarting, you step forward or hit play again, the debugging session will end (the replay Worker gets to the end of this history file and exits). Once a debug session has ended, to restart it, go back to the &lt;code&gt;MAIN&lt;/code&gt; tab and &lt;code&gt;Start&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;humanVerificationWorkflow&lt;/code&gt; workflow used above is in &lt;a href="https://github.com/temporalio/samples-typescript/blob/main/vscode-debugger/src/workflows.ts"&gt;&lt;code&gt;temporalio/samples-typescript&lt;/code&gt;&lt;/a&gt;. For a more in-depth demo, see &lt;a href="https://www.youtube.com/watch?v=3IjQde9HMNY"&gt;this video&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Server
&lt;/h3&gt;

&lt;p&gt;When starting a replay by Workflow Id (and optionally Run Id), the extension downloads the history from the Temporal Server. By default, it connects to a Server running on the default &lt;code&gt;localhost:7233&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To connect to a different Server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open the &lt;code&gt;SETTINGS&lt;/code&gt; tab&lt;/li&gt;
&lt;li&gt;Edit the &lt;code&gt;Address&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;If you're using TLS (e.g. to connect to Temporal Cloud), check the box and select your client cert and key&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/YEkTnasEpExyFnRa4tsUz/552b20eee7e29ed3a120856f6e99751f/settings.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/YEkTnasEpExyFnRa4tsUz/552b20eee7e29ed3a120856f6e99751f/settings.png" alt="SETTINGS tab of Temporal Panel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Entrypoint
&lt;/h3&gt;

&lt;p&gt;By default, the extension will look for the file that calls &lt;a href="https://typescript.temporal.io/api/namespaces/worker#startdebugreplayer"&gt;&lt;code&gt;startDebugReplayer&lt;/code&gt;&lt;/a&gt; at &lt;code&gt;src/debug-replayer.ts&lt;/code&gt;. To use a different TypeScript or JavaScript file, set the &lt;code&gt;temporal.replayerEntrypoint&lt;/code&gt; config:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open or create &lt;code&gt;.vscode/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add the config field:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"temporal.replayerEntrypoint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test/different-file.ts"&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;That's the extension! If you have any problems with it, please &lt;a href="https://github.com/temporalio/vscode-debugger-extension/issues"&gt;open an issue&lt;/a&gt;. We also welcome contributions—please get consensus for your plan in an issue before submitting a PR ☺️. Happy debugging! 🐞💃&lt;/p&gt;

</description>
      <category>temporal</category>
      <category>workflows</category>
      <category>debugging</category>
    </item>
    <item>
      <title>How Durable Execution Works</title>
      <dc:creator>Loren 🤓</dc:creator>
      <pubDate>Tue, 23 May 2023 17:58:19 +0000</pubDate>
      <link>https://forem.com/temporalio/how-durable-execution-works-2b88</link>
      <guid>https://forem.com/temporalio/how-durable-execution-works-2b88</guid>
      <description>&lt;p&gt;This is part 2 of the series, "Building Reliable Distributed Systems in Node.js." In &lt;a href="https://dev.to/temporalio/building-reliable-distributed-systems-in-node-44i"&gt;part 1&lt;/a&gt;, we went over what durable execution is, its benefits, and what a durable function looks like. In this post, we'll look at how Temporal can provide durable execution.&lt;/p&gt;

&lt;p&gt;A function that can't fail, can last forever, and doesn't need to store data in a database? Sounds like magic. There must be a gotcha—like only a small subset of the language can be used, or it only works on specialized hardware. But in fact, it's just JavaScript—you can use the whole language, and it runs on any server that can run Node.js.&lt;/p&gt;

&lt;p&gt;So how does this all work? You can take a look at the &lt;a href="https://temporal.io/how-temporal-works"&gt;How Temporal Works&lt;/a&gt; diagram, which explains the process with Go code. In this post, we'll go through the process with the TypeScript code from the &lt;a href="https://dev.to/temporalio/building-reliable-distributed-systems-in-node-44i"&gt;previous post&lt;/a&gt; in the series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Client ↔️ Server ↔️ Worker
&lt;/h2&gt;

&lt;p&gt;To start out, a Temporal application has three parts: the Client, the Server, and the Worker.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/WkAWPh6ZMXy5EqdZBCa0b/8f324f1fdee6b877d05e1ffa8ac93239/client-server-worker.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/WkAWPh6ZMXy5EqdZBCa0b/8f324f1fdee6b877d05e1ffa8ac93239/client-server-worker.png" alt="client-server-worker"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Client and Worker both connect to the Server, which has a database and maintains state. The Client says things like "start the &lt;code&gt;order()&lt;/code&gt; durable function," "send it a &lt;code&gt;delivered&lt;/code&gt; Signal," and "terminate the function." The Worker is a long-running Node.js process that has our code and polls the Server for tasks. Tasks look like "run the &lt;code&gt;order()&lt;/code&gt; durable function" or "run the normal &lt;code&gt;sendPushNotification()&lt;/code&gt; function." After the Worker runs the code, it reports the result back to the Server.&lt;/p&gt;

&lt;p&gt;In our delivery app, we create the Client in &lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/packages/common/temporal-client.ts"&gt;&lt;code&gt;temporal-client.ts&lt;/code&gt;&lt;/a&gt; and use it in our Next.js serverless functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/apps/menu/pages/api/%5Btrpc%5D.ts#L10-L26"&gt;&lt;code&gt;apps/menu/pages/api/[trpc].ts&lt;/code&gt;&lt;/a&gt; is the API for &lt;a href="https://temporal.menu/"&gt;temporal.menu&lt;/a&gt;, and it

&lt;ul&gt;
&lt;li&gt;Starts &lt;code&gt;order&lt;/code&gt; Workflows.&lt;/li&gt;
&lt;li&gt;Queries &lt;code&gt;order&lt;/code&gt; Workflows for their status.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/apps/driver/pages/api/%5Btrpc%5D.ts#L9-L48"&gt;&lt;code&gt;apps/driver/pages/api/[trpc].ts&lt;/code&gt;&lt;/a&gt; is the API for &lt;a href="https://drive.temporal.menu/"&gt;drive.temporal.menu&lt;/a&gt;, and it

&lt;ul&gt;
&lt;li&gt;Sends &lt;code&gt;pickedUp&lt;/code&gt; and &lt;code&gt;delivered&lt;/code&gt; Signals to &lt;code&gt;order&lt;/code&gt; Workflows.&lt;/li&gt;
&lt;li&gt;Fetches a list of all &lt;code&gt;order&lt;/code&gt; Workflows and Queries for their statuses.&lt;/li&gt;
&lt;/ul&gt;


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

&lt;p&gt;We create the Worker in &lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/apps/worker/src/worker.ts"&gt;&lt;code&gt;apps/worker/src&lt;br&gt;
/worker.ts&lt;/code&gt;&lt;/a&gt;:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NativeConnection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Worker&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;@temporalio/worker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;activities&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;activities&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;taskQueue&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;common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getConnectionOptions&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;common/lib/temporal-connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;run&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;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;NativeConnection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getConnectionOptions&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;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;workflowsPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../../packages/workflows/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="nx"&gt;activities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="k"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;taskQueue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;

 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;run&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We pass the Worker our code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The path to our &lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/packages/workflows/order.ts"&gt;Workflows&lt;/a&gt;, which get bundled with Webpack and &lt;a href="https://temporal.io/blog/intro-to-isolated-vm"&gt;run in isolated contexts&lt;/a&gt; to ensure &lt;a href="https://legacy-documentation-sdks.temporal.io/typescript/determinism"&gt;determinism&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Our imported &lt;em&gt;Activities&lt;/em&gt;—the non-durable functions called by our &lt;code&gt;order&lt;/code&gt; Workflow: &lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/packages/activities/index.ts"&gt;&lt;code&gt;sendPushNotification()&lt;/code&gt;, &lt;code&gt;refundOrder()&lt;/code&gt;, and &lt;code&gt;chargeCustomer()&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And set up &lt;a href="https://render.com/"&gt;Render&lt;/a&gt; to automatically build and deploy on pushes to &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/3xY8HgVrlVYYCwheKUqMyQ/9d6398edf25ac41d3d8beac297d82274/render.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/3xY8HgVrlVYYCwheKUqMyQ/9d6398edf25ac41d3d8beac297d82274/render.png" alt="render"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running each part
&lt;/h2&gt;

&lt;p&gt;In production, our web apps and their serverless functions are deployed to Vercel, our long-running Worker process is deployed to Render, and they both talk to a Server instance hosted by Temporal Cloud. The Server is an &lt;a href="https://github.com/temporalio/temporal"&gt;open source&lt;/a&gt; cluster of services which work with a database (SQL or Cassandra) and ElasticSearch. You can also host all that it &lt;a href="https://docs.temporal.io/cluster-deployment-guide"&gt;yourself&lt;/a&gt;, or you can save a lot of time and get peace of mind with higher reliability and scale by &lt;a href="https://temporal.io/cloud"&gt;paying the experts&lt;/a&gt; to host it 😄.&lt;/p&gt;

&lt;p&gt;In development, we can run all three parts locally. First, we install the Temporal CLI, which has a development version of the Server:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Homebrew: &lt;code&gt;brew install temporal&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;cURL: &lt;code&gt;curl -sSf https://temporal.download/cli.sh | sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Manual: Download and extract the &lt;a href="https://github.com/temporalio/cli/releases"&gt;latest release&lt;/a&gt; and then add it to your PATH.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We start the Server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;temporal server start-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal, with &lt;a href="https://nodejs.org/en/download/"&gt;Node&lt;/a&gt; v16 or higher, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @temporalio/create@latest ./temporal-delivery &lt;span class="nt"&gt;--sample&lt;/span&gt; food-delivery
&lt;span class="nb"&gt;cd&lt;/span&gt; ./temporal-delivery
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;dev&lt;/code&gt; script runs the two Next.js web apps and the Worker. The menu is running at &lt;a href="http://localhost:3000/"&gt;&lt;code&gt;localhost:3000&lt;/code&gt;&lt;/a&gt;, where we can click "Order", and the driver portal is at &lt;a href="http://localhost:3001/"&gt;&lt;code&gt;localhost:3001&lt;/code&gt;&lt;/a&gt;, where we can mark the item we ordered as picked up and delivered. Once we've done that, we can see the "Delivered" status of the order in both of the web apps. We can also see the status of the corresponding Workflow Execution in the Server's Web UI at &lt;a href="http://localhost:8233/"&gt;&lt;code&gt;localhost:8233&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/Sw3J2r6grbVAjYbnFmqvR/cedd05d1c4c7aa8a6724c1c3b9c7479d/workflow-list.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/Sw3J2r6grbVAjYbnFmqvR/cedd05d1c4c7aa8a6724c1c3b9c7479d/workflow-list.png" alt="workflow-list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see its status is Completed, and when we click on it, we see the Event History—the list of events that took place during the execution of the &lt;code&gt;order()&lt;/code&gt; function:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/2ajTFT9kt2L1mVWVA0tQAW/4f347016c009453e71e14088feb415db/event-history.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2ajTFT9kt2L1mVWVA0tQAW/4f347016c009453e71e14088feb415db/event-history.png" alt="event-history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first event is always &lt;code&gt;WorkflowExecutionStarted&lt;/code&gt;, which contains the type of Workflow being started—in this case, an &lt;code&gt;order&lt;/code&gt; Workflow. We'll look more at events in the next section.&lt;/p&gt;

&lt;p&gt;In the Queries tab, we can select the &lt;code&gt;getStatus&lt;/code&gt; Query, which (assuming our Worker is still running) will send a Query to the &lt;code&gt;order&lt;/code&gt; function, which responds that the order was delivered, the time of delivery, and which item was delivered:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/2FKGQju4hGXSWezu5X7gcU/c4591da83a62e0daa4797cee361fdf15/query.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2FKGQju4hGXSWezu5X7gcU/c4591da83a62e0daa4797cee361fdf15/query.png" alt="query"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequence of events
&lt;/h2&gt;

&lt;p&gt;Now let's look at what happened behind the scenes during our order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start order
&lt;/h3&gt;

&lt;p&gt;When we clicked the "Order" button, the API handler used a Temporal Client to send a start command to the Server:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/apps/menu/pages/api/%5Btrpc%5D.ts#L14-L18"&gt;&lt;code&gt;apps/menu/pages/api/[trpc].ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/hNkrXyNvb8ImO1kThj22J/ed29e83942f6907909e035d7e21e79f7/start.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/hNkrXyNvb8ImO1kThj22J/ed29e83942f6907909e035d7e21e79f7/start.png" alt="start"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Server saves the &lt;code&gt;WorkflowExecutionStarted&lt;/code&gt; event to the Event History and returns. It also creates a &lt;code&gt;WorkflowTaskScheduled&lt;/code&gt; event, which results in a "Workflow Task" (an instruction to run the &lt;code&gt;order()&lt;/code&gt; function) getting added to the task queue, which the Worker is polling on.&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/5AIbW3MIhrA0eipYexmeTo/493b83704772cdf6c3ac87c6524180ba/workflow-task.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/5AIbW3MIhrA0eipYexmeTo/493b83704772cdf6c3ac87c6524180ba/workflow-task.png" alt="workflow-task"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Worker receives the task, the Server adds the &lt;code&gt;WorkflowTaskStarted&lt;/code&gt; Event, and the Worker performs the task—in this case, calling &lt;code&gt;order(3)&lt;/code&gt;. The &lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/packages/workflows/order.ts#L32-L61"&gt;&lt;code&gt;order&lt;/code&gt; function&lt;/a&gt; runs until it hits this line:&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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;chargeCustomer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refundOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sendPushNotification&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;proxyActivities&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;activities&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;...&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="p"&gt;...&lt;/span&gt;

 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chargeCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Call Activity
&lt;/h3&gt;

&lt;p&gt;When the function calls the &lt;code&gt;chargeCustomer&lt;/code&gt; Activity, the Worker tells the Server:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/4r11nlgZHdIznbqktCgQu5/191534313b8ea9fc1a5f3941e301e8fd/call-activity.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/4r11nlgZHdIznbqktCgQu5/191534313b8ea9fc1a5f3941e301e8fd/call-activity.png" alt="call-activity"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Server adds the &lt;code&gt;WorkflowTaskCompleted&lt;/code&gt; event (the Workflow Execution didn't complete—just the initial task of "run the &lt;code&gt;order()&lt;/code&gt; function and see what happens") and an &lt;code&gt;ActivityTaskScheduled&lt;/code&gt; event with the Activity type and arguments:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/6M6GwWc9moyVD2PR5pLUcy/84d605c5ce4cefbb7d874c0e2a49b345/activity-task-scheduled.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/6M6GwWc9moyVD2PR5pLUcy/84d605c5ce4cefbb7d874c0e2a49b345/activity-task-scheduled.png" alt="activity-task-scheduled"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then the Server adds an Activity Task (an instruction to run an Activity function) to the task queue, which the Worker picks up:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/ONYiKIDVyeCI5hDPTeDJh/9ab5af6447bdc773fccb0872f26060a4/get-activity-task.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/ONYiKIDVyeCI5hDPTeDJh/9ab5af6447bdc773fccb0872f26060a4/get-activity-task.png" alt="get-activity-task"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In development, we're only running a single Worker process, so it's getting all the tasks, but in production, we'll have enough Workers to handle our load, and any of them can pick up the Activity Task—not just the one that ran the &lt;code&gt;order&lt;/code&gt; function.&lt;/p&gt;

&lt;p&gt;The Worker follows the Activity Task instructions, running the &lt;code&gt;chargeCustomer()&lt;/code&gt; function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/2add450/food-delivery/packages/activities/index.ts#L12-L14"&gt;&lt;code&gt;packages/activities/index.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Which calls &lt;code&gt;paymentService.charge()&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/2add450/food-delivery/packages/activities/services.ts#L14-L24"&gt;&lt;code&gt;packages/activities/services.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the function throws an error, the Worker reports it back to the Server:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/mjbZpJijA82CEMYWD5cMt/4684b6684f19df463dcb9ca3dc3d37b8/activity-failed.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/mjbZpJijA82CEMYWD5cMt/4684b6684f19df463dcb9ca3dc3d37b8/activity-failed.png" alt="activity-failed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the Server schedules a retry. The &lt;a href="https://docs.temporal.io/retry-policies#default-values-for-retry-policy"&gt;default&lt;/a&gt; initial interval is 1 second, so in 1 second, the Activity Task will be added back to the queue for a Worker to pick up.&lt;/p&gt;

&lt;p&gt;If the function completes successfully, the Worker reports success (and the return value, but in this case there is none) back to the Server:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/5O9Z6i0TvCzXwqcfZs32UJ/48f3e81e49654c497d30c4f5011807d3/activity-completed.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/5O9Z6i0TvCzXwqcfZs32UJ/48f3e81e49654c497d30c4f5011807d3/activity-completed.png" alt="activity-completed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Server adds the &lt;code&gt;ActivityTaskStarted&lt;/code&gt; and &lt;code&gt;ActivityTaskCompleted&lt;/code&gt; events. Now that the Activity is completed, the &lt;code&gt;order()&lt;/code&gt; function can continue executing, so the Server adds another &lt;code&gt;WorkflowTaskScheduled&lt;/code&gt; event. It also adds a corresponding Workflow Task to the queue, which the Worker picks up (at which point the Server adds another &lt;code&gt;WorkflowTaskStarted&lt;/code&gt; event). &lt;/p&gt;

&lt;h3&gt;
  
  
  Second Workflow Task
&lt;/h3&gt;

&lt;p&gt;If the Worker still has the execution context of the order function, it can just resolve the &lt;code&gt;chargeCustomer(product)&lt;/code&gt; Promise, and the function will continue executing. If the Worker doesn't have the execution context (because it was evicted from cache in order to make room for another Workflow—see &lt;a href="https://typescript.temporal.io/api/interfaces/worker.WorkerOptions#maxcachedworkflows"&gt;WorkerOptions.maxCachedWorkflows&lt;/a&gt;—or the process crashed or restarted), then the Worker fetches the Event History from the Server, creates a new isolate, and calls the function again:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/6HSlf8Wln0SPDf7HFDqQC8/35784789c2cdbb00cecab7949989c4d0/get-history.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/6HSlf8Wln0SPDf7HFDqQC8/35784789c2cdbb00cecab7949989c4d0/get-history.png" alt="get-history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, when the function hits the &lt;code&gt;await chargeCustomer(product)&lt;/code&gt; line, the Worker knows from event 7, &lt;code&gt;ActivityTaskCompleted&lt;/code&gt;, that &lt;code&gt;chargeCustomer&lt;/code&gt; has already been run, so instead of sending a "Call Activity" command to the Server, it immediately resolves the Promise. The function continues running until the next &lt;code&gt;await&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/packages/workflows/order.ts#L61-L70"&gt;&lt;code&gt;packages/workflows/order.ts&lt;/code&gt;&lt;/a&gt;&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;const&lt;/span&gt; &lt;span class="nx"&gt;notPickedUpInTime&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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Picked up&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="s1"&gt;1 min&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;&lt;code&gt;condition()&lt;/code&gt; will wait until either the state becomes &lt;code&gt;Picked up&lt;/code&gt; or 1 minute has passed. When it's called, the Worker tells the Server to set a timer:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/5nU51bzar7blN6XcqIaXdA/2ca68e3dddd701ef20a66f0e8d7e1ff7/set-timer.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/5nU51bzar7blN6XcqIaXdA/2ca68e3dddd701ef20a66f0e8d7e1ff7/set-timer.png" alt="set-timer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Server adds the &lt;code&gt;WorkflowTaskCompleted&lt;/code&gt; and &lt;code&gt;TimerStarted&lt;/code&gt; events to the Event History, and sets a timer in the database. When the timer goes off, a &lt;code&gt;TimerFired&lt;/code&gt; event will be added along with a &lt;code&gt;WorkflowTaskScheduled&lt;/code&gt; and Workflow Task on the queue telling the Worker the 1 minute is up, at which point the Worker will know to resolve the &lt;code&gt;condition()&lt;/code&gt; Promise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Send Signal
&lt;/h3&gt;

&lt;p&gt;But in our case, that didn't happen. Instead, before a minute was up, we clicked the "Pick up" button in the driver portal, which &lt;a href="https://github.com/temporalio/samples-typescript/blob/2add450db74666f4006ad5ea1b520ddcdef76c90/food-delivery/apps/driver/pages/api/%5Btrpc%5D.ts#L12"&gt;sent a &lt;code&gt;pickedUp&lt;/code&gt; Signal&lt;/a&gt; to the Workflow:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/53BHTZ2ChZMy5tzRT1h7nf/7aa016c7c6ee8778992ee42303a99f05/send-signal.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/53BHTZ2ChZMy5tzRT1h7nf/7aa016c7c6ee8778992ee42303a99f05/send-signal.png" alt="send-signal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Server then added two events: a &lt;code&gt;WorkflowExecutionSignaled&lt;/code&gt; event with the Signal info, and another &lt;code&gt;WorkflowTaskScheduled&lt;/code&gt; event. Then a Workflow Task was added to the queue with the Signal info, which was picked up by the Worker:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/s9B81we063w1Dgh9vSkks/fadf12dbb8208a06300df86709b2fdf0/get-signal.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/s9B81we063w1Dgh9vSkks/fadf12dbb8208a06300df86709b2fdf0/get-signal.png" alt="get-signal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Worker then runs the &lt;code&gt;pickedUp&lt;/code&gt; Signal handler:&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="nx"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pickedUpSignal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Picked up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/packages/workflows/order.ts#L42-L46"&gt;&lt;code&gt;packages/workflows/order.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The handler changes the state to &lt;code&gt;Picked up&lt;/code&gt;, and after Signal handlers have been called, the Worker runs all the &lt;code&gt;condition()&lt;/code&gt; functions. Now &lt;code&gt;() =&amp;gt; state === 'Picked up'&lt;/code&gt; will return &lt;code&gt;true&lt;/code&gt;, so the Worker will resolve the &lt;code&gt;condition()&lt;/code&gt; Promise and continue executing, to see what the function does next, which will determine the next command(s) it sends to the Server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Event History
&lt;/h3&gt;

&lt;p&gt;All together, the part of the Event History we covered was:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/7cZfmMiaTyXULMlFjQjRlO/80372681650c47bac65f370d039d74d0/just-history.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/7cZfmMiaTyXULMlFjQjRlO/80372681650c47bac65f370d039d74d0/just-history.png" alt="just-history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Event History is the core of what enables durable execution: a log of everything important that the Workflow does and gets sent. It allows us to Ctrl-C our Worker process, start it again, open up the UI, select our completed &lt;code&gt;order&lt;/code&gt; Workflow, go to the Queries tab of the UI, and send the &lt;code&gt;getStatus&lt;/code&gt; Query, which the Server will put on a queue for the Worker to pick up, which won't have the Workflow in cache, so it will fetch the Event History from the Server, and then call the &lt;code&gt;order()&lt;/code&gt; function, immediately resolving any async functions with the original result from History, and then calling the &lt;code&gt;getStatus&lt;/code&gt; handler function:&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="nx"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getStatusQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deliveredAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/ba6208155bc7b547c76931b7e14e92ce4594af28/food-delivery/packages/workflows/order.ts#L55-L57"&gt;&lt;code&gt;packages/workflows/order.ts&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the whole function and all the Signal handlers have been run, the &lt;code&gt;{ state, deliveredAt, productId }&lt;/code&gt; variables will all have their final values from when the function was originally executed, and they'll be returned to the Server, which returns them to the Client, which returns them to the UI to display on our screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/2FKGQju4hGXSWezu5X7gcU/c4591da83a62e0daa4797cee361fdf15/query.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2FKGQju4hGXSWezu5X7gcU/c4591da83a62e0daa4797cee361fdf15/query.png" alt="query"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We examined how durable code works under the hood—how we can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write functions that can't fail to complete executing (since when the Worker process dies, the next Worker to pick up the task will get the function's Event History and use the events to re-run the code until it's in the same state).&lt;/li&gt;
&lt;li&gt;Retry the functions that might have transient failures (if the &lt;code&gt;chargeCustomer&lt;/code&gt; Activity can't reach the payment service, the Server automatically schedules another Activity Task).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best part is that these failures are transparent to the application developer—we just write our Workflow and Activity code, and Temporal handles reliably executing it. 💃&lt;/p&gt;

&lt;p&gt;In the next post, you’ll learn more things you can do with durable functions. To get notified when it comes out, you can follow us on &lt;a href="https://twitter.com/temporalio"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/company/temporal-technologies/posts/"&gt;LinkedIn&lt;/a&gt;. Also, check out our new &lt;a href="https://learn.temporal.io/courses/temporal_101/typescript"&gt;Temporal 101 course&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;🖖 till next time!&lt;br&gt;
—&lt;a href="https://twitter.com/lorendsr"&gt;Loren&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks to Brian Hogan, Roey Berman, Patrick Rachford, and Dail Magee Jr for reading drafts of this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>Using the Temporal CLI</title>
      <dc:creator>Loren 🤓</dc:creator>
      <pubDate>Thu, 27 Apr 2023 18:21:14 +0000</pubDate>
      <link>https://forem.com/temporalio/using-the-temporal-cli-4a32</link>
      <guid>https://forem.com/temporalio/using-the-temporal-cli-4a32</guid>
      <description>&lt;p&gt;In the &lt;a href="https://temporal.io/blog/temporal-v1-20-lighter-and-simpler-development-environments-and-improved" rel="noopener noreferrer"&gt;Server 1.20 release&lt;/a&gt; we announced a new Temporal CLI, and we also released an &lt;a href="https://www.youtube.com/watch?v=-BJRQmNCi2I" rel="noopener noreferrer"&gt;interview&lt;/a&gt; and &lt;a href="https://temporal.io/blog/temporalite-the-foundation-of-the-new-temporal-cli-experience" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; with more info about it and its history. &lt;/p&gt;

&lt;p&gt;The tl;dr is we combined our efforts on: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;next&lt;/code&gt; version of &lt;a href="https://github.com/temporalio/tctl" rel="noopener noreferrer"&gt;&lt;code&gt;tctl&lt;/code&gt;&lt;/a&gt;, our old CLI&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/temporalio/temporalite" rel="noopener noreferrer"&gt;&lt;code&gt;temporalite&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;to create a single &lt;code&gt;temporal&lt;/code&gt; command-line tool that lets you both: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run a development version of the Server&lt;/li&gt;
&lt;li&gt;interact with the Server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We recommend switching to &lt;code&gt;temporal&lt;/code&gt; for development and testing, and recommend using it for production scripts once we release v1 (coming soon). We will continue supporting &lt;code&gt;tctl&lt;/code&gt; for two more Server releases (through &lt;code&gt;1.22&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In this post, you’ll learn how to use &lt;code&gt;temporal&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contents:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installing&lt;/li&gt;
&lt;li&gt;Running the Server&lt;/li&gt;
&lt;li&gt;Interacting with the Server&lt;/li&gt;
&lt;li&gt;Environments&lt;/li&gt;
&lt;li&gt;Aliases&lt;/li&gt;
&lt;li&gt;Upgrading&lt;/li&gt;
&lt;li&gt;Future&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Installing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  cURL
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;curl -sSf https://temporal.download/cli.sh | sh&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Homebrew
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;brew install temporal&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual
&lt;/h3&gt;

&lt;p&gt;You can also manually download, extract, and add to PATH the &lt;a href="https://github.com/temporalio/cli#manual" rel="noopener noreferrer"&gt;latest binary&lt;/a&gt; for your CPU architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Server
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;temporal server start-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;starts the Server on &lt;code&gt;localhost:7233&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;runs the Web UI at &lt;a href="http://localhost:8233" rel="noopener noreferrer"&gt;http://localhost:8233&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By default, it doesn’t persist your data—if you start a Workflow, Ctrl-C, and run the command again, your Workflow will be gone. &lt;/p&gt;

&lt;p&gt;If you'd like your Workflows to be saved, use the &lt;code&gt;--db-filename&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;temporal server start-dev &lt;span class="nt"&gt;--db-filename&lt;/span&gt; temporal.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interacting with the Server
&lt;/h2&gt;

&lt;p&gt;In another terminal, you can run commands to interact with the Server. This command starts a Workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow start &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--task-queue&lt;/span&gt; hello-world &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--type&lt;/span&gt; MyWorkflow &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--workflow-id&lt;/span&gt; 123 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--input&lt;/span&gt; 456

Running execution:
  WorkflowId                                   123
  RunId       357074e4-0dd8-4c44-8367-d92536dd0943
  Type        MyWorkflow
  Namespace   default
  TaskQueue   hello-world
  Args        &lt;span class="o"&gt;[&lt;/span&gt;456]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shorthand options are:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;temporal workflow start &lt;span class="nt"&gt;-t&lt;/span&gt; hello-world &lt;span class="nt"&gt;--type&lt;/span&gt; MyWorkflow &lt;span class="nt"&gt;-w&lt;/span&gt; 123 &lt;span class="nt"&gt;-i&lt;/span&gt; 456
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also list and describe Workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow list

  Status   WorkflowId     Name       StartTime
  Running         123  MyWorkflow  14 seconds ago

&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow describe &lt;span class="nt"&gt;--workflow-id&lt;/span&gt; 123

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"executionConfig"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"taskQueue"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"hello-world"&lt;/span&gt;,
      &lt;span class="s2"&gt;"kind"&lt;/span&gt;: &lt;span class="s2"&gt;"Normal"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"workflowExecutionTimeout"&lt;/span&gt;: &lt;span class="s2"&gt;"0s"&lt;/span&gt;,
    &lt;span class="s2"&gt;"workflowRunTimeout"&lt;/span&gt;: &lt;span class="s2"&gt;"0s"&lt;/span&gt;,
    &lt;span class="s2"&gt;"defaultWorkflowTaskTimeout"&lt;/span&gt;: &lt;span class="s2"&gt;"10s"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"workflowExecutionInfo"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"execution"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"workflowId"&lt;/span&gt;: &lt;span class="s2"&gt;"123"&lt;/span&gt;,
      &lt;span class="s2"&gt;"runId"&lt;/span&gt;: &lt;span class="s2"&gt;"357074e4-0dd8-4c44-8367-d92536dd0943"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"MyWorkflow"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"startTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2023-04-15T06:42:31.191137Z"&lt;/span&gt;,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: &lt;span class="s2"&gt;"Running"&lt;/span&gt;,
    &lt;span class="s2"&gt;"historyLength"&lt;/span&gt;: &lt;span class="s2"&gt;"2"&lt;/span&gt;,
    &lt;span class="s2"&gt;"executionTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2023-04-15T06:42:31.191137Z"&lt;/span&gt;,
    &lt;span class="s2"&gt;"memo"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"autoResetPoints"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"stateTransitionCount"&lt;/span&gt;: &lt;span class="s2"&gt;"1"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="s2"&gt;"pendingWorkflowTask"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"state"&lt;/span&gt;: &lt;span class="s2"&gt;"Scheduled"&lt;/span&gt;,
    &lt;span class="s2"&gt;"scheduledTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2023-04-15T06:42:31.191173Z"&lt;/span&gt;,
    &lt;span class="s2"&gt;"originalScheduledTime"&lt;/span&gt;: &lt;span class="s2"&gt;"2023-04-15T06:42:31.191173Z"&lt;/span&gt;,
    &lt;span class="s2"&gt;"attempt"&lt;/span&gt;: 1
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When listing, you can get more Workflow fields and output in JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow list &lt;span class="nt"&gt;--fields&lt;/span&gt; long &lt;span class="nt"&gt;--output&lt;/span&gt; json

&lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"execution"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"workflow_id"&lt;/span&gt;: &lt;span class="s2"&gt;"123"&lt;/span&gt;,
      &lt;span class="s2"&gt;"run_id"&lt;/span&gt;: &lt;span class="s2"&gt;"357074e4-0dd8-4c44-8367-d92536dd0943"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"MyWorkflow"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;,
    &lt;span class="s2"&gt;"start_time"&lt;/span&gt;: &lt;span class="s2"&gt;"2023-04-15T06:42:31.191137Z"&lt;/span&gt;,
    &lt;span class="s2"&gt;"status"&lt;/span&gt;: 1,
    &lt;span class="s2"&gt;"execution_time"&lt;/span&gt;: &lt;span class="s2"&gt;"2023-04-15T06:42:31.191137Z"&lt;/span&gt;,
    &lt;span class="s2"&gt;"memo"&lt;/span&gt;: &lt;span class="o"&gt;{}&lt;/span&gt;,
    &lt;span class="s2"&gt;"task_queue"&lt;/span&gt;: &lt;span class="s2"&gt;"hello-world"&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filter out just the type with &lt;a href="https://stedolan.github.io/jq/" rel="noopener noreferrer"&gt;jq&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow list &lt;span class="nt"&gt;--fields&lt;/span&gt; long &lt;span class="nt"&gt;-o&lt;/span&gt; json | jq &lt;span class="s1"&gt;'.[].type.name'&lt;/span&gt;

&lt;span class="s2"&gt;"OtherWorkflow"&lt;/span&gt;
&lt;span class="s2"&gt;"MyWorkflow"&lt;/span&gt;
&lt;span class="s2"&gt;"MyWorkflow"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And count how many Workflows of each type you have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow list &lt;span class="nt"&gt;--fields&lt;/span&gt; long &lt;span class="nt"&gt;-o&lt;/span&gt; json | jq &lt;span class="s1"&gt;'.[].type.name'&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt;

   1 &lt;span class="s2"&gt;"OtherWorkflow"&lt;/span&gt;
   2 &lt;span class="s2"&gt;"MyWorkflow"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a list of common commands, see our &lt;a href="https://github.com/temporalio/cheatsheets/blob/main/dev-cheatsheet.md" rel="noopener noreferrer"&gt;CLI cheatsheet&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To see what else you do to Workflows, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow 

NAME:
   temporal workflow - Operations that can be performed on Workflows.

COMMANDS:
   start        Starts a new Workflow Execution.
   execute      Start a new Workflow Execution and prints its progress.
   describe     Show information about a Workflow Execution.
   list         List Workflow Executions based on a Query.
   show         Show Event History &lt;span class="k"&gt;for &lt;/span&gt;a Workflow Execution.
   query        Query a Workflow Execution.
   stack        Query a Workflow Execution with __stack_trace as the query type.
   signal       Signal Workflow Execution by Id or List Filter.
   count        Count Workflow Executions &lt;span class="o"&gt;(&lt;/span&gt;requires ElasticSearch to be enabled&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
   cancel       Cancel a Workflow Execution.
   terminate    Terminate Workflow Execution by Id or List Filter.
   delete       Deletes a Workflow Execution.
   reset        Resets a Workflow Execution by Event Id or reset type.
   reset-batch  Reset a batch of Workflow Executions by reset &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;LastContinuedAsNew&lt;span class="o"&gt;)&lt;/span&gt;, FirstWorkflowTask&lt;span class="o"&gt;)&lt;/span&gt;, LastWorkflowTask
   trace        Trace progress of a Workflow Execution and its children.
   &lt;span class="nb"&gt;help&lt;/span&gt;, h      Shows a list of commands or &lt;span class="nb"&gt;help &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;one &lt;span class="nb"&gt;command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here are all the top-level commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal

NAME:
   temporal - Temporal command-line interface and development server

USAGE:
   temporal &lt;span class="o"&gt;[&lt;/span&gt;global options] &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;command &lt;/span&gt;options] &lt;span class="o"&gt;[&lt;/span&gt;arguments...]

VERSION:
   0.8.0 &lt;span class="o"&gt;(&lt;/span&gt;server 1.20.1&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;ui 2.13.3&lt;span class="o"&gt;)&lt;/span&gt;

COMMANDS:
   server      Commands &lt;span class="k"&gt;for &lt;/span&gt;managing the Temporal Server.
   workflow    Operations that can be performed on Workflows.
   activity    Operations that can be performed on Workflow Activities.
   task-queue  Operations performed on Task Queues.
   schedule    Operations performed on Schedules.
   batch       Operations performed on Batch jobs.
   operator    Operations performed on the Temporal Server.
   &lt;span class="nb"&gt;env         &lt;/span&gt;Manage environmental configurations on Temporal Client.
   completion  Output shell completion code &lt;span class="k"&gt;for &lt;/span&gt;the specified shell &lt;span class="o"&gt;(&lt;/span&gt;zsh, bash&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
   &lt;span class="nb"&gt;help&lt;/span&gt;, h     Shows a list of commands or &lt;span class="nb"&gt;help &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;one &lt;span class="nb"&gt;command&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about each by running &lt;code&gt;temporal &amp;lt;command&amp;gt;&lt;/code&gt; or reading the &lt;a href="https://docs.temporal.io/cli/" rel="noopener noreferrer"&gt;CLI docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environments
&lt;/h2&gt;

&lt;p&gt;So far, the CLI has been talking to the Server at the default address, &lt;code&gt;localhost:7233&lt;/code&gt;. To talk to another Server, like a production namespace on Temporal Cloud:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an environment named &lt;code&gt;prod&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pass &lt;code&gt;--env prod&lt;/code&gt; to commands, like &lt;code&gt;temporal workflow list --env prod&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To create a new environment, start setting its properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;temporal &lt;span class="nb"&gt;env set &lt;/span&gt;prod.namespace production.f45a2
temporal &lt;span class="nb"&gt;env set &lt;/span&gt;prod.address production.f45a2.tmprl.cloud:7233
temporal &lt;span class="nb"&gt;env set &lt;/span&gt;prod.tls-cert-path /temporal/certs/prod.pem
temporal &lt;span class="nb"&gt;env set &lt;/span&gt;prod.tls-key-path /temporal/certs/prod.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that you set them correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal &lt;span class="nb"&gt;env &lt;/span&gt;get prod

  address        production.f45a2.tmprl.cloud:7233
  namespace      production.f45a2
  tls-cert-path  /temporal/certs/prod.pem
  tls-key-path   /temporal/certs/prod.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If they’re correct, then this shouldn’t log a connection error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal workflow list &lt;span class="nt"&gt;--env&lt;/span&gt; prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the full list of properties you can set, see the below options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal &lt;span class="nb"&gt;env set&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt;

OPTIONS:
   Client Options:

   &lt;span class="nt"&gt;--address&lt;/span&gt; value                          The host and port &lt;span class="o"&gt;(&lt;/span&gt;formatted as host:port&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;the Temporal Frontend Service. &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--codec-auth&lt;/span&gt; value                       Sets the authorization header on requests to the Codec Server. &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_CODEC_AUTH&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--codec-endpoint&lt;/span&gt; value                   Endpoint &lt;span class="k"&gt;for &lt;/span&gt;a remote Codec Server. &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_CODEC_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--context-timeout&lt;/span&gt; value                  An optional &lt;span class="nb"&gt;timeout &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;the context of an RPC call &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;in &lt;/span&gt;seconds&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;default: 5&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CONTEXT_TIMEOUT&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--env&lt;/span&gt; value                              Name of the environment to &lt;span class="nb"&gt;read &lt;/span&gt;environmental variables from. &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="nt"&gt;--grpc-meta&lt;/span&gt; value &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;--grpc-meta&lt;/span&gt; value &lt;span class="o"&gt;]&lt;/span&gt;  Contains gRPC metadata to send with requests &lt;span class="o"&gt;(&lt;/span&gt;format: &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;value&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; Values must be &lt;span class="k"&gt;in &lt;/span&gt;a valid JSON format.
   &lt;span class="nt"&gt;--namespace&lt;/span&gt; value, &lt;span class="nt"&gt;-n&lt;/span&gt; value              Identifies a Namespace &lt;span class="k"&gt;in &lt;/span&gt;the Temporal Workflow. &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_NAMESPACE&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--tls-ca-path&lt;/span&gt; value                      Path to server CA certificate. &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_TLS_CA&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--tls-cert-path&lt;/span&gt; value                    Path to x509 certificate. &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_TLS_CERT&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--tls-disable-host-verification&lt;/span&gt;          Disables TLS host name verification &lt;span class="k"&gt;if &lt;/span&gt;already enabled. &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_TLS_DISABLE_HOST_VERIFICATION&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--tls-key-path&lt;/span&gt; value                     Path to private certificate key. &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_TLS_KEY&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
   &lt;span class="nt"&gt;--tls-server-name&lt;/span&gt; value                  Provides an override &lt;span class="k"&gt;for &lt;/span&gt;the target TLS server name. &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$TEMPORAL_CLI_TLS_SERVER_NAME&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

   Display Options:

   &lt;span class="nt"&gt;--color&lt;/span&gt; value  when to use color: auto, always, never. &lt;span class="o"&gt;(&lt;/span&gt;default: &lt;span class="s2"&gt;"auto"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, to set &lt;code&gt;--codec-endpoint&lt;/code&gt;, you would do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;temporal &lt;span class="nb"&gt;env set &lt;/span&gt;prod.codec-endpoint localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Aliases
&lt;/h2&gt;

&lt;p&gt;If you like aliases, here’s what I have in my &lt;code&gt;~/.bash_profile&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'temporal'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;tw&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'temporal workflow'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;ts&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'temporal server start-dev'&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;tsdb&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'temporal server start-dev --db-filename ~/temporal.db'&lt;/span&gt;

&lt;span class="c"&gt;# send process to background so you can continue using the terminal&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;tsbg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'temporal server start-dev &amp;amp;&amp;gt; /dev/null &amp;amp; disown'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also set up &lt;a href="https://docs.temporal.io/cli#auto-completion" rel="noopener noreferrer"&gt;auto-completion for zsh or bash&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrading
&lt;/h2&gt;

&lt;p&gt;If you’re moving from &lt;code&gt;tctl&lt;/code&gt;, here are the major changes to be aware of: &lt;/p&gt;

&lt;h3&gt;
  
  
  Global changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;New pagination mechanism with OS level tools such as &lt;code&gt;less&lt;/code&gt; or &lt;code&gt;more&lt;/code&gt;. Controlled by &lt;code&gt;--pager&lt;/code&gt; or &lt;code&gt;$PAGER&lt;/code&gt;. &lt;a href="https://github.com/temporalio/proposals/blob/master/cli/001-cli-improve-piping-support.md" rel="noopener noreferrer"&gt;details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Most commands can format the output as a table or JSON using the &lt;code&gt;--output&lt;/code&gt; option. &lt;a href="https://github.com/temporalio/proposals/blob/master/cli/006-cli-output-options.md" rel="noopener noreferrer"&gt;details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You can limit the number of items when listing workflows, namespaces, etc. with, e.g. &lt;code&gt;--limit 10&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;DateTime formatting. &lt;a href="https://github.com/temporalio/proposals/blob/master/cli/010-cli-datetime-format.md" rel="noopener noreferrer"&gt;details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;All command and flag names now use hyphen &lt;code&gt;-&lt;/code&gt; as delimiter instead of underscore &lt;code&gt;_&lt;/code&gt;. &lt;a href="https://github.com/temporalio/proposals/blob/master/cli/005-cli-hyphens.md" rel="noopener noreferrer"&gt;details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--color&lt;/code&gt; option to choose when to enable/disable output coloring. Adds JSON coloring by default in TTY. &lt;a href="https://github.com/temporalio/proposals/blob/master/cli/007-cli-color.md" rel="noopener noreferrer"&gt;details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;env&lt;/code&gt; feature to manage CLI environments and change CLI options defaults. &lt;a href="https://docs.temporal.io/cli/env" rel="noopener noreferrer"&gt;details&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Updated the semantics of the commands and flags for consistency.&lt;/li&gt;
&lt;li&gt;All global flags are moved under each command.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Specific changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;namespace&lt;/code&gt;, &lt;code&gt;search-attribute&lt;/code&gt;, and &lt;code&gt;cluster&lt;/code&gt; former top-level commands are now under &lt;code&gt;temporal operator&lt;/code&gt; (these are meant for self-hosted Clusters, not Temporal Cloud).&lt;/li&gt;
&lt;li&gt;Some commands are renamed: &lt;code&gt;namespace register&lt;/code&gt; ➡️ &lt;code&gt;namespace create&lt;/code&gt;, &lt;code&gt;search-attribute add&lt;/code&gt; ➡️ &lt;code&gt;search-attribute create&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Added new command &lt;code&gt;server start-dev&lt;/code&gt;!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;0.8.0&lt;/code&gt; is the current version, and we have just a &lt;a href="https://github.com/temporalio/cli/milestone/1" rel="noopener noreferrer"&gt;few issues left&lt;/a&gt; before the 1.0 release. We are following &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;semantic versioning&lt;/a&gt;, so after the 1.0 release, we won’t change or remove commands or arguments until 2.0.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/temporalio/cli/issues?q=is:open+is:issue+label:enhancement+sort:reactions-+1-desc" rel="noopener noreferrer"&gt;list of feature requests&lt;/a&gt; that we and the community have submitted. We would value any additions you have, and it also helps us prioritize if you give the things you want a 👍 emoji upvote! We also welcome contributions—if you’re interested in improving the CLI, comment on the relevant issue with your plans for (or questions about) a PR. Another improvement we’re discussing is being able to use our &lt;a href="https://github.com/temporalio/tcld" rel="noopener noreferrer"&gt;Cloud CLI&lt;/a&gt; commands in the main CLI so that you can do commands like &lt;code&gt;temporal cloud namespace create&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>temporal</category>
      <category>cli</category>
      <category>workflows</category>
    </item>
    <item>
      <title>Building Reliable Distributed Systems in Node</title>
      <dc:creator>Loren 🤓</dc:creator>
      <pubDate>Tue, 24 Jan 2023 05:47:18 +0000</pubDate>
      <link>https://forem.com/temporalio/building-reliable-distributed-systems-in-node-44i</link>
      <guid>https://forem.com/temporalio/building-reliable-distributed-systems-in-node-44i</guid>
      <description>&lt;p&gt;This post introduces the concept of &lt;em&gt;durable execution&lt;/em&gt;, which is used by Stripe, Netflix, Coinbase, Snap, and many others to solve a wide range of problems in distributed systems. Then it shows how simple it is to write durable code using our TypeScript/JavaScript SDK.&lt;/p&gt;

&lt;h1&gt;
  
  
  Distributed systems
&lt;/h1&gt;

&lt;p&gt;When building a request-response monolith backed by a single database that supports transactions, we don’t have many distributed systems concerns. We can have simple failure modes and easily maintain accurate state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the client can’t reach the server, the client retries.&lt;/li&gt;
&lt;li&gt;If the client reaches the server, but the server can’t reach the database, the server responds with an error, and the client retries.&lt;/li&gt;
&lt;li&gt;If the server reaches the database, but the transaction fails, the server responds with an error, and the client retries.&lt;/li&gt;
&lt;li&gt;If the transaction succeeds but the server goes down before responding to the client, the client retries until the server is back up, and the transaction fails the second time (assuming the transaction has some check–like an idempotency token–to tell whether the update has already been applied), and the server reports to the client that the action has already been performed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As soon as we introduce a second place for state to live, whether that’s a service with its own database or an external API, handling failures and maintaining consistency (accuracy across all data stores) gets significantly more complex. For example, if our server has to charge a credit card and also update the database, we can no longer write simple 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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;paymentAPI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chargeCard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOrder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the first step (charging the card) succeeds, but the second step (adding the order to the database) fails, then the system ends up in an inconsistent state; we charged their card, but there’s no record of it in our database. To try to maintain consistency, we might have the second step retry until we can reach the database. However, it’s also possible that the process running our code will fail, in which case we’ll have no knowledge that the first step took place. To fix this, we need to do three things: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Persist the order details&lt;/li&gt;
&lt;li&gt;Persist which steps of the program we’ve completed&lt;/li&gt;
&lt;li&gt;Run a worker process that checks the database for incomplete orders and continues with the next step&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That, along with persisting retry state and adding timeouts for each step, is a lot of code to write, and it’s easy to miss certain edge cases or failure modes (see &lt;a href="https://temporal.io/blog/workflow-engine-principles" rel="noopener noreferrer"&gt;the full, scalable architecture&lt;/a&gt;). We could build things faster and more reliably if we didn’t have to write and debug all that code. And we don’t have to, because we can use durable execution.&lt;/p&gt;

&lt;h1&gt;
  
  
  Durable execution
&lt;/h1&gt;

&lt;p&gt;Durable execution systems run our code in a way that persists each step the code takes. If the process or container running the code dies, the code automatically continues running in another process with all state intact, including call stack and local variables. &lt;/p&gt;

&lt;p&gt;Durable execution ensures that the code is executed to completion, no matter how reliable the hardware or how long downstream services are offline. Retries and timeouts are performed automatically, and resources are freed up when the code isn’t doing anything (for example while waiting on a &lt;code&gt;sleep(‘1 month’)&lt;/code&gt; statement).&lt;/p&gt;

&lt;p&gt;Durable execution makes it trivial or unnecessary to implement distributed systems patterns like event-driven architecture, task queues, &lt;a href="https://microservices.io/patterns/data/saga.html" rel="noopener noreferrer"&gt;sagas&lt;/a&gt;, &lt;a href="https://microservices.io/patterns/reliability/circuit-breaker.html" rel="noopener noreferrer"&gt;circuit breakers&lt;/a&gt;, and &lt;a href="https://microservices.io/patterns/data/transactional-outbox.html" rel="noopener noreferrer"&gt;transactional outboxes&lt;/a&gt;. It’s programming on a higher level of abstraction, where you don’t have to be concerned about transient failures like server crashes or network issues. It opens up new possibilities like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Storing state in local variables instead of a database, because local variables are automatically stored for us &lt;/li&gt;
&lt;li&gt;Writing code that sleeps for a month, because we don’t need to be concerned about the process that started the sleep still being there next month, or resources being tied up for the duration&lt;/li&gt;
&lt;li&gt;Functions that can run forever, and that we can interact with (send commands to or query data from)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some examples of durable execution systems are Azure Durable Functions, Amazon SWF, Uber Cadence, Infinitic, and &lt;a href="https://temporal.io/" rel="noopener noreferrer"&gt;Temporal&lt;/a&gt; (where I work). At the risk of being less than perfectly objective, I think Temporal is the best of these options 😊.&lt;/p&gt;

&lt;h1&gt;
  
  
  Durable JavaScript
&lt;/h1&gt;

&lt;p&gt;Now that we’ve gone over consistency in distributed systems and what durable execution is, let’s look at a practical example. I built this food delivery app to show what durable code looks like and what problems it solves:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://temporal.menu/" rel="noopener noreferrer"&gt;temporal.menu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/23viK26Hvfx8jYCam3kC9r/36321dc6b375fe829a8c3e701b85bb9f/menu.jpg" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/23viK26Hvfx8jYCam3kC9r/36321dc6b375fe829a8c3e701b85bb9f/menu.jpg" alt="Durable Delivery app menu"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Don’t blame me for the logo—that’s just what Stable Diffusion gives you when you ask it for a durable delivery app logo. 🤷‍♂️😄&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The app has four main pieces of functionality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an order and charge the customer&lt;/li&gt;
&lt;li&gt;Get order status&lt;/li&gt;
&lt;li&gt;Mark an order picked up&lt;/li&gt;
&lt;li&gt;Mark an order delivered&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/5t9ArgfJP8K0UGFpOAzidi/5b37dc9c647370714bd5b53b692615d0/delivery-demo.gif" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/5t9ArgfJP8K0UGFpOAzidi/5b37dc9c647370714bd5b53b692615d0/delivery-demo.gif" alt="The order process, showing both the menu and driver sites"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we order an item from the menu, it appears in the delivery driver site (&lt;a href="https://drive.temporal.menu" rel="noopener noreferrer"&gt;drive.temporal.menu&lt;/a&gt;), and the driver can mark the order as picked up, and then as delivered.&lt;/p&gt;

&lt;p&gt;All of this functionality can be implemented in a single function of durable JavaScript or TypeScript. We’ll be using the latter—I recommend TypeScript and our library is named the TypeScript SDK, but it’s published to npm as JavaScript and can be used in any Node.js project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an order
&lt;/h2&gt;

&lt;p&gt;Let’s take a look at the code for this app. We’ll see a few API routes but mostly go over each piece of the single durable function named &lt;code&gt;order&lt;/code&gt;. If you’d like to run the app or view the code on your machine, this will download and set up the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @temporalio/create@latest &lt;span class="nt"&gt;--sample&lt;/span&gt; food-delivery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the user clicks the order button, the React frontend calls the &lt;code&gt;createOrder&lt;/code&gt; mutation defined by the tRPC backend. The &lt;code&gt;createOrder&lt;/code&gt; API route handler creates the order by starting a durable &lt;code&gt;order&lt;/code&gt; function. Durable functions—called &lt;em&gt;Workflows&lt;/em&gt;—are started using a Client instance from &lt;code&gt;@temporalio/client&lt;/code&gt;, which has been added to the tRPC context under &lt;code&gt;ctx.temporal&lt;/code&gt;. The route handler receives a validated &lt;code&gt;input&lt;/code&gt; (an object with a &lt;code&gt;productId&lt;/code&gt; number and &lt;code&gt;orderId&lt;/code&gt; string) and it calls &lt;code&gt;ctx.temporal.workflow.start&lt;/code&gt; to start an &lt;code&gt;order&lt;/code&gt; Workflow, providing &lt;code&gt;input.productId&lt;/code&gt; as an argument:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/main/food-delivery/apps/menu/pages/api/%5Btrpc%5D.ts" rel="noopener noreferrer"&gt;apps/menu/pages/api/[trpc].ts&lt;/a&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;initTRPC&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;@trpc/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&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;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;taskQueue&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;common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Context&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;common/trpc-context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;order&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;workflows&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;initTRPC&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appRouter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;router&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;procedure&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;workflowId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="nx"&gt;taskQueue&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;Order received and persisted!&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;The &lt;code&gt;order&lt;/code&gt; function starts out validating the input, setting up the initial state, and charging the customer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/main/food-delivery/packages/workflows/order.ts" rel="noopener noreferrer"&gt;packages/workflows/order.ts&lt;/a&gt;&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;type&lt;/span&gt; &lt;span class="nx"&gt;OrderState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Charging card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Picked up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Refunding&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getProductById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ApplicationFailure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Product &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; not found`&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="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrderState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Charging card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;deliveredAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;chargeCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Failed to charge customer for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&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;. Error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;errorMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ApplicationFailure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any functions that might fail are automatically retried. In this case, &lt;code&gt;chargeCustomer&lt;/code&gt; and &lt;code&gt;sendPushNotification&lt;/code&gt; both talk to services that might be down at the moment or might return transient error messages like “Temporarily unavailable.” Temporal will automatically retry running these functions (by default indefinitely with exponential backoff, but that’s configurable). The functions can also throw non-retryable errors like “Card declined,” in which case they won’t be retried. Instead, the error will be thrown out of &lt;code&gt;chargeCustomer(product)&lt;/code&gt; and caught by the catch block; the customer receives a notification that their payment method failed, and we throw an &lt;code&gt;ApplicationFailure&lt;/code&gt; to fail the &lt;code&gt;order&lt;/code&gt; Workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get order status
&lt;/h2&gt;

&lt;p&gt;The next bit of code requires some background: Normal functions can’t run for a long time, because they’ll take up resources while they’re waiting for things to happen, and at some point they’ll die when we deploy new code and the old containers get shut down. Durable functions can run for an arbitrary length of time for two reasons: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They don’t take up resources when they’re waiting on something.&lt;/li&gt;
&lt;li&gt;It doesn’t matter if the process running them gets shut down, because execution will seamlessly be continued by another process. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So although some durable functions run for a short period of time—like a successful money transfer function—some run longer—like our order function, which ends when the order is delivered, and a customer function that lasts for the lifetime of the customer.&lt;/p&gt;

&lt;p&gt;It’s useful to be able to interact with long-running functions, so Temporal provides what we call &lt;em&gt;Signals&lt;/em&gt; for sending data into the function and &lt;em&gt;Queries&lt;/em&gt; for getting data out of the function. The driver site shows the status of each order by sending Queries to the order functions through this API route:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/cb617abb3e0f58a6911c66615f9c2c665e0307b0/food-delivery/apps/menu/pages/api/%5Btrpc%5D.ts#L23-L25" rel="noopener noreferrer"&gt;apps/menu/pages/api/[trpc].ts&lt;/a&gt;&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="nx"&gt;getOrderStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;procedure&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getStatusQuery&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It gets a handle to the specific instance of the order function (called a &lt;em&gt;Workflow Execution&lt;/em&gt;), sends the &lt;code&gt;getStatusQuery&lt;/code&gt;, and returns the result. The &lt;code&gt;getStatusQuery&lt;/code&gt; is defined in the order file and handled in the order function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/cb617abb3e0f58a6911c66615f9c2c665e0307b0/food-delivery/packages/workflows/order.ts#L55-L57" rel="noopener noreferrer"&gt;packages/workflows/order.ts&lt;/a&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHandler&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;@temporalio/workflow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getStatusQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;defineQuery&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;OrderStatus&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;getStatus&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;OrderState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Charging card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;deliveredAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;

  &lt;span class="c1"&gt;// …&lt;/span&gt;

  &lt;span class="nf"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getStatusQuery&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;deliveredAt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;productId&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;When the order function receives the &lt;code&gt;getStatusQuery&lt;/code&gt;, the function passed to &lt;code&gt;setHandler&lt;/code&gt; is called, which returns the values of local variables. After the call to &lt;code&gt;chargeCustomer&lt;/code&gt; succeeds, the state is changed to &lt;code&gt;’Paid’&lt;/code&gt;, and the driver site, which has been polling &lt;code&gt;getStatusQuery&lt;/code&gt;, gets the updated state. It displays the “Pick up” button. &lt;/p&gt;

&lt;h2&gt;
  
  
  Picking up an order
&lt;/h2&gt;

&lt;p&gt;When the driver taps the button to mark the order as picked up, the site sends a &lt;code&gt;pickUp&lt;/code&gt; mutation to the API server, which sends a &lt;code&gt;pickedUpSignal&lt;/code&gt; to the order function:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/main/food-delivery/apps/driver/pages/api/%5Btrpc%5D.ts" rel="noopener noreferrer"&gt;apps/driver/pages/api/[trpc].ts&lt;/a&gt;&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="nx"&gt;pickUp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;procedure&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
      &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;temporal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workflow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHandle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;orderId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pickedUpSignal&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 order function handles the Signal by updating the state:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/cb617abb3e0f58a6911c66615f9c2c665e0307b0/food-delivery/packages/workflows/order.ts#L42-L46" rel="noopener noreferrer"&gt;packages/workflows/order.ts&lt;/a&gt;&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pickedUpSignal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pickedUp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// …&lt;/span&gt;

  &lt;span class="nf"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pickedUpSignal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;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="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Picked up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meanwhile, further down in the function, after the customer was charged, the function has been waiting for the pickup to happen: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/cb617abb3e0f58a6911c66615f9c2c665e0307b0/food-delivery/packages/workflows/order.ts#L70" rel="noopener noreferrer"&gt;packages/workflows/order.ts&lt;/a&gt;&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;condition&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;@temporalio/workflow&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// …&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;chargeCustomer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&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;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Paid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notPickedUpInTime&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Picked up&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="s1"&gt;1 min&lt;/span&gt;&lt;span class="dl"&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;notPickedUpInTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Refunding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;refundAndNotify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⚠️ No drivers were available to pick up your order. Your payment has been refunded.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ApplicationFailure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not picked up in time&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;await condition(() =&amp;gt; state === 'Picked up', '1 min')&lt;/code&gt; waits for up to 1 minute for the state to change to &lt;code&gt;Picked up&lt;/code&gt;. If a minute goes by without it changing, it returns false, and we refund the customer. (Either we have very high standards for the speed of our chefs and delivery drivers, or we want the users of a demo app to be able to see all the failure modes 😄.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Delivery
&lt;/h2&gt;

&lt;p&gt;Similarly, there’s a &lt;code&gt;deliveredSignal&lt;/code&gt; sent by the “Deliver” button, and if the driver doesn’t complete delivery within a minute of pickup, the customer is refunded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/temporalio/samples-typescript/blob/cb617abb3e0f58a6911c66615f9c2c665e0307b0/food-delivery/packages/workflows/order.ts#L82-L89" rel="noopener noreferrer"&gt;packages/workflows/order.ts&lt;/a&gt;&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deliveredSignal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defineSignal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productId&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deliveredSignal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Picked up&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="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Delivered&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="nx"&gt;deliveredAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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="c1"&gt;// …&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🚗 Order picked up&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;notDeliveredInTime&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Delivered&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="s1"&gt;1 min&lt;/span&gt;&lt;span class="dl"&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;notDeliveredInTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Refunding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;refundAndNotify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⚠️ Your driver was unable to deliver your order. Your payment has been refunded.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;ApplicationFailure&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not delivered in time&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;await&lt;/span&gt; &lt;span class="nf"&gt;sendPushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Order delivered!&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;If delivery was successful, the function waits for a minute for the customer to eat their meal and asks them to rate their experience.&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1 min&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// this could also be hours or even months&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendPushNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✍️ Rate your meal. How was the &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;product&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;toLowerCase&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;After the final push notification, the order function’s execution ends, and the Workflow Execution completes successfully. Even though the function has completed, we can still send Queries, since Temporal has the final state of the function saved. And we can test that by refreshing the page a minute after an order has been delivered: the &lt;code&gt;getStatusQuery&lt;/code&gt; still works and “Delivered” is shown as the status:&lt;/p&gt;

&lt;p&gt;&lt;a href="//images.ctfassets.net/0uuz8ydxyd9p/2IAEpuDtPpp8NNCjeswM2h/b783df7d5c1554a50aab7a4b427b89f3/delivered.png" class="article-body-image-wrapper"&gt;&lt;img src="//images.ctfassets.net/0uuz8ydxyd9p/2IAEpuDtPpp8NNCjeswM2h/b783df7d5c1554a50aab7a4b427b89f3/delivered.png" alt="Poke order with Status: Delivered"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;We’ve seen how a multi-step order flow can be implemented with a single durable function. The function is guaranteed to complete in the presence of failures, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Temporary issues with the network, data stores, or downstream services&lt;/li&gt;
&lt;li&gt;The process running the function failing&lt;/li&gt;
&lt;li&gt;The underlying Temporal services or database going down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This addressed a number of distributed systems concerns for us, and meant that: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We could use local variables instead of saving state to a database.&lt;/li&gt;
&lt;li&gt;We didn’t need to set timers in a database for application logic like canceling an order that takes too long or for the built-in functionality of retrying and timing out transient functions like &lt;code&gt;chargeCustomer&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We didn’t need to set up a job queue that workers polled, either for progressing to the next step or picking up unfinished tasks that were dropped by failed processes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;a href="https://dev.to/temporalio/how-durable-execution-works-2b88"&gt;next post&lt;/a&gt;, we look at more of the delivery app’s code and learn how Temporal is able to provide us with durable execution. &lt;/p&gt;

&lt;p&gt;If you have any questions, I would be happy to help! Temporal’s mission is helping developers, and I also personally find joy in it 🤗. I’m &lt;a href="https://twitter.com/lorendsr" rel="noopener noreferrer"&gt;@lorendsr&lt;/a&gt; on Twitter, I answer (and upvote 😄) any StackOverflow questions tagged with &lt;a href="https://stackoverflow.com/questions/ask?tags=temporal-typescript" rel="noopener noreferrer"&gt;&lt;code&gt;temporal-typescript&lt;/code&gt;&lt;/a&gt;, and am &lt;a class="mentioned-user" href="https://dev.to/loren"&gt;@loren&lt;/a&gt; on the &lt;a href="https://t.mp/slack" rel="noopener noreferrer"&gt;community Slack&lt;/a&gt; 💃.&lt;/p&gt;

&lt;h1&gt;
  
  
  Learn more
&lt;/h1&gt;

&lt;p&gt;To learn more, I recommend these resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Video: &lt;a href="https://youtu.be/6lSuDRRFgyY" rel="noopener noreferrer"&gt;Intro to Temporal and using the TypeScript SDK&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Some &lt;a href="https://temporal.io/use-cases" rel="noopener noreferrer"&gt;common use cases&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TypeScript SDK docs: &lt;a href="https://t.mp/ts" rel="noopener noreferrer"&gt;t.mp/ts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TypeScript API reference: &lt;a href="https://t.mp/ts-api" rel="noopener noreferrer"&gt;t.mp/ts-api&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.temporal.io/tutorials/typescript/" rel="noopener noreferrer"&gt;TypeScript tutorials&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More blog posts about our TypeScript SDK:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://temporal.io/blog/using-temporal-as-a-node-task-queue" rel="noopener noreferrer"&gt;Using Temporal as a Node.js Task Queue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://temporal.io/blog/caching-api-requests-with-long-lived-workflows" rel="noopener noreferrer"&gt;Caching API Requests with Long-Lived Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://temporal.io/blog/temporal-rest" rel="noopener noreferrer"&gt;Express middleware that creates a REST API for your Workflows&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://temporal.io/blog/typescript-1-0-0" rel="noopener noreferrer"&gt;1.0.0 release of the TS SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://temporal.io/blog/intro-to-isolated-vm" rel="noopener noreferrer"&gt;How we use V8 isolates to enforce Workflow determinism&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks to Jessica West, Brian Hogan, Amelia Mango, and Jim Walker for reading drafts of this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
