back to indexAPI 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
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: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:43.600 |
User ID. Is it because this needs to be a string, I think. 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.