back to index

API Series #2 - Building an API with Flask in Python


Chapters

0:0
1:52 Insomnia
3:34 Initialize Our Flask Api
5:1 Add Resource
9:7 Get Request
11:25 Restart the Api
13:26 Get Data from the User
17:36 Post Request
24:48 Delete Function

Whisper Transcript | Transcript Only Page

00:00:00.000 | In this video, we are going to explore how we can put together a pretty simple API using Flask.
00:00:07.360 | Now, what we're going to do is go through and build essentially what you can see here. So,
00:00:13.600 | all of this code here is a single API. We're not going to go through all of it in this one video,
00:00:19.760 | but we're going to go through the essentials, and we're going to
00:00:22.960 | step by step put an API together that is going to look very similar to this.
00:00:28.160 | Now, what this API is doing is pulling information from these two CSV files,
00:00:33.840 | and either presenting that information to us, that data, allowing us to modify,
00:00:40.320 | allowing us to add new entries, or allowing us to delete it.
00:00:44.160 | So, we get to use get, post, put, and delete methods. Now, if you are following along with
00:00:53.680 | this tutorial, you'll probably want this data. So, you can get it using this code over here.
00:01:01.360 | So, over here, we have, I'll make sure there's a link to this in the description so you can get it.
00:01:07.600 | All we're doing is downloading the code, or those two data files from here, and then storing it
00:01:14.800 | in a local directory. I mean, you can change that directory to whatever you want, of course.
00:01:23.120 | Inside those two files, we have locations here, which is just a list of, I think these are
00:01:31.600 | actually real coffee cafes, but I made this CSV quite a long time ago, so I'm not 100% sure.
00:01:40.720 | And then, over here, we just have some made-up user data as well.
00:01:43.920 | And the end result will be something like this. So, this is a program that I'm working on,
00:01:51.120 | so this is a program called Insomnia. I'll leave a download link to that in the description,
00:01:56.560 | but it just allows us to send API requests very easily. So, I'm going to send an API request to
00:02:02.720 | this address here, which is the API I just showed you, and it's a user's endpoint. We will have
00:02:09.520 | two endpoints, which are essentially just separations of the API. So, users, and that's
00:02:16.160 | going to return all the user's data, and then we also have locations. And we'll send that as well,
00:02:24.640 | and we return all the locations. Now, we're going to start from scratch, and the first thing that
00:02:30.560 | we'll need to do is import everything we need. So, we will need to import Flask. So, from Flask,
00:02:37.360 | import Flask. We'll also need to import Flask RESTful. So, this is another Flask library that
00:02:47.760 | gives us a few very useful tools for building an API. So, right from Flask RESTful, import resource
00:02:59.680 | API and REC pass. Now, specific to what we're doing here, we will also want to import pandas
00:03:10.800 | and import AST. Now, you will probably have to pip install these as well. So,
00:03:20.080 | the pip for those is pip install Flask and Flask RESTful. Nothing weird there.
00:03:27.760 | All right. So, the first thing we need to do is initialize our Flask app and then initialize our
00:03:35.520 | Flask API. So, we do that. So, this is typical Flask here. So, we write Flask name, and then the
00:03:45.280 | API is API equals API app. Okay? Super easy. Now, we already touched on it, but our app is going to
00:03:58.480 | have two different endpoints. So, we're going to have the user's endpoint, and we're also going
00:04:05.120 | to have the location's endpoint. And we're going to go through building the user's endpoint, but
00:04:13.920 | you'll be able to find the code for both of those in the description. But the way that we separate
00:04:20.000 | both of these is we use a class object. So, we're going to create our use class first. So, we write
00:04:26.000 | class users. And then to initialize this as a endpoint for our API, we actually need to pass
00:04:35.600 | the resource object into it. Now, we'll inherit the resource object and expose it to different
00:04:44.720 | like HTTP requests, like get posts and so on. For now, we're not going to add anything in there.
00:04:51.680 | But to make sure that endpoint is added to our API, we need to write API,
00:05:01.520 | add resource, and then we need to specify the web address of this or the
00:05:10.000 | almost like a page of a web address that the resource or endpoint will be at. So, we'll say
00:05:21.280 | users, which is our class, and then the actual endpoint location, which is users. Okay? So,
00:05:29.600 | this is saying I want you to map the class users here to this place in our API. So, our web address.
00:05:40.320 | So, if, for example, the API was located at, let's say, API.com,
00:05:47.200 | this resource would be located at API.com/users. Okay?
00:05:56.080 | And we'll also create another one, although we're not going to fill it out in this video,
00:05:59.920 | that will be called locations. Again, exactly the same thing again. Resource,
00:06:06.560 | we'll just pass for now. And we'll copy that and do the same for locations.
00:06:20.720 | Okay. Now, that's sort of the structure or the very high level structure of our API itself.
00:06:30.000 | But we so, what I like to do is whilst we are building or writing the code for our API,
00:06:38.960 | I like to also test it as we go along. I think it makes things a lot easier. So,
00:06:46.800 | to run our API, we need to write this. If name is equal to main,
00:06:54.800 | we write app.run. Okay? It's that easy. Now, what we can do is just press the execute button up here.
00:07:09.040 | Okay? Okay. So, we got this error because I, okay. I don't know why I did that. So, this is,
00:07:18.880 | this needs to be the class, not a string of the class. Okay.
00:07:25.920 | So, now let's run that again. It should work. Okay. So, we get this. This is a development
00:07:33.760 | server. Do not use it in production environment. That's fine because we're just testing it.
00:07:38.800 | And then we can see this is running at this address. Okay. So, if we take this,
00:07:44.880 | we'll copy it and we're going to take it over into Insomnia.
00:07:49.040 | Okay. And I'll just clear everything that I was doing before and we'll go into here. Okay. And
00:08:01.760 | if we send this, we won't return anything yet. So, we'll just get this not found. But
00:08:09.520 | if we go to users, we should at least return this internal server error. So, this means that,
00:08:19.360 | okay, we are not getting anything back because there's an error on server side. So, in our API.
00:08:25.440 | But there is something there. So, before when we just had this. So, remember, we have those two
00:08:32.880 | endpoints. We're not actually using the base URL. So, there's not actually anything there.
00:08:38.560 | We get this not found. Because there just isn't anything there. If we type in locations or users,
00:08:44.880 | we will return this internal server. Now, we're getting this because we haven't written any code.
00:08:51.760 | So, it doesn't know what to do when we send that request. So, let's go back to our code and we'll
00:08:57.920 | start writing something. So, first thing I want to do is we'll define a get request. So, write
00:09:10.000 | find get plus self. And in here, what I want to do is load the user's CSV. So, to get that, I'm
00:09:22.720 | going to write user's path. So, this is the specific directory that I have stored my user's
00:09:35.360 | data. Of course, it might be different for you. And what I'm going to do is down here, I'm going
00:09:49.120 | to use pandas to read in that data. Whenever I call this get request, I'm going to read it in.
00:09:55.040 | So, this is similar to if we, for example, had a database on a server somewhere, when the API
00:10:03.360 | gets a request, it would read that data quickly and return it to us. So, this is a very simple
00:10:10.240 | version of that. So, I'm going to do data equals pd.read_csv. And we just go user's path. And
00:10:18.320 | hopefully, that's the right path. I think it is. We can see. So, if I come up to here, yes,
00:10:25.440 | we have data. So, I guess that should be fine. User's CSV. Cool. And then what we want to do,
00:10:36.080 | we can't pass a pandas dataframe object through an API. So, we need to convert this into a
00:10:45.600 | dictionary. So, we write data.to_date. Now, what we want to do is return. We want to return the
00:10:54.240 | data. So, we're going to write data, which is equal to data, our dictionary. And we're going
00:10:59.360 | to return the 200 code, which means the API request was successful. Here's your data. Okay.
00:11:07.440 | I'll save that. And I believe it should update automatically. Let me check. So, come over here.
00:11:20.480 | Let's send that again to users. Okay. No. So, I think we need to restart the API.
00:11:27.280 | So, come over here. Here, I'm just going to control C and run it again.
00:11:45.040 | Now, you can turn debug mode on. So, I think that will automatically reload everything.
00:11:53.600 | So, let's do that first. So, down here in app run, we're just going to set debug
00:12:00.000 | equals true. Save that. I'm going to press control C down here and execute again.
00:12:07.840 | Now, when we make changes, it should reload automatically.
00:12:13.120 | So, come over here. Let's send that request again. Now, we see user path is not defined. So,
00:12:27.040 | see why that is wrong. So, it's user's path. So, I'll save that and that should reload automatically.
00:12:41.120 | Let's try again. Okay. Perfect. So, now we get a 200 response there. We get all of our user's
00:12:48.080 | data. Okay. So, we're just returning that data frame in a dictionary format. Okay. Cool. So,
00:12:53.520 | that's our get request. Let's move on to let's put requests, I think. So, adding more data.
00:13:01.920 | Okay. So, this time, what we're going to need to do
00:13:07.200 | is we're using a post request. So, I put a second ago, I meant posts. And in here, the first thing
00:13:14.960 | we need to do is we need to retrieve information from the user that they want to upload.
00:13:20.320 | So, the first thing we need to think about here is we are trying to get data from the user. So,
00:13:30.720 | they are going to send us like a new entry. So, what did we have before in our users?
00:13:37.280 | We have user ID, name, city, locations. Okay. So, we want the user to be able to add a new
00:13:48.720 | row. So, each one is essentially a row in a data frame. So, we have locations, city, name,
00:13:55.920 | and user ID. Now, we want the user to be able to add locations or location, locations,
00:14:11.200 | name, and city. Now, the I think the user ID, we don't really want the well,
00:14:24.480 | the user isn't going to add that. That's going to be generated automatically. So, we ignore that,
00:14:29.280 | but we do want the user to be able to pass those three things. Now,
00:14:33.920 | to do that, we're going to use the REC parser. So, request parse that we use up here.
00:14:43.280 | And to initialize that, we need to write parser equals request. Sorry, why is it up here? REC
00:14:51.920 | parse, sorry. REC parse dot request parser here. It initializes our parser. And what this is going
00:15:00.480 | to do is when we add arguments into the request, this is going to read them. And what we're going
00:15:08.960 | to do is parse them out into variables. So, we are going to within our within this request,
00:15:18.240 | we want to allow those three. So, location, name, and city. So, we write parser, add argument.
00:15:28.400 | This one will be location. And we're just going to do one location at a time.
00:15:34.560 | So, do location ID. And then, of course, we use a put request later if they want to add more
00:15:42.960 | locations. We say required is true. They do need to add one of these. And the type of this is a
00:15:53.280 | integer. So, we'll write integer. Okay. We need to add another one. I'm just going to copy this.
00:16:00.320 | So, let's copy here. Here. And we have name and city. Name, city. And the type is for both of
00:16:14.240 | those strings. Which I think might be the default. So, we probably don't even need to include that
00:16:21.520 | type string there. But it is there. So, we'll leave it. So, now we have the three arguments
00:16:30.320 | that a user can use to post information to our API. And what we need to do to extract whatever
00:16:37.920 | the user has sent us is we do this. We're going to extract them into this dictionary here, which
00:16:43.840 | is called args. And we write parser. Like that. Okay? So, now we have those arguments. And let's
00:16:53.280 | first just make sure this is working. So, I'm going to return what we just tried to send that.
00:17:01.280 | So, let's return location. Loc equals args location ID. Name.
00:17:14.480 | And the city. Okay. So, let's just make sure it's actually working.
00:17:26.880 | And we'll say 200. Save it. And that should reload automatically. Let's go over to Insomnia.
00:17:33.360 | And we are sending a post request here. So, post. We have users. We enter this question mark.
00:17:43.040 | So, this allows us to start adding our parameters. So, location ID is going to be equal to I'm going
00:17:53.200 | to say 5. And so, we added the ampersand symbol there to add another parameter. So, location ID
00:18:02.480 | and what are the other ones? User. User. Or name.
00:18:13.920 | James. That's me. And for the city, let's put London. Okay. Send that. Cool. And then we're
00:18:27.200 | just returning that request back to Azure. You notice here we put lock, not location ID. So,
00:18:32.400 | you can tell it's not just returning what we sent. It's returning what we have processed
00:18:37.040 | through our function and returned back to ourselves. Okay. Cool. So, we know we're
00:18:42.400 | passing those arguments correctly. Now, let's use them.
00:18:46.880 | So, again, what I want to do is read our data.
00:19:01.920 | So, we do data equals data pandas.read CSV, again. And we're reading the user's path.
00:19:12.880 | Okay. So, user's path. And we're doing users here. So, actually,
00:19:21.520 | do we want location in there at all? I don't know.
00:19:23.680 | Yeah, maybe. So, no. Let's say the user can specify that. User ID. We're not returning
00:19:43.200 | anymore. So, I want the user to I want to be able to post this. Because then if we have a duplicate
00:19:52.960 | of a previous user ID, we're not we can say we can check if that already exists in the data or not.
00:19:59.680 | Which we'll do now. So, if args user ID. In the data that we just loaded. So, in data is it user
00:20:13.360 | ID, I think? I'm sure it must be. Okay. I think it is. I want to return an error message saying
00:20:24.800 | this already exists. So, write message. And all we do is write args user ID. Already exists.
00:20:37.920 | And what we'll return here is the 409 code, which indicates that there's some sort of conflict.
00:20:45.600 | And there is a conflict. We already this user ID already exists. So, we're saying there's a
00:20:52.480 | conflict. You can't create a new one. It's already there. If it doesn't already exist,
00:20:58.320 | that's great. And we go ahead and we create that new user ID.
00:21:02.160 | So, what we do is write data equals data.append. And then we're just appending the new data that
00:21:10.640 | we've retrieved from our args. So, it's going to be we need user ID. Which is going to be args user
00:21:20.480 | ID. Let me copy this and make it a little bit quicker. For locations, it's just empty for now.
00:21:35.840 | We have city and we have name. Okay. And then we need to I think we need to
00:21:47.280 | ignore. Yes. Ignore index. True. True. Sorry. Okay. And then we'll save that data.
00:21:55.200 | So, two CSV. It is user's path. And I'm going to say index equals false.
00:22:04.800 | So, we're not saving the row numbers. And then I just want to return that data.
00:22:14.880 | Again, we can't return a data frame. We need to convert it into a dictionary. So, write to dict.
00:22:20.800 | And it's a 200 response there. I'm going to save that. Let's test it. See if it works.
00:22:29.440 | So, I'm going to let's see. Modify this to user ID now.
00:22:41.280 | Send that. Internal server error. So, let's see. Date frame, no attribute to date. So,
00:22:48.960 | I need to add an underscore. Save again. And let's send that again. Okay. So, now we can see
00:23:03.680 | we have more items in there. The only thing is I've added five for all of them. Oh, did I?
00:23:12.240 | It's in the code, right?
00:23:14.640 | What did I do? Yeah. Very smart. Okay. So, change that name. And this is city.
00:23:27.840 | And one other thing is that these are all this should also be a string.
00:23:31.440 | One thing I noticed. And this should also be a string. I'm going to leave it for now. It's fine.
00:23:37.040 | So, let me send that again. Five already exists. So, at least we know that our conflict
00:23:48.800 | code works. So, let's create another one. Number six. I'm going to call this, I don't know, Jim.
00:23:55.600 | Let's go again. Okay. So, now we've created number six as well.
00:24:00.720 | Gosh, we've created a few there. Oops. So, yeah. Now we've created number six here,
00:24:11.120 | which is Jim lives in London. His user ID is six. Okay. So, I mean, formatting isn't perfect,
00:24:19.200 | but that's good for now. We've created our first post request. Okay. So, it's pretty annoying that
00:24:27.120 | we have those kind of messy entries that we added to our data sets or data frame.
00:24:36.320 | So, what we really could do with having in our API is a way to actually delete those.
00:24:41.520 | So, let's go ahead and build that. So, we need to first define a delete function method.
00:24:50.800 | And all we really need here is to say, okay, which entry do you want to delete? So,
00:24:58.400 | we don't need to have a name or a city or anything. We just want the user ID.
00:25:03.040 | So, we'll copy that into here. So, we pass the input from the user.
00:25:09.840 | And then we say, okay, add argument, user ID. That's all we need. We don't need anything else.
00:25:15.840 | And then what we need to do is pass those as well. So, just like before, pass args. Yeah.
00:25:24.240 | There we go. So, now we have so, at this point, the user has given us a user ID that they want
00:25:32.880 | to delete. And we need to go ahead and delete that. So, to delete it, first, we need to say,
00:25:39.680 | okay, does it exist? So, we say if args user ID is in our data. So, we need to load the data first.
00:25:52.480 | So, let me load it here. Okay. So, if that is in data user ID, then we can go ahead and delete it.
00:26:09.120 | And to do that, we're just going to use the typical Pandas data frame logic. So, we just write data
00:26:17.840 | equals data, data, user ID. And we just want to select the rows that are not equal to the user
00:26:27.360 | ID that we just pulled in. So, the args user ID. Right? So, that's just going to return all of the
00:26:34.960 | rows except from the one that we've specified. Okay. So, we deleted that. Now, we just need to
00:26:43.040 | save that to file again. So, to CSV, it is I keep forgetting the variables, user's path,
00:26:53.760 | and then indexes false. Okay. So, we've opened the file, deleted the entry, saved it back to CSV.
00:27:05.280 | If we have done all of that, we can return to the user a 200 code. So, we'll just return,
00:27:12.480 | let's return all the data again. We don't need to do this every time. We could just return
00:27:16.640 | a statement saying, hey, you've deleted everything. So, I'm just going to return so we
00:27:25.680 | can see what we're actually doing. In reality, probably, I'm pretty sure you wouldn't want to
00:27:31.600 | actually do this. But I suppose it depends. Maybe you would. Who knows? So, return to 100 code,
00:27:39.280 | because it's successful. Now, in the case that the user ID does not exist,
00:27:44.080 | here we want to say, okay, we want to check. Well, we've already checked if it exists.
00:27:50.560 | And then from there, we're just like, okay, it doesn't exist. What are you doing? So, we just
00:27:55.840 | return. We say, what did we use before when we sent a message? Message. Just message. Okay, cool.
00:28:05.120 | Message. And the message is going to be user ID does not exist. Something along those lines. So,
00:28:12.080 | args user ID does not exist. Let me format that a little nicer. And then here, we need to add
00:28:27.440 | the code, which is, I suppose, not found. So, 404. Okay. So, I think that's it, really. So,
00:28:42.080 | let's save it. Again, it should reload automatically.
00:28:50.640 | Head over to Insomnia. Let's, again, so let's go to users. We're just going to look at what we have.
00:28:56.560 | So, get users. What do we get? We have all these. Now, we want to delete
00:29:03.520 | user IDs four and five. Okay. These two are messy. We don't want those.
00:29:09.280 | So, we want to specify a user ID. We don't want four. So, we change that to a delete request.
00:29:20.960 | We don't want four. Let's send that, see what happens. Why did that not change?
00:29:30.400 | A data. Why didn't that change?
00:29:43.600 | User ID. Is it because this needs to be a string, I think.
00:29:49.440 | Okay. I think. Yeah, probably.
00:29:57.360 | Let me try again. Send. No, still nothing. Okay. So, that took me far too long to actually figure
00:30:09.520 | out what was wrong. But there wasn't anything wrong. There was the string issue. So, we did
00:30:15.280 | need to convert over to a string to compare them. But also, what I was doing here was reading the
00:30:21.680 | key value here, so the row number, and thinking four was a user ID. It's not. We have two fives
00:30:28.160 | that we sort of messed up and made those earlier. So, there is no four for us to remove now. We
00:30:36.240 | don't need to worry about it. So, let's test it with five instead, which are the two messy ones
00:30:41.840 | that we need to remove. So, let's send that. And now, you can see that we've actually removed
00:30:47.440 | those entries. So, that's pretty good. Now, the one thing I did also notice is if I try and send
00:30:55.280 | that again, we do get the 404 not found. So, that's pretty cool, right? We've got everything
00:31:04.720 | that we would expect with it. Now, I think that's it for the code. The only other thing I wanted to
00:31:11.600 | show you was this other. So, this is the actual full API script here. So, in here, I also included
00:31:18.640 | another method here, or another endpoint locations. And we run that, and we can mess
00:31:26.800 | around with the cafe locations as well. But that's everything. So, I think there's nothing
00:31:33.200 | else we want to cover in this video. So, we'll leave it there. So, thank you very much for
00:31:37.600 | watching. I hope it's been useful, and I'll see you again in the next one. Bye.