In this chapter, we're going to learn all about background jobs. We're going to learn how to add them to a Next.js application, and we're going to learn what they are and why we need them. In order to understand that, let's first look at a normal example. Imagine a login form. You enter your email, your password, you click login, we send a network request and we get an instant response, success or fail.
We've already seen this a million times. But now, imagine you have a more complex task at hand. Imagine you offer your user an ability to generate a summary of a very, very long YouTube video. Imagine my videos, for example. They are sometimes 12, 24 hours long.
So this time, we send a network request. And in order for our back end to generate this summary, it can take well over 30 seconds to do that, because just imagine everything that needs to happen. We first have to download the YouTube video. Then we have to transcribe the video. And only then can we send that to an AI model to generate the summary.
So depending on the size of the video, depending on the AI model you will use, and depending on your overall infrastructure, it can take well over 30 seconds for that to finish. And if you have a task that's running for so long within a normal network request, like this one, you know, something completely normal, you risk your user never getting the result. So the problem is not that the user has to wait. The problem is this network request can timeout. The user can accidentally close the tab, or the user can lose their connection.
If any of these things happen, the user will never get the result back and we have to start the entire process again. That's why we have something called background jobs. So let's imagine this again. The user clicks generate summary. This time we send a network request again, but instead of using our backend to generate the summary, we use our backend to invoke a background job.
And the moment we've done that, we are finished with our network request, which means that we immediately return back to the user and say the summary is being generated and the user can now close the tab, they can go for a run, they can do whatever they want. What's actually happening is that the moment we invoke a background job, the background job now runs in a separate environment independent of the user's session, independent of the user's connection, right? And we can simply notify the user when we are done. This is the structure you have to understand if you want to build AI apps, because depending on the model you will use and the complexity of the app you will build, most of your tasks will be long-running tasks. We will achieve this in our project using Ingest.
So let's go ahead and create our first function and let's trigger a background job from Next.js. You can use the link you can see on the screen or the link in the description to let them know you came from this video. And once you're here, you can immediately go inside of the documentation, select Next.js and select AppRouter here. And then let's go ahead and install Ingest. Before you do that, just make sure that you are on your main branch and you can click Synchronize Changes just to confirm you didn't have any unsaved changes here.
You can see my last chapter was TRPC setup and yours should be as well. So I'm going to shut down my app now and I'm gonna do npm install ingest. And now that I have this installed let me quickly show you my package json so you can see the version that I'm using. Once you have installed Ingest, the second step is to run the Ingest developer server. The version that I will be using is 1.8.0.
But you can see that in here they simply target the latest version. So when you see me now doing this, npx ingest-cli at latest, the latest is equivalent to 1.8.0, just in case you were interested in my exact version. So npx ingest-cli at latest or a specific version and then dev. And when I start this, you can see that it says Ingest dev server is online at localhost 8288. And if I visit the project here, well, you can see that not much is going on here.
It is a developer server, but nothing is here for us to do. So what we have to do now is we also have to have our app running. So let's do npm run dev. And in here, you will start to see something. You will start to see a bunch of 404 pages here.
That is because the ingest developer server is trying to find our ingest initialization in our project. But since we haven't done that, we just have a bunch of 404s. So let's go ahead and continue with the documentation here. I'm gonna go ahead and create an ingest folder and then put a client.ts inside with this simple code snippet. So let me go ahead here inside of source and I will create a new folder called ingest And I will create client.ts and I will paste this inside.
I'm going to call this vibe project or vibe development. Something like that. Basically the name of your project here. And once you've done that, you should, actually not yet, sorry. So this is the first step.
And then we come to the second step, which is creating an API endpoint for Ingest. So you can copy this snippet as well and go inside of app API, create a new folder, ingest, and inside go ahead and create route.ts and paste this inside and you can replace this with an at sign and a forward slash, There we go. It's basically using this ingest which we just created. And the moment you save this file, if you have named it correctly and put it in the API folder, you should go here and you will see that now you finally get 200 here because it finally found the ingest integration and now it will only try to hit that endpoint instead of all of these other ones. Great, but we still don't have anything useful in the developer server here.
So let's go ahead now and let's continue by creating the first ingest function. So I'm going to go here inside of source ingest functions.ts and create a new function. So inside of ingest folder in the source, make sure you don't accidentally do this inside of API ingest. So in here, create functions, whoops, functions.ts, import the client and a simple hello world. In just create function, we give this an id, we give this an event name, and we have a very simple step which waits for one second.
And it then returns a dynamic string which uses the data we can pass to this background job. So this is an example. So they show us how easy it is for us to pass some data to this background job. This would be, for example, a link to a YouTube video we want to summarize, right? That's what this data object would hold, for example.
I think I have a typo in my functions here, so let me just fix that. And then let's go inside of the app folder, api.ingest.route, and inside of here, let's import hello world from that ingest functions file. And now, if you go back here, you should be seeing your app here available, auto-detected, you can see everything is fine. It found it at API ingest framework next.js, and one function found, hello world, because we just added it here. So if I rename this to hello world 2 it immediately renames here as well and now instead of these functions Let me just refresh so it's back to this name.
I can now click Invoke here and you probably have empty data, but you can go ahead and add email and pass in, I don't know, it can even just be the name, it doesn't matter. And click Invoke function. And you will see how it went from queued to running to complete it. So that's the status that just happened. It happened very quickly because there was no function before this, so the queue to running went very fast.
And the running only took one second because we only wait for one second. For example, let's now increase this to 10 seconds and save the file. And let's go inside of our functions, invoke, and Let's click this again. You can see how now it keeps running. It's running for five seconds, seven seconds, and finally, after 10 seconds, it's finished.
Is this getting familiar to what we just discussed? This is a background job. The only problem so far is that we are not invoking this function from our network request. We are manually clicking on invoke here. How do we invoke this from, in our case, trpc procedure.
Let's go ahead and let's do that. So I'm gonna go inside of source, trpc routers underscore app. And in here, I'm going to call this invoke. This will be base procedure. And let's add an input here, z.object.
And let's pass in the text to be z.string, like this. And then instead of .query, let's add .mutation this time. It's going to be asynchronous here. Let's extract input from here like this and this time what we're going to do is await ingest from ingestClient.send await ingest from ingestClient.send. The name of the function you can find here.
This is the name of the function you will run. So pass that here. And then you pass in the data and the data can be anything you want but we know that we accept email here so let's go ahead and pass email to be input.text because we defined it as text here And this is how you invoke a background job from TRPC. So now let's go ahead and actually use this invoke method. I'm going to go inside of source app folder.
And I'm going to go inside of page dot dsx here. And well, we already have this setup. But since we are not going to need it, I'm going to delete it for now. I'm going to delete this client.tsx. I'm going to go back inside of the page here.
And I will simply return a div here, test, and I will remove everything from here. We only use this to learn about DRPC. So let's mark this as useClient so this becomes a client component. The useClient is very important for our demonstration here, so please do it. Now, when you refresh on your localhost 3000, you should just see a test here.
So now let's go ahead and let's create a padding for maximum with 7xl-mx-auto. And in here, let's add a button component, invoke-background-job. And there we go, we now have a button to invoke a background job. So let's go ahead and add trpc here. Use the RPC.
And let's add invoke from use mutation from Dan stack react query, pass in trpc dot invoke, and pass in the mutation options here. And then in here on click, call invoke and call mutate. And pass in the text to be test or John, something like that. So what I want to demonstrate to you now is how quickly this background job lasts in comparison to the network request. So I'm going to go ahead and open my developer console here.
I'm going to go inside of the network tab. And I will click invoke background job. And let me just see, did I even do anything now or not because I'm definitely expecting to see something here but it is not happening. Let me refresh the page. Alright, so my website froze.
So what I did was I simply shut down my app and I did npm run dev again. So I'm hoping to try it out again, this time successfully. There we go. Now when I click on invoke a background job, you can see how quickly this finished. Let's just see.
468 milliseconds. That's how long this network request took. But we know that the actual background job took 10 seconds. So that is what we wanted to achieve. If I go inside of tRPC invoke again, and inside of the ingest and go inside of the functions.
And let's change this to 30 seconds and add a comment here. You know, imagine this is a download step right here. In here we are downloading a video. Then in here, imagine this is a transcript step, another 10 seconds. And then finally, you know, imagine this is a summary step.
And this finally then takes five seconds. This is what we want to achieve, right? The moment we click on generate summary, we send a network request, which trigger a background job and we immediately allow the user to close the tab, right? So let's try it again. In order, If you want to, you can also do this.
You can go inside of your layout in app folder, and you can add a toaster from components UI sonar. Like this. I just like to order the components like so. And now once you've added the toaster, you can go inside of the page here, go inside of mutation options and add on success here. And you can add toast from sonar.success background job started.
And now you will see the following. If your app gets stuck, you can go ahead and just do npm run dev again. I did get this happen a few times and I think I solved it when I removed TurboPack but we'll see. Basically if your app hangs on loading don't worry you can just restart it. It will not happen in the actual app.
So let's try this now. We can also do disabled here and let's do invoke is pending. Like that. And we can also go inside of invoke function here, trpc invoke, and after a wait, let's return, okay, success. Like this.
So now, If you click Invoke Background Job, you can see this is it. The request is finished, right? The user can now close their tab. And what's happening is in the background, right? We're now doing the first step, which would be 30 seconds of downloading a YouTube video.
After that, we're gonna go ahead onto the second step, right? Which would be transcribing a video. So let's see. After 30 seconds here is finished, we go to the second step. And this step will last for 10 seconds, because we just wait for transcription to happen.
And then finally, we have a third step. And in here, we would do the AI summarization, and that's it. Hello, John. Or this would actually be the summarization. So that is how background jobs work, and that's how you add them in a Next.js environment.
And it doesn't matter if the user lost their internet connection, it doesn't matter if they close the tab, because the moment they invoke a background job, the background job has started. They can lose their internet connection. Obviously, In development mode, if you lose the internet connection, your dev server would fail. So yes, in development, technically, you cannot really shut down your laptop. But in production, it will run on a separate server, in a separate environment.
And from this Ingest developer server, you can easily cancel things if you don't want to, you can rerun them, you can go ahead and look at the payload that was added, you can do a bunch of things here. And one cool thing about Ingest steps is this will later be, of course, more complex things than just sleeping for 30 or 10 seconds. This will be API calls, database requests. And if they fail, it is crucial to, well, retry them. And that is what Ingest does automatically for you.
They actually have a cool example on their landing page here. And yes, they have AgentKit. This is something I didn't want to talk about immediately because I don't want to confuse you, right? So alongside background jobs, we're gonna use Ingest to build autonomous agents, right? So AI and background jobs go hand in hand, and Ingest is the platform to do both of that.
But I first want to introduce it through background jobs because it's easier to understand, right? And in here they have three very cool examples. So this is the transcription example. You can see that they have a step called transcribe video. So it's very similar to step.slip, but instead it is step.run and they call it transcribe video.
And in here, they simply return a DeepGram SDK with a function to transcribe a video URL. And once this step finishes, they call an LLM chat GPT 4.0 to create a summary. So exactly what we did. So this would be the two steps, right? This would be the first one, you know, transcribe the video.
And then the second one, summarize the video, right? I just used the download step as well. So it looks like we don't need a download step, right? So this is what you can use InGEST for. The second one is to build AI with automatic retries, caching, and improved observability, right?
So It's way more powerful than I can showcase in this short chapter. That's why we will have more chapters later on to build the actual agent networks, agent router and agent tools, right? And of course you can do sleep, which right now seems only for fun, but sleep can be very useful. For example, you can send a welcome email to your user, then wait a week, and then send a follow-up email. So yes, that's how long these tasks can wait.
They are background tasks. Perfect, Yes. And for development, you don't need any account yet. But later, for production for deployment, we're going to have to create an account with Ingest. But for development, there's going to be no need for that as well.
And I think that's great, because we can get started right away just by running this. Perfect! So I think that was the goal of this chapter. I think we achieved this exact thing right here. So we set up Ingest.
Let me just change the color. So we've set up Ingest, we created the first function, we explored the Ingest developer server, and we triggered a background job from Next.js. Now let's go ahead and branch out and push this to GitHub. So let me see this chapter name. This is 04 background jobs.
So I'm going to go ahead and click here. You can see that I have 10 unsaved files. And I also have this little database in the ingest folder. If you're wondering what that is, I'm just guessing it's cache for the Ingest developer server. And now I'm gonna go and click on the main here I will click create new branch and I will do 04 background jobs.
After I've done that I'm going to stage all of my changes and I'm going to create a commit message. And I'm going to click commit and then you can see that CodeRabbit extension, if you remember from the previous chapter, you can install this, a completely free AI code extension, which allows you to review all of those files. So let's go ahead and review all of these files. And while this is doing its own thing, which is most likely a background job on its own. So you can see background jobs every day, right?
You probably just didn't know they were called background jobs. While that is going on, let's go ahead and let's create a new pull request inside of our repository here. So let me go inside of pull requests here, a new pull request. Oh, it looks like it won't push until, this reviews. So I'm going to go ahead and review first.
Actually it will, I just have to click Publish Branch. Yes, I forgot to click that. So make sure you click Publish Branch. And you can see that this is still doing its own thing in the background. And I think now that we have a new branch, there we go.
I forgot to do that. My apologies. So now we're going to have two AI reviews here. One is going to be from here, and the other one is going to be from here. And you can see how cool it is that it can add comments on my code locally here in my, there we go, you can see fix duplicate step ID.
So it noticed that all of my steps are called wait a moment, which is very not useful when you were reading inside of the InGest developer server. So it already detected that, for example. So you can see how cool it is. And it also fixes some wrong things here. Payload mismatch with InGest function.
This is, of course, not something that we need to fix right now simply because this is a demo, but it is very useful as you can see. It detects pretty much everything. So if you don't want it here in your pull requests, you can have it here in your IDE. And here we have our pull request summary which is pretty much identical as you can see to the pre-extension for Visual Studio Code. You're gonna see in here we have fixed duplicate step IDs, and you can see that here at the bottom, I have the exact same issue here.
So you can choose which one do you like more. Do you like your pull request reviewed, or do you want to submit clean pull requests by having this run before you push a pull request. So in here I'm going to read through the pull request simply because we have the summary here. So we introduced an API endpoint to handle background jobs using Ingest. We added a button on the main page to trigger a background job with real-time toast notification on success.
We use this to visually measure how quickly the network request is finished in comparison to how long the background job actually lasts, right? And this is what I like the most. I like the sequence diagram because it is exactly what we discussed in the beginning of the chapter. So the user clicks on invoke a background job. We send the invoke mutation with the text and then our TRPC router, which is our network request, simply sends the event with that data.
This can basically read as invoke a background job and the moment we do that we can send back the user okay success. So this part right here is identical to what I wanted to achieve here. The user clicks, we send a network request, we forward the data to a background job, and we immediately respond to the user so they can close the tab and move on. And the fun thing about this background jobs is the way CodeRabbit just reviewed my 10 files here is by using a background job. They definitely didn't have a network request which went on for 30 seconds, they had a background job which did this exact thing.
So I went through these potential changes, they are all very correct, but since this was just a demonstration It makes no sense to fix them right now because we will remove the whole page entirely In the first place, right? So we are good to go with merging this pull request right here I'm not going to delete my branch simply so I can go back to this part whenever I want. And let's go ahead now. Right here, let me close this. Let's go back to our main branch.
Let's click this and let's synchronize our changes. And after that, I'm going to click no on this. I'm going to go instead of my source control on the graph. And you can see that I have 04 background jobs now merged right here. And you can see that I'm on the main branch and I should have access to my ingest folder here which basically means we fixed all of these things.
Perfect. I'm also not gonna do anything regarding this CodeRabbit comments here simply because all of this was just a demonstration. Amazing, amazing job. Let's go ahead and mark this as complete now. And see you in the next chapter when we are going to extend the use of our background jobs with AI.
Amazing, amazing job.