What is an API and how do you design it? 🗒️✅

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
Hi everyone, this is GKCS, today we are talking about how do we design good APIs and and at the end of this video you should know about what an API is or what a good API is and how you can design it. So, at the end of this you should be a good software engineer when it comes to designing APIs Imagine you're writing down a library to sort integers. So a person comes to you and says that I want to buy this library from you but I don't know how to interact with this library an API is a documented way in which external consumers can understand how they can interact with your code not how your code works but how they can interact with your code to get their work done. let's take an example, you need to find all the admins from a given WhatsApp group so, what WhatsApp is going to expose is an API. And, what is that? It's a function such that, you need to call GetAdmins with the string type of groupID there's two possibilities of errors: first one is that the groupID that you're talking about does not exist, or the group has already been deleted. OK, depending on the logic, group is deleted may or may not be an error. and finally you get a response if things go well you get a response which is a list of Admin objects named admins The Admin object itself might be complicated, might be JSON inside, which is containing the name, user ID, and so on and so forth. So as you can see this is very similar to a function. You define a return type, you define possible errors, you define parameters you define the name of the function and that's what an API in general, it's a function that external people can call and, it's just a contract, not how you're going to do it but what it is going to do, that's the important bit here. There's a useful checklist of things to run through. The first thing is: where should this function be exposed so if you have a microservice architecture and you have a service which is a group service which handles everything related to groups that is where this API belongs, because this is something to do with finding all the admins of a particular group. OK so the first question that we asked ourselves is where does this belong? So, what does it do? It gets the admins, that is the function name So this is part of the what What do I need to pass it? The groupID. So these two are part of the result. Now there's a lot of mistakes that people make while designing APIs. The first thing is if you are saying getAdmins then it should return admins. You know if it's returning more than admins, if it's returning the groups that they belong to, then that API is named wrongly. It's no longer get admins it's get admins and all groups that they belong to. Right? Or something similar. So naming is important. The second issue could be that when you are asking for a group id maybe the caller has more than that. Maybe they have the list of admins, the list of user IDs that they want to check for. OK? The person calling getAdmins just wants to check whether the admin list that they have is the same as what the server has. So in these cases, should you also allow an additional parameter of list of users. OK? This depends on the context, but it no longer becomes get admins for a group, it becomes check admins for this group and this user list. So again the naming will be important, but more than anything the action that you're doing should define the name and should define the parameters. So don't take additional parameters unless absolutely necessary. Now there is one case where you can forgive people requiring additional parameters, and that's when you need to optimize your API. For example, I'm doing a get admins of the group service and I'm passing in a group ID. Maybe the group service needs to authenticate this call Maybe the group service needs to find out the admins of another group. I'm just imagining things over here, but maybe it needs to make four or five queries to different microservices, which is expensive because those are IO calls. If you can optimize that using some additional parameters over here, you know, basically giving my service more information so that I don't need to ask other services all that information, It might be forgiveable When is that ok? When you really need speed on this API and you're being crushed by the number of calls that you're getting and the number of calls that you're making. In that case maybe some additional parameters are useful. Even then I would strongly suggest that the name of the API needs to be changed but sometimes that can't be done easily because you have set one name Similar to the parameter list is the response that we have A lot of people in the response stuff a lot of things and give it to the caller in the hope that they won't need to change the response object later on, when the caller asks for more things. But this is a bad API design, it's giving more information than the caller needs, so that's more network requirement. But, more than anything it's confusing, because you never know what will come in the future, and building a big response just because, to try to make it extensible is a bad idea The final bit is a little interesting, it depends on personality and that is the number of errors that you define. So I've seen people defining all the errors possible for their API and I've seen people designing absolutely nothing, taking no responsibility and just returning a generic error for everything that happens. For example, when a person passes in a group ID and you expect it to be an integer, you can just define the data type to be an integer, you don't need to check whether the string is going to be an integer and then throw an error when group is not an integer Similarly, if the group id is too long the string is too long, and your database query fails because of that, maybe it has a particular length of string that it can put in, that's not my responsibility, I'm not going to be returning group ID too long I'm just going to fail the query, right? and the person who's calling the API can figure out whether I mean why did they send such a long string. All of these things of course are subjective, but like I said extremes are always odd, so taking absolutely no responsibility where if the group does not exist then I just throw a 404 error, that's wrong, because this is an expected error from your service similarly the group is deleted is an expected error so in general when you're designing errors, think about the common expectations and think about the responsibilities that your API has. A lot times we need to expose our API on an HTTP endpoint and in those cases we need to understand how HTTP response and request objects usually work So the first thing we need to design is where is this API going to be So this is the address of your site, this is going to be, let's say the model thing that actually contains these functions then you go to which function needs to be called and that is getAdmins, and I've put in a V1 over here in case you want to change the version on that function, you know you make updates on the same function you can call it V2, so this is necessary not necessary not really but this is the routing so the routing, or the Where question is answered over here now what does it do well it does a POST query on this So, get me the admins who have group ID 123, so this is like filtering group ID is a JSON object that we have, this is the HTTP request object we have passed in through the browser and the response will also be through the browse, and that is admins which is an array, JSON array of admins, ID and name. So this is how we can think about our normal functions, programming functions into this. Now let's try to analyze this. Can this be done using a get function? Absolutely. I could have passed in 123 in the getAdmins function over here, and that would work the exact same way except that this thing instead of POST I could have made it GET so this would have been slightly shorter because there was no payload, request payload to be sent in the second thing to remember is not to mix up routing with the action so, for example, you go to chat messaging instead of saying get admins, you immediately say here is the request with the group ID, now figure out what you need to do The problem here is that this is going to be highly ambiguous, it doesn't know whether this group ID has to be fetched in which case you need to give the group information it doesn't know whether the admins have to be fetched it doesn't know whether members have to be fetched and a lot of times what people do is they try to put in the routing information or the function information over here So they say that action, or whatever you'd like to call it, is get admins Don't mix that up, it's possible to do it this way too yeah, but it's going to be a very dirty API Another interesting thing to note in getAdmins, is that it's it's saying get which is an action that HTTP already gives you So if GET is defined in HTTP maybe you don't need to make this redundant statement again, you can just say Admins. So Chat Messaging, you have the admins with a parameter that is groupID equal to 123. Now let's get into more subjective and major issues we have when designing APIs We have this function that is setAdmins, it takes a list of admins, and it has a group ID in which it's going to set these guys, as administrators What if, let's go for the bad cases as usual, What if these admins are not members of this group In general, this should return an error, saying that this person is not a member, so this person cannot be an admin However, depending on the use case, you might expect to add a member, and then make them an admin Similarly, what if the group doesn't exist? Well, create the group, and then make these people admins of the group. That's also a possible expectation, I don't say it's a good API design, but it's possible In these cases what's happening is there's a side effect, you're saying set admins but you're also doing much more than that, you're also the members sometimes and sometimes you're setting the groups also. So, your API should have no side effects. If there's multiple operations that your single API's doing, if there's a lot of flags that you're setting if there's this Config object that you're passing into the API and which is setting it's behavior, that's a really weird API and maybe you should break it into multiple APIs So that's the first reason of side effects, that is doing everything in one function, the second more important reason for a side effect is atomicity, when I say set admins, I need to create a group and what if I pass in set admins, it fails, then I create a group and who's going to set the admins what if I create the group and somebody else set admins, and now these guys can't be admins Depending on the use case you might need atomicity, and that is the second case for side effects, however even with atomicity, I'm doing everything the naming should definitely be better, so there's no excuse for poor naming of a function To an extent, atomicity can be mitigated, because you want an action to happen one after another, so you can in this microservice itself, set the admins, if it fails, then ask the person to make a create group call let's say the group doesn't exist, you say 404 group does not exist, now the client makes a create group call with some members passed in so list of members, and now you can set that these guys as admins if you like so list of members, a list of admins and over here you can bring in the atomicity instead of this So, in general when you need atomicity, for the action that you're doing, try to call the right action instead of something which is more generic because that really pollutes the API, and there's a lot of confusion testing the API's going to be extremely difficult but more than anything the person who's calling the API doesn't really know what is going to happen, make them admins is going to make them members, who knows. Now let's think of the function for getting all the members of a group So that is 200 members, each member has their own profile, that might be pretty detailed, and so effectively what's happened is that the response is verylarge. so one of the things you can think of, over there, is to break the response into pieces, and there's two ways to do this, the first one gives responsibility to the client, and that is pagination, you have 200 members, get me the first 10 then get me the next 10, and so on and so forth, so this kind of breaks the property of stateless, maybe not really, you give an offset, and the server figures out which are the next ten users So pagination is one way, the second way is to fragment the API. So usually the problem is not on HTTP, it's on the internal protocol that you're using, when you're talking to, from server to server from microservice to microservice, it's going to be a short response most cases, and when it is a large response, it's an exception, so when there's an exception there's usually a problem now what you can do is because your response is limited to let's say 1 kb or 10 kb whatever may be the case and if your response is bigger than this, 10 kb, you can break the response into multiple pieces and give it to the next service, one by one, with a number so the TCP number will be 10 the next one will be 11 next one will be 12 and so on and so forth, so in this way the other service knows there is more information to come there's fragments to come and finally there'll be an end packet which is going to say say that's it that's the response ending so fragmentation and pagination are pretty important when the response of the API is huge and you need to break it into pieces. The final two things that I want to talk about are pretty interesting. The first one is how consistent do you want your data, so I say get admins for this group, you got here, you read from the database, but while you're reading in the database someone came and added a new admin, so you read two admins but there are actually three now you did not get a consistent view of the world you do not get three admins, but do you really care and that's an important question when designing APIs, do you want perfect consistency, that's going to be slow, Another example is I ask you for the admins and you cache it, so if you cache the information already if you cache the response, that's going to be slightly older but maybe you don't care, maybe you want all the comments for a particular post and you don't care about the comments which have happened in the past 3 seconds, you just want general comments for a post so is there a cache, or is there not a cache, that is the question If you have a lot of load on your database, maybe you want to start serving requests from cache. If you have a lot of load in the database, maybe you want to reduce the response size that you have. Instead of passing in the whole profile with lots of information maybe the profile picture information and so on and so forth you just pass in the name of the person, right? That is service degradation, giving the essentials, still responding, without completely thrashing so this is what an API design looks like so if you have any comments or suggestions you can leave in the comment below and if like the video then make sure to hit the like button also if you want notification for further videos hit the subscribe button I'll see you next time
Info
Channel: Gaurav Sen
Views: 416,567
Rating: 4.8844171 out of 5
Keywords: system design, interview preparation, interviews, software interview, design interview, programming interview, gaurav sen, api design, application programmable interface, application programming interface, what is api, what is an api, api parameters, http api, api endpoint, http endpoint
Id: _YlYuNMTCc8
Channel Id: undefined
Length: 15min 26sec (926 seconds)
Published: Sun Mar 17 2019
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.