Using Inngest to Add Email Automation Feature to ProNextJS

Transcript

[00:00] We are going to take a look at adding a new email automation feature to CourseBuilder, which is our platform for delivering course products for developers. The feature that we want to add to this upcoming product is a post-purchase follow-up based on the user's activity. Basically, when they first sign up, we send them a thank you with some product instructions. And then we want to wait for activity. If the user completes a lesson, so if they make progress in the course, then we're going to send them a congratulations for making that step.

[00:35] If they don't do anything, if they don't have any activity or make progress in the course, then we want to send them a polite nudge encouraging them to do so. And if they continue not to do any activity, we're going to logarithmically delay continued nudges until they do actually make progress or we eventually leave them alone. This product is Jack Harrington's Pro Next.js and we are connected so we're going to go ahead and make a purchase. I'm going to use my test email, enter the test card, and then make that purchase. The purchase was successful.

[01:16] We get some post purchase instructions and now we can go to our inbox to actually log in and access the course that we've purchased. If we hop over to Gmail we can see that the email was properly delivered and I can log in to the course. Behind the scenes if we open the Ingest dev server we can see that two events were triggered through the purchase process. The first event is the checkout session completed event and this occurs when Stripe sends us that checkout session webhook. And if we open that up, we can see that we can inspect the details of that Stripe event that came through, including the customer information, which product they purchased, that sort of thing.

[01:58] The other event is new purchase created. And this is emitted when a new purchase is created in our database. And what we do then is send a post purchase email and send a post purchase Slack message. So the post purchase email is the one that we just looked at, that login email that gives them access to the purchase, and the Slack message is a message in Slack that just notifies that somebody has bought something. When we do this, when we take one event and send that to multiple functions, we call this fanning out or the fan out pattern.

[02:31] As a developer, one of the coolest things about using an event-driven system like Ingest is that I can come in here and at the new purchase created event I can hit replay. And behind the scenes I can see that that event was then re-triggered and the functions that it executes were then executed again. What this means in practice is that I don't have to go continuously through the Stripe checkout process purchasing test products over and over again, simply to work on the email features that I want to work on. If we go check Gmail, we can see that we received that email and we could then update the formatting or change this email while we develop, instead of going back and typing 4242 424242424242424242424 all day long, we can skip right ahead to the functionality that we're actually interested in because we are in an event-driven system. It's really great.

[03:37] So that's the post-purchase follow-up as it works today before we implement the rest. We end up at the send thanks with product instructions step And now we want to wait for activity to send that nudge or see if they make progress and send them kudos for making that progress. CourseBuilder is a production product, and that means that there is some complexity involved and this isn't dulled down for presentation. Dulled down for presentation. Course Builder is a mono-repo that is powered by TurboRepo, and it contains a set of Next.js apps and a series of packages that are shared across all of those Next.js applications.

[04:20] To implement this feature for Pro Next.js, we're going to have to focus on, one, the Pro Next.js application, and then the core package, which drives a lot of commerce and other core functionality in Course Builder. If we dive down into Pro Next.js and into the source folder, we'll see a folder called ingest, and this is where we can look at the configuration for ingest in this specific application. The ingest folder contains the events that are specific to the ProNext.js application as well as functions that are specific to the Pro Next.js application. We have the configuration, so the configuration is actually passed to the API endpoint, and This tells Ingest the functions that we have available. That includes all of the course builder core functions that are shared across all applications and the functions that are specific to Pro Next.js.

[05:29] Additionally, we have the server, which is the Ingest client that sets up the relationship between the events and the functions that they execute. So this is pretty complex. We use Ingest's middleware functionality to provide different aspects, like access to our database adapter and different providers for the various servers that we use. And finally, we configure the client with both the middleware as well as schemas that give us type safety across our events, both the ones locally and also the Course Builder core events, which are shared across all applications. The event trigger for our email follow-up features is the new purchase created event.

[06:15] And to understand that, we're going to have to dig in to the core package. Inside of the core package we'll open source. There's a top-level folder called ingest and then we'll look at the Stripe event checkout session completed. So we can see here down in the depths of this particular file we have a step send event new purchase created event. When that gets handled that actually triggers our send creator slack notifications that's set up to listen for that so that's the trigger to that as well as the post purchase email which is also set up to be triggered by the new purchase created event.

[07:10] Since our post-purchase encouragement emails for now are just going to be for ProNext.js, we don't have to spend a lot of time in the core package, which is a little more abstract because it is shared across all of the applications that reside in Course Builder. The next thing we're gonna want to do is create a new function in the Pro Next JS app that is triggered with the new purchase created event. This function is going to be called waitForProgress and we're going to say export waitForProgress and this is going to be an ingest.create function, and we need to import ingest. So we import that. There we go.

[08:06] And then we're going to pass in the configuration, which in this case we want the ID. And we'll go ahead and call this wait for progress. Then we want to have the trigger. So the event that triggers this is going to be the new purchase created event, which we can then import. And finally we have the function itself.

[08:29] So now we can create the functionality that we're looking for that's waiting for them to make progress. In this case we're going to await and we're going to wait a step dot wait for event. Here we're going to give this a name. So wait for learner progress and this carries an event and that event is the lesson completed event. So we'll import that and the next thing we need to add to this is the timeout and we'll wait We're gonna wait two minutes the reason we're waiting two minutes is because we are in development and Generally, we'd probably want to wait for days for this, but for right now, we're going to wait for two minutes So that in development, we're not having to wait for days In fact, we might take this down to say 30 seconds.

[09:27] So we're waiting for even less time Now in this case we can say const progress and this will return this progress object. So then we can say something to the effect of if progress. So we know they made some progress. We could simply return progress else but in this case we're actually curious if they didn't make progress and in this case we want to then send them an encouragement email. We want them to actually make progress.

[10:03] So if they do make progress we're gonna handle that in another event. So we want to then encourage them when they make progress in another event and we're not going to handle the case if they do make progress here. We want to do that because we're going to add debounce and some other cool features to the progress when they actually make some. So the next step here is to take this wait for progress and then we need to actually add it to our config so that Ingest knows that that exists. Switching back to the Ingest dev server, we can open this new purchase created event that we accomplished earlier and push replay.

[10:45] And we can see that this now fans out to our new wait for progress Event and if we come over here, we can see that this is waiting. It's currently sleeping We set it for 30 seconds. So you are gonna have to wait for whatever time you've set it to You can cancel it so if you've set it for a long time and you want to cancel it you can do that. But ingest will sit here and wait for as long as you've set that delay. And you can see now the wait completed and the function completed.

[11:14] All right so we're going to make another function and this one is going to be called learner progress. This is something that is going to happen or be triggered basically anytime a learner makes progress. So we can say export const learner progress and this is ingest so we can import that and say create function and here first we'll have our config and the config needs an ID so we can just call this learner progress. And for learner progress, we actually want to debounce this. So what that means is if they're making quite a bit of progress, we wanna wait till they're done and send it after they've finished making progress.

[12:00] So I'm just gonna set this to something like 30 seconds again. This is great because if they're in a learning session we don't want to send them an email while they're in the learning session. We want to wait probably I don't know 12 or 24 hours even to send that that congratulations. So the second thing we want to add here is the event and this is our trigger and we're going to use the lesson completed event. And finally we can add our handler and handle that lesson completed event.

[12:36] Like before we need to take this new function that we've created, come down to the config, and add it to our config so that Ingest will actually handle it. I'm going to come in here. I'm actually going to give myself a little bit more headroom. We've seen it work once, so I'm going to give myself two minutes. So if no progress is made, then we have two minutes to wait for this event before this finalizes.

[13:02] And we're going to say, return no progress was made. And here we can return progress was made. So I'm not gonna implement the actual emails here, but we'll be able to test it. The same is true for lesson progress here. I want to say, an encouraging email was sent.

[13:30] In this case, we're, I'm gonna return that. We're gonna do several things because this is going to fire every time they complete a lesson. So we want to load all of their progress, analyze where they are at, send on that progress. So something we do in other apps is actually come in and use chat GPT APIs to take all of their progress and all of that context and use that to make a customized GPT email that we send based on the progress that they've made through both this and the rest of the course. So if it's their first progress and we can we can look at that and we can send them a note and congratulate them.

[14:21] Or if they've just completed the whole course or they're making leaps and bounds or they're progressing really fast, we can do that here as well. So back in the Ingest development server, I am going to replay the new purchase created event. And now I'm gonna come in here and I'm gonna test it by starting to learn. I'm gonna come in here and I'm gonna go to my first lesson and I'm gonna make some progress. Come back to the ingest server.

[14:53] We can see that we get the learner progress and that an encouraging e-mail was sent. There's a lot more that we could do with this now. Obviously, we want to implement the e-mail sending, looking up the progress, thinking about ways to customize this, but we've set up a rig now where we can start to expand and think about that and set up the logic in our workflow to complete this feature. We can also come in here and test out the situation where they make no progress. So we're waiting for progress here.

[15:28] We set this to wait for a couple of minutes. And after staring at this for a full two minutes we can see no progress was made. So then we could handle that situation also. Ingest is fantastic. This dev server is world-class.

[15:48] There is no other product that I know of that comes anywhere near this level of functionality. If you are developing any sort of applications, at the end of the day I think Ingest should be in basically every application unless you have a full team dedicated to providing this kind of functionality. Check it out, I think you'll like it, let me know. Also, if you're building Next.js apps professionally, I strongly recommend Jack Harrington's ProNextJS.dev. This is a great course that goes into very fine detail about building world-class, enterprise-ready Next.js applications with absolutely no bullshit.