In this chapter, we're going to continue our work on executing nodes by fixing some issues we discovered in the previous chapter. Let's start by fixing the CodeRabbit reported issue about our missing content type header and let's discuss the cyclic error message that we decided to look for whenever toposort is happening. So I'm going to go ahead and open the previous pull request right here and this is the first suggestion and it is completely valid We have forgot to add headers to our post put patch request. So let's go ahead and do that to ensure that our HTTP request node can properly make those requests. So what we have to do is we have to find executor.ts inside of features executions components HTTP request folder.
So let me show you how that looks right here. Features executions components HTTP request executor.ts. And in here when we decide that this will be a post put or patch request, besides filling the body, we also have to do options dot headers. And let's do this properly. Options dot headers, and we have to add content dash type and in the type will be the exact this one that we are querying for later.
There we go. This will ensure that our post put or patch request doesn't get rejected because headers are an important part of an HTTP request of course. Great. So that's one thing resolved from our previous pull request here. There are some other things here such as validate node types and data at runtime.
Now depending on your personal preference you can of course do this. This is a good advice but let me show you exactly where this is happening. So this is happening inside of our source folder inside of ingest functions.ts. So basically what it's telling us to do is the following. You can see that when we pass this node.type, by default, it doesn't have a proper node type.
So we have to cast it as such, right? Or maybe we don't even, yeah, you can see that. Okay, it looks like it's working. But still, CodeRabbit is telling us to check at runtime if it's actually a part of node type using this, and then throw an error if it's not. Here's why we don't have to do that.
So first things first, I had no idea that we don't have to cast it as node type. So I'll have to retrace my steps to like fully confirm that. But just for now, I don't want to change the code I've written because I don't want you to have any errors, right? So if you have this, leave it just in case. And if you go inside of getExecutor here, you can see what happens.
So if that type, which is a node type, does not match to whatever we define in our executor registry, we're going to throw an error. So you don't really have to worry about node type not being compatible. So that's why I chose not to do this suggestion right here. But of course, depending on your preference, you might think this is a great runtime validation. So you might do it.
Same goes from data, which is later retrieved from the executor constant. For now, I'm going to leave this as is. And now let's go ahead and discuss the other problem that we have. And that is key collision. So what is key collision?
In our previous example, we had this type of schema, right? Very simple. When I click execute, I'm going to get this and then I'm going to get that in my results. So if I go ahead and visit my let me go ahead and just find my ingest server here 8288 so I suggest you go here to having some trouble clicking on it 8288 here we go completed And if you go inside of finalization here, you will see that the result simply says HTTP response with data of ID one inside. That's pretty straightforward, right?
Because we have an HTTP request which request to do is write an id of 1. If I change this to do 2 and if I click save here and then if I click execute workflow again I'm going to have another request here finished and this time this request's finalization will be different it will include the id of 2. Perfect! But What if I do this? What if I add another HTTP request right after this one?
And if I change this, let's make this to be to-do 1, click save. And change this to be to-do 2, and click save. Save the entire workflow, and then click execute again. So what's going to happen now? So I will have, as you can see, two HTTP requests fired.
But in the finalization block, you will see that I only have one HTTP response. Only ID 2. Which means that this one was completely overridden. You can see that and prove that by going inside of these individual ones. You can see that the first HTTP request topologically called the to-do with id of 1, but the second one called with to-do 2.
But both of them have the same value here, HTTP response, which means that in the finalization block, only the last one gets written. That's because we have a key collision. So now let's go ahead and fix that. And yes, there is this thing right here. So cyclic error message.
So CodeRabbit told us to double check if that's the correct one. So instead of my utils in the source ingest folder, yes, this is correct. I went into the source code and I can confirm that this toposort library here throws an error message which includes the word cyclic, not cycle. So this is correct. Just wanted to resolve that because I think I promised that I will resolve it.
And now we have to fix this bug that we have, right? Basically, it's not allowing us to chain our HTTP requests. In order to do that, we're going to have to introduce a new field to UI. So each of our nodes, besides having a method, endpoint URL, right, those specific things for itself, It's also going to need to have a variable name and I suggest we add that as the first field in every node that we have because it's going to be a very important one and it's always going to be required. So let's start from the UI side and work our way into the executor and finally assigning it to the context.
The first component I want to go in is node.tsx. So let's find it together so we refresh our knowledge. It is inside of source features executions components HTTP request node.tsx. So besides having an endpoint method and the body, we're also now going to have variable name like this. The reason we are making this optional is not because it's optional, it's because it will not be added at some point, right?
So we cannot always rely that this will exist. It will be required for executing the node, but it will not be required as the initial node, if that makes sense, right? So user will have to add this later. And once we have this variable name, what we can do with it is, well, we don't have to worry about it too much because you can see it's automatically going to be spread here it will automatically be passed here in the default values because we refactored our code previously but maybe we can improve the description so the user can visually see for example right here in this HTTP request node perhaps we can add some kind of variable name here. That's like one idea I have, I'm not sure.
Maybe later, let's leave it like this for now and then we'll see. Now that we have the variable name we can go ahead and safely go instead of dialog.tsx and now we have to modify our form schema. So our form schema now needs a new field called variable name. Let me fix it. Variable name is going to be a type of string.
Now, we can just leave it to be any type of string, but it needs to be a type of string that will be compatible with a JavaScript object, right? So it should not be able to be something like this. I think, I think this might not be valid or maybe it is because of white space is valid. But basically we wanna make sure that whatever uses rights inside will not throw any runtime errors when assigned to the key value of an object. So because of that, we're going to add some regex to validate that.
So z.string like this. After string, let's go ahead and make it required. Let's give it a message variable name is required and then let's add a regex. So I'm just going to copy the regex so you don't have to see me type it here And let's go ahead and make sure that we have an error message. If it's not applied, variable name must start with a letter or underscore and contain only letters, numbers and underscores like this.
So this isn't an unknown regex, you can find it quite easily on Google. Basically it does exactly what the message says. It allows capital and non-capital letters as well as numbers from 0 to 9 with the exception of a dollar sign because that's also valid to be inside of a JSON object. So I think this is good enough. You can, of course, for yourself research if you have a better regex, but I think this will work just as well.
Now let's go ahead and let's add it here in the default values here. Variable name will be default values.variableName or an empty string. Same thing in the form reset, default values, variable name or an empty string. And now what we have to do is we have to add a form field for it. So let me try and find the simplest one.
I think that's this one, the endpoint URL. I'm just going to copy it because it's very simple and I'm going to add it above the first form field because we said we want this to be the first one. I'm going to change the name to be variable name and I'm going to change this to be variable name and the placeholder can be whatever we want for example my API call basically allowing the user to know hey you can name this whatever you want and in the form description Let's go ahead and describe something like this. Use this name to reference the result in other nodes. Then I'm using this to create a space and then I open an object so that I can do double curly brackets because that's how we are going to do templating.
For example, my API call dot HTTP response dot data and the placeholder here is the same. So if user names this test later in other nodes they will be able to do test dot HTTP response dot data. Perfect. So now once we do that if you go ahead and open an HTTP request you should see a variable name. And I just got a really cool idea.
If we change the variable name, maybe we could also change the description to make it even clearer to your users what this will be used for. Let's see if I can do it very quickly. So just as I did this, let me go ahead and do const watch variable name form watch variable name like this. And then in here in the form description. Let's see if I change this to backticks.
Whoops. Change this to backticks. Change this to backticks. And if I specifically do this, watch variable name. And maybe make this fall back to my ABI call.
So it looks better. There we go. Exactly what the variable name is. That's what the user will see. So it will be easier for them to understand how they're going to use this later in other nodes.
Right. So test or if nothing is written it's just going to use the placeholder one. I think it's cool. And if I save it should give me an error because variable name is required. So let's go ahead and call this my API call one like this and let's click save and let's do this one my API call to and let's click save there we go so now we have different variable names for two identical nodes with of course different endpoint URLs.
So what we have to do now is we have to use that variable name inside of the actual executor. So first things first, well first things first and well last things last, we have to go to the executor. So features, executions, components, HTTP request, executor.ts. And now let's consider the HTTP request data and let's go ahead and let's add the variable name. Again it's going to be an optional string.
And what we're going to do is well you can define how strict you want to be with this. I would personally be as strict as if we were missing an endpoint right if there is no variable name it's a non retryable error right so let's remove this to do right here. Actually no yes we also have to publish the error state for this. So non-retriable error variable name not configured like this. And now let's go all the way down to the return method and we have to slightly modify it so let's go ahead and do const response payload and let's go ahead and copy the HTTP response from here so basically our old payload exactly as it is And now we're just going to slightly modify this by spreading the context.
And instead of storing this under HTTP response, let's store it under data.variableName. And the response payload inside. There we go. So let's see. A computed property name must be a type of string number symbol or any data dot variable name.
I was expecting this to take care of that. Maybe I should also check for string. Let me just just for my curiosity if I do if data.variable name right here does that fix the issue that seems to fix the issue. Okay. Let me just pause the video just a little bit and see what is the best way to handle this.
Here is a potential solution that we can do. We could just... We can do both actually. Let's do if data.variable name like this and then return like that. And we can also do another return here, which can be served as a fallback to direct HTTP response for backwards compatibility.
And let's just go ahead and do what we used to do so context and then append the response payload like this. And let's just try it out for now. So I think this should work now. So let's see everything we changed in executor.ts is we checked if we don't have the variable name and we throw an error because it's now required. And we changed another check here if we have it simply because this type error is being a little...
It's giving us a hard time basically. So I made sure to wrap that instead of another if clause here so I can safely return that with that variable name. But if Somehow all of this validation fails or something. Let's just fall back to what we have right now, even though it doesn't work as well as we want it to. But I think that now this should work okay.
I think I should be able to save this because we can extend individual know the data without extending the schema. That's another cool thing about React flow and the fact that we store this in a JSON object. So I think that now the situation should be different. Let's click execute workflow and let's follow it again. So it is running and there we go.
You can see that now we have a my API call one with its own HTTP response and then we have another my API call one but here it is thankfully my API call two. Perfect. It's just that this one was there first so I thought something had happened and here is the final result the result object now has myAPICall1 with HTTP response and data of id 1 and down here my API call 2 with HTTP response data and id of 2. And this way we resolve our problem of key collision. So we have added the variable name to the UI and we added the variable name inside of the context.
I'm not too happy about this part right here that I have to check and this fallback because this fallback really doesn't make any sense because it's never going to get to that, right? If we somehow remove the variable name, it will just throw an error. And honestly, since we are still in development phase, we don't really need to offer any fallback. But this currently is a solution to not have that type error here. For the next chapter, I might research a bit more into it and how I can give you a prettier solution so that we can simply validate our variable name here.
Perhaps I also have to add and if type of let's see and if type of data variable name is equal to string Maybe that's what I had to do. And then if I remove this. Yeah, no. OK. Maybe I'm missing something very obvious.
So I will make sure to take a deeper look into this in the next chapter and tell you if there is a prettier solution to this. But as you can see as I just tested we officially fixed our overriding problem. So let's go ahead and quickly go over these three files that we have modified. So I think we actually started with the executor, right? And the only thing we did here first was we added options.headers, make sure you have that, make sure you didn't misspell any key and value there.
Then we went into node.tsx where we very simply extended with a new variable name and I don't think we modified anything else. If you want to you can add it into your description so that users can visually see what variable that is. You can add it right like before the get method, whatever you prefer. And then we went into the dialog, we added a variable name with some regex, which basically allows A to Z letters, lowercase and uppercase, dollar sign and numeric variables. Perfect.
And we added that new form field and a cool little watch util that will directly show the user how they can use that in other nodes. Perfect. So what I want to do now is open a pull request and merge those changes. Let's go ahead and open a new branch. Let's call it 19 node variables.
Let's go ahead and click on stage all changes. 19 node variables. Let's go ahead and click commit and let's click publish branch. Now let's go ahead and open a pull request. And since this was a fairly simple pull request, I don't think we have to go through the entire review simply because it's just three files, some quick fixes I want to resolve before moving on to other chapters.
I'm going to immediately merge this pull request and let's focus on adding more interesting nodes finally. So we pushed to GitHub, we created a new branch, a new PR, and we reviewed locally right here all the files. Of course, you can go through CodeRabbit's review for this one. I just think it's a very short pull request. So now let's go ahead and change this branch back to main and let's go ahead and click on synchronize changes right here and let's click ok and after that as always I like to confirm by going inside of my source control tab clicking inside of graph and I want to make sure that 19 is the newest thing I just merged and it's just those three files.
Amazing. That means everything is right and see you in the next chapter.