Connect everything and anything in real time

Learn With Jason S8.E16 Sep 12, 2025

Complex orchestration across different services, APIs, and LLMs is, in a word, hard. Especially when you need user interaction along the way. Inngest is out to make it manageable. CTO Dan Farrelly will teach us.

Read the transcript

Captions provided by White Coat Captioning (https://whitecoatcaptioning.com/). Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.

JASON: Hello, friends. We are back. It has been a long time since we have done one of these live streams. I walked into my studio to realize I had unplugged everything. We are running a little behind schedule. That's my fault. Sorry about that. Here we are. We are doing it. This is going to be an exciting one because this is the first time we are trying a new format. We have been learning Learn With Jason for a little over 400 episodes. It occurred to me we have been doing this in a way that's two different shows for two different audiences. The first part of the show, we are talking about new tech, and it is short, and basically a podcast. It is good for people trying to sample what is going on, figure out, keep up, but don't have the time or the interest in going deep. And then the second half of the show is us going really deep. We will do the 0-1, pair program, and sharing the screen. What I am going to do is split this up into two shows. This is our first effort at it. We will be releasing the first half of the show as a video and audio podcast title TBD. You will find out we release it. The second half of the show will be the traditional Learn With Jason but we will jump right into code in that show. From the live standpoint, it will stay the same. We will have the discussion up front, the pair programming at the end, so if you watch live not a lot changes. If you are watching later, the format will switch up a little and hopefully be more relevant to your interest. Watch the discussion, or the pair programming, or both. Let's jump into this new format. I am super excited today because we are going to be talking about a piece of tech that I have been having so much fun building with. It is a problem people have and figuring out how to solve it has been a whole department like a DevOps team or an infra team and heavy back end things to figure out how to orchestrate different things together. At a high level, here is what I want to talk about and why I am so excited about having our guest on. When you start plucking services together, you are going to be making API calls, sending messages back and forth, maybe doing real-time, you need to be confident when you send that message it is going to get where it needs to go, and when you start streaming services together, someone submits a form that needs to go to a database and also update the Slack channel maybe and also send off a request to upload assets from the form to your S3 buckets or wherever you keep files, maybe you are coordinating with CMS to update a file. All these happen in sequence so what happens if there is a blip in the network and your database fails? How are you going to try and manage that? How do you make your sure you don't save a double entry? This stuff is hard and complicated if you don't have the right tools. I am going to bring on my guest today who has been thinking about this space for a while now and is going to talk about how we think about this, how we can solve it, and what he is working on to solve it. Welcome to the stage Dan Farrelly. How are you doing?

DAN: Pretty good.

JASON: I feel like you have been building stuff that has been scratching an itch that I have had for a long time which is I like to plug lots of things together, I love making nice complicated workflows, but the pain of doing that, when it goes wrong, like the happy path is always great. I do this and that and this thing happens. You feel like a genus. Then you get a network blip, someone on a slow network connection, something goes wrong and the whole thing falls over and you are on manual debugging duty. I guess, maybe first and foremost, let's talk about who you are and sort of your path to where you are today.

DAN: You teed up a lot of great stuff. We are definitely vibing on where to Inngest this. For me, as context, before I started in Jazz, my co-founder Tony I worked as a CTO at a company called Buffer which is a sass app for marketing teams to schedule, analyze, and respond to like social media posts. So I basically started as a Full-Stack engineer there, took on more responsibilities, ended up digging in from the lowest level of the infrastructure all of the way to the front end thinking about systemizing all the bits, and eventually became CTO there. That was kind of my mission at that point -- to kind of already back in 2016-2017 thinking about -- we were a small team with a small infrastructure friendly group. We were thinking about platform engineering back then. And you know, I think before maybe it had a term, not sure, really how do we enable our product engineers to do the best work. We had like a ton of great product engineers and I always wanted to figure out how we make them do their best things. We were a product first company, obsessed with the user, always caring about the user experience, so I brought that into down the stack, how can we spend a disproportionate amount of time focusing on what we were building. It is always about enablement. That lined me up for what I am thinking in my head and what the next stages were of this problem. Then I happened to reconnect with Tony, my co-founder, after a few years, and we had similar thoughts in the space. That's a little bit about me. We started back in 2021 and that's when we started digging in.

JASON: Inngest is an extension of things you were solving with you were at Buffer?

DAN: Totally. Tony was building a healthcare startup and I was building a sass startup and we had both seen similar flaws in different problems and use-cases. It definitely is an extension of where are things going. During the almost decade I spent at Buffer we saw a big range of changes. The 2010s a lot of changes happened. Backbone and coding Nider was the original build and the infrastructure was pretty rudimentary originally. Then we saw Kubernetes which brought complexity to the infrastructure side of thin but also offered the ability to layer on custom abstractions on the infrastructure. It is just kind of a continuation of what was happening there. I think, Tony on his side, he was working at a healthcare startup and building complex flows that similar to what you said in the intro, hand roll a lot of these distinct cases that were required for, say, HIPPA compliance. They had to trigger an event, have an audit trail for that, they always needed to be able to understand what happened in the system for HIPPA compliance. Also being safe for PII triggers a delivery to happen but if someone didn't happen after X number of days, if say the patient didn't -- say approval and say I received this package, they needed to do something about it. So there was always a trail and a thing of actions that needed to happen and follow through. For his use case, he had seen that everything prior to this was building everything from scratch. Bespoke and I will store in the database and have a chron job that checks. You are building the same thing again but there should be an abstraction above that that allows you to define the business rules and logic and not have to worry about all of the pieces. More of a general purpose. We came at it from two different angles but aligned on this needs to exist and there needs to be something better.

JASON: Yeah, so the way as you said, people hand roll these things, and so I think the first step that somebody would take would be as you said, you have a database, maybe you are setting up a cron job, as you get more services involved, start getting into re-try and third party services, it starts to get a lot more complicated. Traditionally, how have companies solved this when rolling it in? What are some of the tools you will see in the stack?

DAN: I think it is also sometimes useful to think about where Inngest fits in the stack to explain how I think about it and then what would have been there. If I look at the stack modern application if you are building, web app, API and database previously and maybe a cron app for something simple. When I see this now there is a fourth component. And I see that it is Inngest. It is an execution that allows things that aren't able from a stable database. What does that mean? The stack that would exist below this is a web of depending on what you need, a layer ques, e your own version of whatever you want here spinning up that infrastructure whether you are running a queue on a machine or something that is like a cloud provider solution. Then you are spinning up workers. And then you are defining the logic on the workers. How you pull from messages and how you handle retries and from there you have a complex flow. Like the one I described with Tony's previous company that he led was you have a queue starting the first job, does work, and then you need to define database schema to store the state of where that job is. Then you have to define something that updates later or has a cron job and another working and say I will ping this and clean it up and do something else depending if this works. Now you have two queues, a cron job, two workers, state in the database, and this is how people traditionally like build these systems. And, you know, there is layers and layers deep to the problem, but generally like you start there, and then the complexity just snowballs. You spend half your time trying to design the system which can be fun for some people but doesn't get you closer to something that is usable.

JASON: Right.

DAN: I think you can get lost in three months.

JASON: I think it sort of points to something I talk about a lot which is the idea of when you are work at a company, you don't necessarily want to be engaging in hobbies. You want to be building things that are actively valuable to the customer you are trying to serve. It is really fun to go on an internal tools or infrastructure rabbit hole and just see what you can build and see whether you can get something running. But generally speaking, that turns into a project that's pulling you away from shipping the things that a customer would pay for. They will pay for stability but asked for XYZ feature support. If you spend six months building infrastructure stability you are not shipping on features and that can cost you in the long run. And you know, the other thing that I think about with this is that in addition to just the overhead of having to spin up these other services, it tends to especially for say like a front end heavy team that's maybe building in a TypeScript stack or you know, more of like a Full-Stack JavaScript-y house, when you start getting into these back end services, you either have to hire a specialist or you find yourself needing to learn like rabbit MQ or cough cu. You have to have a whole different deployment pipeline and different ways to manage the servers, and all these not necessarily complicated, but chores. Thing you have to learn, figure out how this system runs, how to make it resilient and how to make sure it won't fall over when you end up under load or when you restart you won't dump the active jobs that are incomplete, all these interesting problems that happen that if you have never done it before it is daunting. It is really daunting. And so, I know that this is where you and Tony started and Inngest came from, so maybe let's talk a little bit about how are you approaching this space from the technology side?

DAN: Yeah, and I guess from this originally what we wanted to start with was -- you made a point about the front end or Full-Stack heavy teams which has been happening in various teams for years but this Full-Stack product engineer good on the front end, solid on the back end, maybe on the system side is a little bit like is where you might be dipping your toes. I think when it comes to that, we saw this group and said you know what? Let's see where they are. Some are deploying to serverless. Pick the serverless front end like Vercelli and how are you deploying anything in the background that's running durably? We approached it saying what's the least common denominator and let's make something that will work on serverless because it will work on other things. You have to run a worker usually, maybe running in a container, scaling them horizontally, fleet of containers, maybe dipping in auto scaling, maybe you aren't getting that far, and you are having to manage all the stuff happening in the background that you have no visibility on. That's one thing that's easy with the API, you ping it, request, response, see feedback and log a failure. With the background it is a little more how do you get feedback to the user, figure out what happened later, re-try and what not. We started when looking at serverless and said let's make something serverless friendly and we defined our original approach and this is still our main approach right now is that's you can define in your API server Inngest functions that are running. You define the event, any flow control which we can talk about, and then just a handler. Maybe you break that function into multiple steps, turning it into a more complex workflow, but you should take that, ship it on the API and tell us where the server is and Inngest is the engine that handles everything from there. I will pause.

JASON: Yeah, I think what's really interesting about this, and the thing I think is important to specifically call out about a service like Inngest is when you are a front-end developer, you start putting together these workflows, and the first time that you do it, when you are early in your career, you will create a function and you will do everything inside of that one function, and then you start to learn how to, you know, break up each action into its own function so that you can separate the code and make it a little more reusable. Then you start to realize certain functions could actually be put in the background and think about maybe I can offload this to different places. Your application is more complicated, talking to databases, third party services, asset managers, user systems, and notification systems, and maybe you have a logging system, and each of those things now is living inside of these little functions but those functions then are now still kind of on your system so it is hard to background and they become blocking and you have a long running adding call and adding 150 milliseconds for no reason the user cares about so you look to off load them and that is where you run into problems. In order to solve this, I need to go get a queue or back end supplies -- system. When I looked at Inngest I am like I already broke my functions into composable pieces so if I put them inside Inngest steps I am done and it just sort of work. What I think is so powerful about this is yes, you have to start thinking about things as atomic steps, and I am more or less like you are building a state machine. I saw David is in the chat. It is like same input same output. If that's the case, you can start to orchestrate these things but then putting them into Inngest isn't like I am learning how to do a Yaml function or container function. I am just organizing them with a little more here is where it starts and here is where it ends and where get all this magic. That is when I really got excited. I realized just through my own coding best practices the things that other engineers have been encouraging me to do, the things that my peers are saying, hey, write cleaner code, here is tips for doing that, I was like, oh, pretty much already there. I don't have to learn a whole new system. I don't have to learn a new language or even a deployment system. I have to add a service end point and I was up running with this whole organization. I am like am I an ops engineer?

DAN: That's the goal. You have those functions broken out. You are already in your codebase. Instead of defining a worker and service and building Docker files and in that realm is you just drop it into your existing codebase. And the less that you need to think about the better. And that was when, you know, we had -- we were a team of three at that point and built the first like what is this SDK look like? That's when we came up with the step. Run primitive and we were like all right, you know, you could make higher level abstraction that adds more complexity or over define it is get into yaml and custom DSL. But what's simple? Steps in a workflow, wrap it in step. Run, it runs and encapsulates the logic that runs, and that's the lower level building block. That's all you really need to know. What's great about that is typically you will push something to background but as you run these complex things you mentioned at the top about more often than not we are building complex applications. Rather than a decade ago which maybe had less integrations, crud apps that might be fully contained, everything is intertwined and everything is hidden LLM at some point. You are hitting all these different stages. And something can fail. So this step primitive is basically wrapper to say this is a side effect or something that is running. Execute this. When it is done, never execute it again. Talked about potency earlier. You alluded to it. And then run the next thing. If that third thing fails, you don't want to rerun that LLM call in the beginning if it was fine because you are wasting money and might be doing things twice.

JASON: And you are wasting time.

DAN: Right. Wasting time and resources. That step primitive we have is the building blocks of building these durable functions or durable workflows. I think that is I can import my function here and wrap it Florida -- in a step and get that. We expanded it in different ways with different primitives. That's the foundation of what you are talking about.

JASON: Here is the piece where the light bulb went on for me. I am already writing a utility function that is like save to database, I wrapped up the logic for how I save in that function, and then I return back the new user object or record. I will take that and say send a notification to Discord that thing happened. I was already breaking this up into atomic steps where I expect one input, one output. All I had to do to adopt Inngest was say step.run and put that function inside of that step. And then when my database call succeeds but the call to Discord fails, I don't have to do a triarch. I don't have to write logic for exponential back off. I don't have to double check the database record is there before I move on to second step. I just know Inngest has already done that for me. And I am able to say do this workflow. It is like I already did this but step two is not done, let me take the output from step one, drop it into step two, it succeeded and your workflow is done. I get a dashboard that says your thing is done and that's the other thing that I think is really cool. One of the things that is hard about debugging these complex workflows is you have all these steps and when something goes wrong it isn't always immediately clear where it went wrong. This something I am like I am so glad I don't have to roll anything myself. You just have a nice little UI where I can see here is the run happening, each step, this is the step that failed, here is the error message coming out, here is the input that went into that step, now I can go in and debug that one function that failed as opposed to trying to figure out like, OK, let me console log here and here and here is where it is wrong. It is these little things that make you more effective that would be really hard if you are repeating that database call every time you it debug that's a potentially destructive thing to be doing if you are trying to debug production data and you have to pull a copy over to the development database and run it safely without like polluting to production data. It takes a ton of time whereas in Inngest you can look at it, see it succeeded and it is the next step that failed because I don't know the return value changed shape from the database and it is causing our second step to fail. We can fix that. No problem. It just sort of made my life easier as somebody trying to orchestrate these things because you get super high visibility at a granular level and a step-by-step level. Is that the sort of thing when you were building it -- was that a thing one? Or I guess the way we did this made that easy?

DAN: No, you know, I mean, we had a lot planned early on. We kind of looked at the -- we took a step back and built these systems, and the model is easier right, can admit to the system. We have the way it fails and we log it. You get this observability for free when you run with Inngest. If you rewind to where people might have been prior to Inngest using something like Inngest maybe you are DAUCHL dumping logs, and parsing them, and giving everything a job ID. You are good at structuring logs but you are parsing them or manually tracking them in a database which is like you are building your own system or maybe the ability tool. Like with Datadog which means you still need to figure -- you should just be able to get it right out of the system. I think the key thing you mentioned in that case is when you debug something and see failure. It could be an isolated case, which is great. We can fix something over here, in the Inngest dashboard, because we know the code and where it is running and what happened, we can rerun, you can just click it in the UI. This is not a problem, click rerun, rerun, everything is fixed and good. What also happens is you are deploying a bug, or if there is something that changed in a library, and let's say 10,000 jobs fail in a short period of time depending on your scale. Maybe it is a 1,000. Typically you might have to work with dead letter queues, or you might have to say let me just scrape all my logs and replay something, or communicate with my users, I can't fix this. It is a terrible situation to be when you are like recovering from an incident. But with Inngest, because we know the job status for each thing, and we save the inputs for each, one of the things we have built in is recovery tools. You can say between these times I want to replay all these functions that fail. I fixed the bug, tested it out in production, it is there. Always ways where you can copy payloads and send them to your local environment and run the function and step through everything locally on your machine to debug like you said. You know, these types of things, like to be able to build these recovery tools, whether you are bulk canceling, pausing a function while you fix and unpausing and letting everything rip, or replaying everything in bulk, you need -- like the actual system itself, we believe needs to be aware of this and have the data. If you off load to a third party visibility tool like Datadog, you are in the bulk where you need the data out of Datadog, put it in the system, and figure out how to replay. I think that's kind of a key thing we saw that when you are building systems like this things happen, you know you will need it, we knew you needed the data so you could fix it and have self-recovery tools. It flows in the same thing that allows devs to get thing done.

JASON: We are just about at time so to put a bow on this, the thing I love about this approach to managing the complexity of modern apps, is that, you know, we, as the people building the apps, want to be focusing on the specific bits of logic; right? And maybe we need to do some step-based stuff where we are going through and updating a lot of different systems and services and we don't want to have to think about retries, and you know, duplicate data, or all of the complexity that happens there. Maybe we are doing something where we need to wait for user input and need to be able to pause a workflow and wait for an event to happen so we can continue once we get a conformation or a user completes a task. Maybe we need to do something real-time. Doesn't matter what it is, the way you built the system and the way Inngest is setup to work is I get to write the flow I want to write as a linear thing and I say do step one, step two, wait for event, do step three, send this real-time event back to the browser and wait for that event so I can do step five. It keeps going. And from my standpoint, I am just writing TypeScript; right? And that, to me, is such a powerful way to level up any complex workflow and to remove all of these things we were talking about, managing queues, infrastructure, and it becomes something we don't have to deal with any more. And to me, especially I use Inngest on TV. There is no way I can build this back into the system. If you told me I need to setup a coffee queue, I would add stuff that says stuff breaks. I am not setting up rabbit MQ. I am sorry. So I think that to me gets me really excited about what's possible with this stuff. And so, you know, as we get to the end of the time, Dan, anything you want to end with before we switch over?

DAN: I think as an engineer myself; my team is all engineers. I am sure the audience is all product engineers in similar places. It is hard to build applications these days. Everything is everything is getting way more complex. Everything has the it demand of bring AI into the product. We didn't set out to build this for AI. But the demand to move faster is real. The demand to ship something really cool and you can build so much more and faster now at the code level but what about the infrastructure? Clod code doesn't help with that at least yet. When it comes to this like we believe that we want to enable developers to do more and ship better products. With AI it is hard to have a remote when you build products so how do you iterate faster and build great products for your users? That's what they care about. And how can you do it without less headaches and caught in the weeds with things that aren't moving the business forward and may cost you more to maintain long term. What it comes to this, I think I see Inngest as the companion in that stack, you have your API, database, web app, and Inngest and you can build anything. So, you know, this is where I think we are at. You know, the system that we built is generic too. You run it on any different cloud from Vercell to a server and any language. TypeScript, Python, Go, Java as well. So it is generic. It can work on anything. We have a lot of polyglot teams working together with multiple languages all within the Inngest system. It is making things a lot easier for folks.

JASON: Go check out inngest.com if you want to learn more. Thank for spending time with us today. We are going do the pair programming part. We are splitting into two formats which is why I am ending things for a second. Stick with us for a second.

DAN: If you enjoy this podcast, go ahead and subscribe. You can listen on Spotify, YouTube, wherever you get your podcasts. Make sure you tune in for more episodes like this. Head to the code TV.dev website and search for Learn With Jason and the Dan and Inngest episode will be up soon.

JASON: Hello, everyone, and welcome to another episode of Learn With Jason. We are going to be learning how to get setup and running with Inngest and to teach us is CTO and co-founder Dan fairly. Thank you so much were joining us.

JASON: Thanks for having me, Jason.

JASON: Let's talk quickly about what Inngest is and then I want to get up and running. Can you give us there two second overview?

DAN: Basically it is an ex cushion engine n that allows you to auto scale queues, monitor runs happening in the backgrounds and replay errors without touching any infrastructure. That's basically it. We allow you to run asynchronous workflows in the background without thinking about getting caught in the weeds of infrastructure. Drop our SDK into any application stack that you have. That's as brief as I can make it.

JASON: That's perfect. Let me close this one out. I am going to open up a new window over here. If I want to get started, what's the first thing that I should do? Let me get one over here and one over here.

DAN: Yeah, I think the first thing, let's figure out like -- it all depends on the stack. If you are TypeScript -- let's assume TypeScript for the show.

JASON: It will be and typically speaking I will jump win Astro my default if that's OK.

DAN: That's great. All Inngest requires is some sort of server you have running. Astro has APIs or actions you can use. If we start with a first Astro project we can start there or an existing one if you have something.

JASON: Let's just make a new one. Create astro and we will call this Inngest Astro. We will just get a nice blank project going so that will give us full capabilities here. And

DAN: And you would go to the docs. We have samples on GitHub that you can pursue to make sure you set tup right. You have the server running and the first thing is to install our SDK.

JASON: Let's prove we got it. There is our site. I am going to stop that server now. We want to pmpm install Inngest with two Ns. Any other packages we will need?

DAN: Maybe we will get to real-time so we can do a space and hit @ Inngest space real-time. That should be it. We will also separately run -- we can use mpx or the Inngest dev server but we will get to that in a minute. But yeah, let's start here and just kind of like dive into the code. Now that we have the SDK.

JASON: Got the SDK and our site. I guess we don't need the site running yet. Let me open this. I will close this and do this one. Let's maximize. We have got our baseline Astro site. Nothing in it. You can see we have nothing going on. In order to create our file, what files do we need to get Inngest running?

DAN: There is three components that you will be creating when you start installing Inngest. You will create a client which is basically just a wrapper for you to create functions and send events which we will get to. Usually what I recommend is someone creates an Inngest directory or call it a workflows directory but we can go with an Inngest directory. And then I would just a client.ts file. Really to create a client, you just import Inngest. We are getting some auto complete here. That is the right code right there. Basically all this does is you define an app. Every Inngest system can have different apps. So basically that is like I have a server or a group of servers running over here doing similar work. You may have 20 different ones of these depending on the complexity in the system. Since we have one server and one application this is great. You can specify arguments and different things here also. But this is all you really need to get started in local development.

JASON: OK. Great.

DAN: Now we have this. We want to create our first function. We can just create -- let's think of a good function. What should we do?

JASON: Let's do this. I want to have somebody submit a form and then I want to show them a couple options. I want them to choose one and then we will continue forward. I feel like that's a workflow that I have been working on, a simplified version, where I am building like a calendar clunk, and I have got a lot of that functioning, but let's do the simple case which is we will have someone put in their name, ask them for their favorite color, and then why -- we are going to continue the workflow. That will give us a chance to do steps and real-time.

DAN: Let's create function and call it new booking function or handle booking.

JASON: We will call it handle booking. I like that.

DAN: Then we will import the client from the other file.

JASON: OK. So we will import there.

DAN: That code is not completely correct. We have an LLM.text if anyone wants to use that. You can find on the home page of the docs. Inngest.createfunction which takes three arguments. There is an object, object, and a callback. The first object, and let's fill those objects, and yeah. That autocomplete is correct. The first object is basically like what are we defining the function. So define a function really all you need to do is specify an ID. Think about it like this is a slug so we will call it handle booking. It defines it uniquely in your system so when you are invoking this we know exactly what it is. If you change the ID, it can break things, so that's why it is called that. The second argument is the trigger. In our case we will use an event trigger. And let's say, I think booking created is a good event.

JASON: Makes sense to me.

DAN: What we also recommend when you think about events in systems sometimes there is a nice pattern where we think about object action. You are thinking about a noun, and then a verb, usually past tense because someone created it or initiated it or did something. This is responding to that input. So just like you would use events in the client side of an application, right? Button clicked or something like that. In this, this is all you need to do for the function right here. Let's return something. Let's just return, you know, some sort of success or -- yeah, let's say let's return an object and with a message that just says booking created. We will just leave it at that. This is basically just an Inngest function. That's the bare bones of what you need. There is a lot more you can do from here but we will just pause here for a second. Is this all making sense so far?

JASON: This is all making sense so far. We are saying this is what the thing is called, this is what causes it to run, and this is what it does when it is called.

DAN: Exactly. In this area, if you had some existing logic you were using, you could import libraries or business logic and use it within that handler.

JASON: So if you are writing dry code you will break up the logic into like little utility functions. If you have save new user to database as a function you would be able to drop that right in here and say like, hey, run my save new user to database function, and the user details would come in in the event. So you use it just like you would any where else in your app but now you have these bonuses of it being logged and retried and all the good stuff there.

DAN: Yeah, completely. What we can do now is we need to expose the functions. By default applications are what you want to do is have an API route.

JASON: Is it a get?

DAN: It needs to be get post input.

JASON: There is a thing for this if we go to the dock -- docs? This is exactly what we need. You can copy and edit that.

DAN: We can import the client and the separate function.

JASON: So the functions we don't have yet. What we are going to get for functions is handle booking and then functions is -- look at it. So this allows you to define as many functions as you want. Oh, yeah, we didn't export it yet. That's perfect.

JASON: Now we have a handle booking function.

DAN: You can do a single file or function per file, whatever you want. This is it. What this sort of does is important. This creates an on your app/Inngest and uses our SDK to act as a handler for that end point. If folks are familiar with GraphQL there is a single thing that handles mutations and you define. It is kind of similar to that. What this does is it allows Inngest to communicate with your application securely and basically tell it the work to perform. What do I need to do. This allows us to, one, look at what functions I have, and when something happens, we can tell your system to run and execute those functions.

JASON: So this is running. The thing that would make me nervous about this is debugging, do I have to go set up a whole production instance? How do I run this to make sure it is working?

DAN: That's a great question. The core of Inngest is open source. We think you should be able to run this stuff locally and you don't need to create an Inngest account to run things and get things started. You should not have to pay us for credited if you are putting things locally. You can use the Inngest dev server and download that mpm or run mpx. In the terminal, is the app running yet?

JASON: Let me get it running.

DAN: In the browser, sometimes I recommend we do like a check of hello world and do slash API slash Inngest. Error occurred here. What do we have here?

JASON: Invalid URL. Oh, do we need to define the URL in Astro? I think we might. Let me double check. The checks. Those are all fine. These are all fine. So what's missing? It says... trying to call URL -- serve invalid handle URL. It doesn't run the server actions on another port, right?

JASON: It shouldn't. But let me just double check. We did auto accept some code.

DAN: Do we do -- in the client, there is Astro dev, do we have to do dash dash host? I have that on mine --

JASON: That's definitely possible. Let me look back at the demo here. Let's go to Astro. Let's look at the package JSON. We have got host. Yeah. Let's set that and see what happens. I am going to go back to my package JSON. We are going to do astrodev--host. Let's try that again. We will see -- so now it is running here. Let's try this URL run more time.

DAN: No go?

JASON: Not liking that. Maybe it needs to be the network one. It is not getting URL which makes me wonder if it is trying to pull -- we didn't miss anything. It is all here.

DAN: No, it is all there. It should be -- yeah.

JASON: Let me just match the thing. It had the port set to 3,000. If that doesn't work, I am going to clone your example and it is going to be just fine. We are going to stop and start this. What are you doing? Local host 3,000 API. It still doesn't like that URL.

DAN: I am running basically the same thing on my machine. I am running astro 5.12 instead of 13 but I will upgrade.

JASON: That would be super weird but now that we have everything in we will pmpm install. Done. Didn't update anything. That's all fine. What on earth are we missing? I am literally running this on another project and it is just fine. Is it -- what if it is just that it is trying to read something -- didn't like that either.Al this runs a promise. That's async. If we return it empty --

DAN: That's fine. It should also work. What it does and what is happening and I think it is some sort of thing that I am not sure what the Astrolevel is it tries to introspect the incoming URL. Owen in the comments said something about saying base -- is the server in your browser trying to do https?

JASON: It's not. Why does the base say https? What a great question. Let's try it with this. I am going to see if we can do https on it.

DAN: I don't know if Astro has something like that.

JASON: No, it's not going to do it. If this doesn't work, we are knowing -- going to to -- do a do over.

DAN: If you curl the end point does it do something? Curl local host 3,000.

JASON: Type error. Just a straight up type error. That's not good. Something is weird. Is there anything that's trying to load it in a server? Or -- it really shouldn't. Let me stand up another API end point. I am just going to have this -- that's working. It is trying to do something. I have a hunch. I am going to try just setting the base --

JASON: We have a config in my test output server --

JASON: I bet that what it is. Where were we? There we go. God damn it. That was an excellent use of our time.

DAN: We can edit that part down.

JASON: We are fine. We are doing great.

DAN: I just not sure what that option is in Astro. It is very interesting.

JASON: By default, Astro is serving static end points so when you hit a get it will try to do it statically because we didn't configure it to be server render. By configuring to be output server we are saying render it on each request. That's what went wrong. We were not sending it the request and it was saying I need stuff and I am not getting it. That's my bad. I am supposed to be an Astro expert and forgot that.

JASON: It was a good journey. This is why I recommend people to open this in the URL. Everything is setup. This is a good like diagnostic end point that might allow you to debug certain things in the future. It tells you the mode dev. It is like I am running in local dev mode.

JASON: Right. Right. We are good. We are in dev. We have one function. We aren't doing event or signing keys which we need if we want to go production but we aren't doing that right now. It works. We are happy.

JASON: Let's run the Inngest dev server. Now we have everything and the server running, let's open up a new terminal. We will Inngest dev server, mpx or pmpmx or whatever it is. Just type in Inngest-cli@latest. What we are going to do is run the dev command and pass an argument. A dash u saying ping this local URL and our Inngest route hpp local host 3,000. Hit enter. If you have it -- pmpmdlx.

JASON: That's what it is. That's my bad. I always forget -- come on, bro. I am going to run MPX because I don't remember what the actual command here is and I don't want to debug that. Here we go.

JASON: And we recommend the latest because you will always get the latest version we release. Now you can command click, open up local host 8288 and this is running Inngest on the machine. This is our own sandbox. Let's click the apps tab. We say where is my app running and that's all Inngest needs to know. It looks at your route and sends a basically introspection request saying what the are the functions of the app and the configurations and we can see all the different things. If you click on view functions or the functions tab either way, this will allow you to see kind of what is configured. Click on the view functions. What we are going to do is just run it. You can view by clicking or just envoke it. Let's put that, you know, whatever data we want to put in there, we can edit that and say hello, world, or what might our booking data be.

JASON: Yeah. Right now let's just say name and we will pass in and start there. That will be a good starting point. This is going to throw in our data. We have got our we have our handle booking and here is our run. This is what is kind of cool. We can see the events that run and you can see what ran. These are the actual things happening. When I click this, I can see a bunch of extra stuff. It is my app, the function I was expected to call, and it shows what the input was. We can see do you know here our data. There is Inngest data coming through as well. I am looking at this. This is my data. And then the output is the message we hard coded.

JASON: Yeah. That is, you know, that is the everything is running in the background. You can test things and debug things. Often, we have been situations where we need to procedurally do three things in the UI just to test functions. This allows you to change like you are doing right now and exactly --

JASON: This is one of my absolute favorite things. Watch. I am going to save this. I am going to come out here and just click rerun. Now it is up here. Watch. We didn't have to go retrigger the thing. We just get to use our function again and do the stuff we wanted to do with it. From a debugging standpoint, I have never been happier then when I discovered that was possible. It just makes your life so much easier because now -- like the flow, right, when you are not using something like this, is you write a form, you want to process that form, and so you go, you write it, write your handler logic, fill out the form, submit it, go look at the output, it is broke, fix it, fill out the form again, resubmit it, look at the output and now you can make a fix and retrigger. It is as if you submitted the form again but you don't have to go back and do that. It is these little tiny up grades in your debugging workflow that save you a few seconds at a time but it is delightful.

DAN: I love to hear experience there. We could just throw an error in the code just to see what it looks like. In our function, we will do the same thing, we will just throw an error. You can get fancy if you want to do math.random but let's just do a test and we can show. This is something that can happen typically in the server. Let's do the same thing.

JASON: Rerunning it.

DAN: You can see it is making attempts to run it and there is the error. As we watch this, there is an exponential back off which I think you hinted at before. It will re-try the function again. And now we have multiple attempts. We will space those out in case of a networking blip and it will have backup. It will continue to try. You will see the logged errors. If you theoretically actually changed tabs and remove the error and save, let's see if we can do it fast enough. We will just comment that out. Save it. As it backs off, there is more time in between this and the next one. It might take 60 seconds and then ideally it succeeds. Also there we go. Attempt three we can see it worked. This shows what you would get in production as well as in terms of individual reliability and retries out of the box. If you also said, you know what, I know this code isn't going to run but I don't know how to fix it you can hit the cancel button, chill, figure out the error, and go back and rerun. That also is sometimes a challenge to be like can I fix the bug fast enough?

JASON: And we have control if we wanted to say only re-try this. I can come down and say retries. Can I describe the backoff and stuff?

JASON: No, we have a standard back off strategy right now. We don't allow you to get too completely customize it. You can specify the number so if you don't want to do it you can kind of control what you want to do.

JASON: If you are running an LLM, you can try twice and don't want to sit there and burn tokens with 50 retries. Lots of configuration there. We also have got these other things we will not get into like running things concurrently or having a handler you call on failure. All these really cool things. Now the next thing I would love to do if you don't mind is I want to add another step. I want to trigger a real workflow here. I want to run a workflow where when we submit and grab the name.

DAN: On the handler arguments, you have event in the destructured handler arguments online 11. There is also the step object that's also returned here. This has a few different things. At the basic level, we can add a step.run and then let's see. We might check availability if you are running a booking. And we can make it a no auth running a value and fill out the rest. This is an async function so we want to await the step.run. And then this handler that's within the step.run is also async so you can do the same type of experience. It is a promise. You can parallelize it, chain them, whatever you want. You can return values from this to use in other steps.

JASON: For this one, let's get a message. We are going to await. We will say what's your favorite color get that data name. Here I can get this back. I have access and it says what's your favorite color or whatever I submit. I can use this down here so theoretically we would want to send this message somewhere and then we want to wait. We will talk a little about how we do this -- actually, what's the easiest way to show this? We can probably just invoke an event to show it?

DAN: There is two things you would go from here. We have been testing it in the dev server and what I typically say if you want to figure out end to end, we have that form in the front end that triggers say an Astro action which then sends the event so you can kind of use the front end to trigger the job. That would be the thing to wire up end to end. We could do a human in the middle thing depending on time.

JASON: Let's fake it 1st and send the vent. If we have time, I would love to continue and try to get the real-time wired up. I think that would be really fun.

DAN: You would import the Inngest client and use Inngest.send and send the vent payload and that would trigger this asynchronously. You could then get data from the front end or wherever in your system to be able to send it to the background.

JASON: We would do something here, get the action, and do label for name, and input tag name, and submit that and this would then go to our action which I think we can put together pretty quickly. Here is go actions.index.ts. I am just going to grab an Astro quickly. We can do one of these. Throw one of these in here. We will define our action and our action is going to be submit.

DAN: The name.

JASON: Yup. We don't have a good export server action. Down here you submit to my action. OK. And soal -- define action. Where is define action. Show me validating form data.

JASON: Accept form. Perfect. I want to accept a form. Whoops. We will call this one submit. We have a submit and the only thing it is going to accept is a name. It is not an email. We will dump that and that. Down here we will get the name. Import from the client ts file. Got it.

DAN: Then it is Inngest.send and the payload is the structure of the data. There is a name, booking created, and data name. You will want to await this as well. Theoretically you could set it in the background, but depending on the environment, you always want to await these promises.

JASON: We will send a message to confirm it is working. We will get actions.submit out here. We should now be able to come back out here. If I am right, it didn't do it. What did I miss? It didn't even try to run that. The thing I missed was form, define action, and then we are going to --

DAN: Do you have to add -- actions.log out.

JASON: Maybe I have to set the method. Let's try method.post on this. Try that one more time. There we go. There is our action submit.

DAN: Booking created. You can see the event and the payload on the right. There is the name.

JASON: Output is booking created. Now we are getting the thing, right? So now we can actually submit something. When we get back in here, this is what is happening. We are getting our message and all that stuff. Let's wait for a thing before we like fully return.

DAN: Sounds good. Color chosen, that's great

JASON: It kind of did the thing for us.

DAN: Wait for event pauses your function as is. Because the state is held by Inngest, your code doesn't have to keep running. It tells Inngest this function went to sleep, stop doing anything, and when and it tells us when to call the function back and continue from that point. So we can call it, yeah. Like color chosen and timeout can be one minute. You can specify different things. Let's give ourselves one more time and do one hour like 1H. Also what this allows you, and we can return say booking created and throw the color in here too.

DAN: What happens with this is color, if there is a timeout, it will be null. Because every function, you don't want to run it inperpetuity forever. You want it to end at some point. You might give something two weeks, or one hour, but we require you to set a timeout. If someone bounces from your website you are not waiting forever. This is great.

JASON: Another way to do this is we could set-up an if-color. Then we can return this --

DAN: You can play with the different things, yeah.

JASON: Otherwise you would say -- reading my mind today. That's exactly what I needed. This then should allow us, and we said color chosen so they are going to -- yeah.

DAN: And you know, this is the basic thing. Let's fire this off. Submitting out here. Says it is running. And then you can look at any things. You see color chosen is blue. It is waiting.

JASON: It ran a bunch of times. Every time I saved it was auto creating. This is when we actually submitted.

DAN: If you click on color chosen on the left, on the trace, that step, on the right you can see that this has basically what is it waiting for? It is waiting -- it tells you when the timeout is happening. At the top right there is a button called send test event. In here, let's do color-chosen.

JASON: I am just going to copy/paste this. And then the data is going to be color.

DAN: This is JSON.

JASON: You are right. Sorry.

DAN: No, its cool. That's why we have syntax highlighting.

JASON: What's your favorite color, Dan?

DAN: Green. Green house and green car.

JASON: If I hit submit, we should see all this complete. Click on that same step. There you go. Are you kidding me? You know how freaking hard this would be if you didn't have a tool like this? Save it in the database, listening, make sure it returns an event ID that you are attaching to this particular thing. There is so many things that have to happen to make it work. There are things we still need do because if me or Dan are filling out the form at the same time we would have to make sure t is tied to current user instances and those sorts of things. But still geez that is cool. That is so cool.

DAN: When you specify the objects in the way form event, you can check it out in the docs, you can specify an if parameter and this allows you to create a spring expression and we optimize on the system because we are handling millions of events simultaneously trying to match in real-time. Event.data name is equal to -- you would put in -- let's see. Async.data.name is equal to and you can put in Jason here. But you want to make sure you use a template kind of thing. You can use a parameter from your event payload. Ideally you have a user on each side. You have user ID is equal to and a sink -- this is coming in asynchronously. It has a prefix. This is all on the doc. That allows you to make sure you aren't resuming something for somebody else's work. You can compound things to see it is this user and I want it to be vis -- this booking task ID.

JASON: Very cool stuff. Do we want to attempt to get anything else done? I know we are running a little short on time here. I don't have a hard stop at 12.

JASON: You think if we had 10-15 we can do real-time?

DAN: I think so. What we would end up doing, and maybe we can set the stage for real-time, if you are creating this interactive human in the loop flow, and anyone messing around with UI agents, this is a pattern of AI suggests to do something, do you want to do it? You need to pause the execution before continuing. You can do the same thing like you did with what's your color. To get started, real-time is just a little tiny package that we have already installed fortunately which to get started, let's go to the client file. And let's import, we will do import real-time middleware. And this should be from real-time/middleware. Cool. And then just under ID and we will type in middleware colon and what it is prefilling is exactly what we want to do. You can design your own and there is different types of middleware that we offer that will allow you to extend Inngest across all functions. Real-time is middleware we ship and you can drop it. Now what you can do in the handle bookic function, middleware allows you to hook up to a client that you can subscribe to updates and on the server you can publish updates. In here, once you added middleware, in the arguments online 10 of the handler there is event step and there should be now a publish. Cool. Let's publish an vent here. Let's go simple in the docs. It is an object. And the object has a channel, different properties, and let's do -- then there is a topic and there is data. Channels are how you can divide tenants or users so this user is going to authenticate for user ID 12345. Maybe we could hard code that right now. Let's say user ABC or user 12345. And topic let's say messages? Questions?

JASON: Yeah, questions work for me. Message.

DAN: That's perfect. You are sending it right to the compliant. That's the server side component of this. Now I think we need to go into the Astro view. Maybe let's add another like let's add another dom. Let's put a defensive with an ID. You might know more Astro than me. I think what we want to do is write some JavaScript here. You can import from the import subscribe from the Inngest real-time. And we are going to go fast. Let's just do -- we are going to do await subscribe here. We will do a wait subscribe. We will specify the channel and the topics. Let's do channel, user ABC, and this could be multiple. We will just listen to one topic. You might have this part of the UI is listening to questions, this one real-time progress updates as I am transcoding an asset, and now from here, we can just -- let's chuck this into a constant like a con stream await and subscribe. Now we have this thing that we will just keep adding chunks basically to it. Let's make a four loop. Let's say call it chunk. Chunk of stream. And in that -- I think we will do for await. I forgot about all this helper stuff. It is so much easier these days. Makes me feel old. We will chuck whatever the output is into the message.

JASON: Let me grab the element out here. ConstantL is get ID. That is our message. We can L.text content equals chunk. Or do we want to append?

DAN: We can do either. Chunk would be the full data object which we have chunk.message. There is all kinds of awesome typing stuff you can find in the docs. You get all the type safety front end to back end. We don't need to do that right now. This would be chunk.msg maybe. L might be --

JASON: Chunk.data.msg. You are correct. Chunk.data.msg. You already did that.

JASON: They thought this might be so I cast it as an element because I know it is there. Theoretically this works?

DAN: Theoretically this works. I will give you caveats after this. Let's give it a shot.

JASON: I don't want to do that. It is already sending it through. It is working.

JASON: It kept resubmitting to functions.

JASON: I specifically don't want you to keep resubmitting. Now if I say my name tada. We will grab that real-time input, create a new form, and you would be able to choose a color. Since we are out of time we will leave that as an exercise for the reader because it would be the exact same thing. In your actions file here, you would be adding a new handler that's like hey, spend a color selector and pick a chosen color and that would be it. Really, really nice to work with. I think it is just a great tool. For folks who want to go further, we have got the docs somewhere down here. Let me open up again. I am going to send them --

DAN: You can go to recall our Discord. If you are in Astro you can go through the quick start or look on our GitHub repo. From the astro setup, you can learn about these and go through the docs. If you are not using this -- if you learn one you can learn to the other. The only Astro specific stuff was in the client and that serve which is the same in every framework because you can see on the screen.

JASON: It is good stuff. There is a lot to like in here. I am going to throw people to your LinkedIn. Is there something else you want people to go if they follow what you have to say?

DAN: No, no, we love feedback. We love people to ask questions in Discord. You know? We have our Twitter and you can @ mention me. I am at @djfarrelly. If anybody is interested in real-time, we have docs and we will continue to work on building out more examples and frameworks. You can do secure type safe and that's all in the documentation you can find.

JASON: This was so much fun. I hope that y'all are inspired to go out and try some of this stuff. It is really, really, cool to see this working. It is just so much fun to build these complex workflows knowing that you don't have to manage all the plumbing that keeps them safe and functioning and retribal. That was about I think an hour of Inngest. I feel pretty good about that. I am really excited to go get this real-time stuff running in the code TV workflow. I have ideas in flight, going to get it plugged in, and see a lot of fun stuff coming out. Thank you, Dan, for spending time with us. Thank you to everybody watching for being part of the show. We will see you next time.