0:00 All right, so our first step in setting up our new 0:02 workflow is 0:04 to be able to actually configure the workflow module, 0:09 which allows us to create new workflows inside of NestJS Workflow. 0:13 So for this, we're gonna head back to our root app 0:15 module and configure it 0:17 accordingly. So for this, inside of our existing imports array, 0:21 we're gonna add a new import to the workflows module 0:25 from NestJS/workflows and call forRoot on 0:29 it. Now, this also offers an async variant 0:33 if we want to inject any dependencies to build the 0:36 configuration. 0:38 For our case, we'll stick with a static configuration, 0:41 so this is fine 0:42 as forRoot. And now you can configure further 0:45 properties 0:46 on the workflow module globally that will apply to all workflows 0:50 created inside of the application. 0:53 For us, we wanna look at tuning some properties 0:56 that the underlying 0:57 worker gets configured. Now, the way NestJS 1:01 Workflows works is that it is a distributed 1:05 model where we can have multiple instances of our 1:08 application all participating in the workflow 1:12 together to help process large amounts 1:16 of work. So if we have multiple orders 1:19 that have to all go through 1:20 our workflow at once, we don't have to rely on just a single 1:23 instance of 1:24 our application. The NestJS Workflows module allows 1:28 us to scale our application horizontally, so we can have 1:32 multiple instances of our app that are all processing the workflow, 1:36 processing different orders in our case, and helping the throughput of our 1:40 application increase. Now, in order for this to work, we 1:43 need a way to have a store of the workers, 1:47 which ones are actually registered and processing data. 1:51 And the data itself, of course, as we'll see, is all stored inside of 1:55 Postgres. So both the data that belongs to the workflow, 2:00 as we'll see later on, gets stored to Postgres, 2:02 as well as the workers 2:04 that are actually registering to work on the workflow. 2:08 In this way, when a new instance starts up, 2:11 it can poll the database to 2:12 check to see if there's already a worker assigned, and if not, it can start 2:16 work. This is also known as a lease-based model. 2:20 When an application starts, it's going to poll Postgres for pending 2:23 resumed instances. If there's a slot available, it's going to 2:27 acquire a lease and start running the workflow after 2:31 registering. So this ensures we have as many, as we 2:35 call workers, running inside of the workflow as we want, 2:39 and all this information will be stored in Postgres, 2:43 also known as the workflow journal, which is gonna store 2:47 the state of our workflow. Now, we are able to further configure 2:51 this worker and specify options on it. 2:54 For example, we can actually specify 2:58 how many maximum number of workers we want processing the 3:02 workflow. So for example, we can set the concurrency, 3:07 and this will specify how many instances execute at once by 3:11 this process. So if we hard code this to ten, 3:15 this means we can have at most ten instances of the 3:18 workflow processing in parallel. 3:21 Now, as we increase this concurrency limit, 3:24 we will have greater 3:25 throughput, and that will put additional load on our 3:28 application. 3:28 But this is what allows us to scale the NestJS Workflows, is that 3:32 we can have both concurrency with inside the same process with 3:36 this property, so we can have multiple instances 3:41 each working on the workflow, 3:43 and then we can scale horizontally by adding multiple instances of the 3:47 application. So this provides two layers of 3:51 scaling using workflows. The next option we can look at 3:55 is the lease duration. And so what lease duration says 3:59 is if the 4:01 application crashes or it fails to respond 4:05 for a certain amount of time, then we consider the instances that 4:09 the application has claimed 4:12 to be released, and they can be picked up by other 4:15 processes. 4:16 So we can set this to any time value, 4:20 in this case, thirty seconds, which just says if our application fails 4:23 to 4:23 respond for thirty seconds, then we will give up any 4:27 instances of the workflow that we have been processing so 4:30 that they can be 4:31 claimed 4:32 again. The last property we'll look at is called the shutdown 4:36 timeout, and this essentially specifies how long 4:39 the application 4:40 shutdown will wait for any running executions before 4:44 abandoning them. And so what this means is when our application shuts down, 4:48 we might wanna wait for any steps inside of our workflow 4:52 to complete if they are currently in process. 4:55 So by giving this a buffer value of, for example, ten seconds, we 4:59 essentially just give the workflow a little bit of time to finish what it's 5:03 doing before shutting the application down. 5:06 So there are further options we can configure with inside of the workflow, 5:10 and I'll 5:10 leave a link below if you wanna dig deeper into all of these. 5:13 These are a few key ones that help us start to understand the 5:17 mental 5:17 model of what the workflow is doing under the hood, where 5:21 we have this idea of instances of the workflow, which essentially are just 5:25 invocations of it, and we have the idea of parallel 5:29 execution where inside of the same process we can execute 5:33 multiple requests for the workflow at once, and we can also 5:37 have multiple instances of the application itself 5:41 also processing the workflow. And we do this via a lease model 5:45 where each application will 5:48 claim a lease as it is processing the workflow. 5:51 And finally, the database is acting as the coordinator. 5:54 Workers compete for available instances via row-level locking, and the 5:58 lease prevents two pods from executing the same instance at the same 6:02 time. So this is all handled for us under the 6:05 hood, and this is what a 6:06 big benefit of NestJS Workflow is. 6:08 It gives us this Scalable architecture where we can scale 6:12 both within a single process and across multiple 6:16 due to our centralized journal inside of PostgreSQL. 6:20 So now the workflow module is fully configured 6:22 and ready to actually use. 6:25 The last thing we need to configure is the PostgreSQL 6:28 workflow store, and this is gonna be this journal I've been 6:32 referencing, 6:32 which is essentially how the workflow is gonna save its data. 6:37 To do this, we provide a custom provider 6:41 using the injection token called PostgreSQL workflow 6:44 store from NestJS workflows/PostgreSQL. 6:48 And this is gonna tell NestJS Workflows to use this 6:52 implementation when storing any metadata associated 6:56 with the workflows that we will be executing. 7:00 And this metadata that I'm referring to is the state of given 7:03 workflows so that they can be resumed, retried, 7:08 and run in a controlled, predictable manner. 7:11 We need to store this state 7:13 because these are long-running workflows, and we need to know their state at any 7:17 given point in time to actually orchestrate them, prevent 7:21 duplicates, and make sure they run to completion. 7:23 So to store that data, we are gonna provide 7:27 the PostgreSQL workflow store, 7:31 and we're gonna use useFactory, which allows us to inject dependencies. 7:35 So now inside of useFactory, we want to inject our 7:39 underlying database, our Drizzle ORM database 7:42 that we've set up. 7:43 Since this is how we're actually gonna interact with the PostgreSQL database in 7:47 our 7:47 application, make sure we import the type correctly. 7:50 And then we also are going to now add this workflow 7:54 storage dependency 7:56 from 7:57 NestJS Workflows. And this is a 8:00 dependency from NestJS Workflow that is a generic 8:04 container that provides the workflow storage. 8:09 And now what we return from useFactory is gonna be a 8:13 new 8:14 PostgreSQL workflow store. And this takes in 8:18 a configuration object where we have the executor. 8:22 And this executor is how we're actually going to translate 8:27 our s- in-memory storage, this workflow storage, into the 8:31 database. So here we have a special function called 8:35 fromDrizzle, again, coming from NestJS Workflows 8:38 PostgreSQL. And this is essentially an adapter layer that will 8:42 convert calls from our in-memory storage to 8:47 the PostgreSQL database using our Drizzle ORM instance. 8:51 All we have to do is pass in the raw Drizzle ORM instance, and 8:55 this is handled for us automatically. 8:57 We also pass through the in-memory workflow storage. 9:01 And finally, don't forget to add the inject array where 9:04 we actually 9:05 provide these dependencies. To provide these, 9:09 we will use getDrizzleToken, which is a NestJS function 9:13 from NestJS Drizzle that allows us to 9:16 get access to the 9:18 injection token for our Drizzle ORM instance, 9:22 which will inject the database. 9:24 And then for workflow storage, this is simply 9:28 WorkflowStorageType coming from the same 9:32 NestJS Workflows, which will inject the in-memory storage. 9:36 And so now we've successfully provided the PostgreSQL workflow store. 9:40 We've configured the Workflows module. 9:43 We have everything we actually need to start setting up the first workflow, 9:47 store the data associated with it, and configure this workflow to 9:51 process instances of it in parallel across 9:55 multiple instances of our app as well. So this is excellent. 9:58 In the next lecture, let's finally create our first workflow 10:01 and start using 10:02 this functionality.