How To Make Your API Idempotent To Stop Duplicate Requests

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
what will happen if a duplicate request reaches your API and what are the consequences of processing such a request you could solve all of this by implementing API item potency and I'm going to show you how in today's video item potency in a restful API means that if you send the same request to the API multiple times you're going to get back the same response and you're only going to trigger some side effect once so this is an important quality that we are striving for when we are implementing item potency and we're going to be adding it to our post endpoint for creating a product I'm going to start off by defining an item potent command that's the create product command will Implement so in the application folder I'm going to add a new folder which I will call item potency and inside of it I want to create a new class that's going to represent my item potent command I'm going to make this class public and abstract and it can actually even be a record and it's only going to have one thing on it which is going to represent the request ID for this current command it's going to implement the I request interface from mediator and now I'm going to update the create product command to inherit from the item potent command so I'm going to add the request ID as part of the create product command Constructor so that I can pass it to my item button command base class now the question becomes how am I going to provide the value for the request ID when creating the create product command a good solution is going to be to include the request ID in some sort of custom request header that you're going to be sending to the API I'm going to add another argument to our endpoint and it's going to be a string where representing the request ID and it's going to come from a header so I'm going to add a from header attribute and the header name is going to be X item potency key when someone is sending a request to my API they're going to need to include this header and I'm actually going to enforce that this value is a good otherwise you're going to get back a bad request so let's do something like this let's say if not good try parts and you're going to try to parse the request ID that we get from this item potency key header and you're going to move it into an out parameter which I will call first request ID so if you're not able to parse this then I'm sorry but you're going to get back a bad request so I'll say return results bad request otherwise we have our parse request ID and let's pass it to the command the from header attribute is complaining because I forgot to actually set the name property so now everything should be as expected the next thing I'm going to add is a pipeline Behavior also in the application project so I'm going to add the behaviors folder let's add a new class inside which I'm going to call our important command pipeline behavior I'm going to make it internal and sealed it has to be generic so the first argument is the T request the second argument is the P response and it implements the I pipeline Behavior interface I'm going to pass the request and the response as the generic arguments and the important thing is I'm going to pass in our generic constraint that the T request has to be an item potent command so now my pipeline is only going to be running when I'm sending an item potent command instance and let's implement this inside of the pipeline I want to check if a request with the given ID already exists then I break the pipeline otherwise I process the command as normal let's add one more abstraction inside of the potency folder which I will call I item potency service this interface is going to have two methods one is going to be a method returning a Boolean value where trouble call request exists async and it's going to have one argument which is the request ID the second method is also going to be asynchronous and I'm going to call it create request async this is going to be the method responsible for storing that this request has just occurred besides the request ID I'm also going to give it the name of the request that was just processed so if I head back to my item potent command pipeline behavior I'm going to inject this I item potency service from The Constructor and inside of my handle method which I will make asynchronous I can say this so if a weight item potency service request exists for this request ID then I need to return what should I return in this case well let's say I return whatever is the default response for the T response type now because my commands don't return a value this is going to be just void but if you are returning values from your commands then you need to consider what the default value should be if my precondition passes and the request does not exist then I'm going to go ahead and create it right away so I'm going to call Item potency service create request async and I'm going to pass it the request ID and I'll grab the name of the T request argument which is going to be the name of my command so I'm going to pass this to the create request method and create this in the database after this executes successfully I'll go ahead and run my command by saying await next and then I can return the response from my command the reason I'm creating a request before processing my command is that both of these operations could potentially fail I would much rather have my entire request fail because the command couldn't be processed rather than something like this where I let's say successfully process the command but creating the request fails and then I would have to retry my command where the command actually succeeded in the first place and it was the item potency service that caused the error with this setup in place let's register our pipeline behavior in the dependency injection class in the call to add mediator I'm going to register our pipeline Behavior by calling add open Behavior then I say type off and specify my pipeline Behavior type so now I can focus on implementing the item potency service and I'll head over into the persistence project so let's create an item potency folder because because I'll need to do more than just implement this service so I'll create a class which will hold my item potency service and let's make it internal and sealed and we need to implement the I item potency service interface I'm going to need a table in the database that's going to persist my requests and I'll also need an entity to represent this table so I'm going to call it hide important request it only needs to have a few properties so I'm going to say public with ID which is going to be our actual request ID that we sent to the API then I'm going to give it a name which is going to be the command name and let's include a date and time when it was created and I'm going to make it in the UTC time zone there should be enough information for my item potent request let's add an entity configuration so item potent request configuration and I'll make this internal and sealed we need to implement the I entity type configuration interface and specify the item potent request as the general argument so now I'm going to quickly configure this entity I'm going to say that the key is the item important request ID then for the name of this item Port request I'm going to say that it is our required property and for the date and time is going to be required by default let's also configure the table name so I'm going to say to table and I will call it item potent requests all right now all I need to do is to generate an EF core migration and execute it to create this table in the database so I'm going to Omit this step because I don't think it's necessary and let's head over into the item potency service so I'm going to inject the application DB context and we're going to use it to implement our methods so in the request exists async method we're going to say return context and I'll say set and access the item potent request database set and let's call the any async method to check if there is a request with this given ID of course I need to await this to return a Boolean value but this should satisfy our request and then in the create method let's also make it asynchronous let's create our item potent request entity I'm going to say new add important request let's set the ID to the request ID and the name to the name that we get from the route and for the date and time I can say date time UTC now now I need to add this to my database set I can just say context add and pass in the item PostNet request and I need to persist this immediately by calling save changes async so this is my item potency service it's pretty straightforward and I need to also register this with dependency injection so I'm going to say Services add scoped I'm going to use the generic overload and specify I item potency service and my item potency service implementation so with this in place we should be good to go and try out our item potent API implementation I'm first going to send the post request for creating a product to the API without including the item potency key header at all so if I send this request I'm going to get back a bad request saying that the required parameter request ID was not provided from a header the runtime is already protecting me from Bad requests if I head over to the headers tab here's the header and I'm going to include it in this request but I'm going to send an empty value so we hit our endpoint but the request ID is empty and we're going to fail while trying to parse it because we're not going to get back a valid quit so the correct approach would be to generate a unique grid on the client side included in this header and then pass it to our API if I send this request to the API we are going to pass the step where we are parsing the request ID and then we're going to send our Command so because this is an item potent command we are going to hit the breakpoint inside of our item potent command pipeline behavior and land in the handle method first we are going to check if a request with this ID exists so it doesn't exist and our method returns false so we're first going to create an item potent request instance after this succeeds we process the command and return back the response now I'm going to hit continue we land back in Postman and our request is successfully processed now I'm going to send the same request again and let's observe what happens we again hit the breakpoint in the endpoint itself we're not interested in this as much so I'm going to hit continue and we land in our item button pipeline Behavior so now the first check seeing if this request exists is going to return untrue and we're just going to return from our Pipeline and we're not going to be processing the command at all so if I click continue we get back a 200 okay response simulating that this request was processed successfully but nothing was changed in the server side state generating and passing in a valid item potency key is the responsibility of the color of the API you don't want to be thinking about this in your backend you just want to implement support for item potency and let your client think about generating the item potency key header one more thing I want to comment on is in the actual pipeline Behavior what's going to happen if you horizontally scale your API so you have multiple instances running but the duplicate request lands on a different instance assuming you're running multiple databases one for each API instance then it's going to be processed one more time so if you're working in an environment like that the persistence store for your item potent requests will have to be the same for all of your API instances so this is really important to consider I hope you enjoyed this video about implementing item potency in an API while leveraging the features that we have such as mediators pipeline Behavior make sure to smash that like button and until next time stay awesome
Info
Channel: Milan Jovanović
Views: 20,402
Rating: undefined out of 5
Keywords: idempotent, idempotent consumer, idempotent microservices, idempotent api, idempotent request, idempotent rest api, idempotent consumer pattern, idempotent api pattern, idempotent request pattern, idempotent request header, idempotent key, idempotency, api idempotency, idempotent prevent duplicate, idempotent duplicate request, idempotent api tutorial, idempotent request tutorial, api idempotency tutorial, idempotency tutorial, idempotent pipeline behavior
Id: smXAgcdJzLc
Channel Id: undefined
Length: 14min 26sec (866 seconds)
Published: Fri Jul 21 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.