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

Transcript

In this video, we are going to explore how we can put together a pretty simple API using Flask. Now, what we're going to do is go through and build essentially what you can see here. So, all of this code here is a single API. We're not going to go through all of it in this one video, but we're going to go through the essentials, and we're going to step by step put an API together that is going to look very similar to this.

Now, what this API is doing is pulling information from these two CSV files, and either presenting that information to us, that data, allowing us to modify, allowing us to add new entries, or allowing us to delete it. So, we get to use get, post, put, and delete methods. Now, if you are following along with this tutorial, you'll probably want this data.

So, you can get it using this code over here. So, over here, we have, I'll make sure there's a link to this in the description so you can get it. All we're doing is downloading the code, or those two data files from here, and then storing it in a local directory.

I mean, you can change that directory to whatever you want, of course. Inside those two files, we have locations here, which is just a list of, I think these are actually real coffee cafes, but I made this CSV quite a long time ago, so I'm not 100% sure. And then, over here, we just have some made-up user data as well.

And the end result will be something like this. So, this is a program that I'm working on, so this is a program called Insomnia. I'll leave a download link to that in the description, but it just allows us to send API requests very easily. So, I'm going to send an API request to this address here, which is the API I just showed you, and it's a user's endpoint.

We will have two endpoints, which are essentially just separations of the API. So, users, and that's going to return all the user's data, and then we also have locations. And we'll send that as well, and we return all the locations. Now, we're going to start from scratch, and the first thing that we'll need to do is import everything we need.

So, we will need to import Flask. So, from Flask, import Flask. We'll also need to import Flask RESTful. So, this is another Flask library that gives us a few very useful tools for building an API. So, right from Flask RESTful, import resource API and REC pass. Now, specific to what we're doing here, we will also want to import pandas and import AST.

Now, you will probably have to pip install these as well. So, the pip for those is pip install Flask and Flask RESTful. Nothing weird there. All right. So, the first thing we need to do is initialize our Flask app and then initialize our Flask API. So, we do that.

So, this is typical Flask here. So, we write Flask name, and then the API is API equals API app. Okay? Super easy. Now, we already touched on it, but our app is going to have two different endpoints. So, we're going to have the user's endpoint, and we're also going to have the location's endpoint.

And we're going to go through building the user's endpoint, but you'll be able to find the code for both of those in the description. But the way that we separate both of these is we use a class object. So, we're going to create our use class first. So, we write class users.

And then to initialize this as a endpoint for our API, we actually need to pass the resource object into it. Now, we'll inherit the resource object and expose it to different like HTTP requests, like get posts and so on. For now, we're not going to add anything in there.

But to make sure that endpoint is added to our API, we need to write API, add resource, and then we need to specify the web address of this or the almost like a page of a web address that the resource or endpoint will be at. So, we'll say users, which is our class, and then the actual endpoint location, which is users.

Okay? So, this is saying I want you to map the class users here to this place in our API. So, our web address. So, if, for example, the API was located at, let's say, API.com, this resource would be located at API.com/users. Okay? And we'll also create another one, although we're not going to fill it out in this video, that will be called locations.

Again, exactly the same thing again. Resource, we'll just pass for now. And we'll copy that and do the same for locations. Okay. Now, that's sort of the structure or the very high level structure of our API itself. But we so, what I like to do is whilst we are building or writing the code for our API, I like to also test it as we go along.

I think it makes things a lot easier. So, to run our API, we need to write this. If name is equal to main, we write app.run. Okay? It's that easy. Now, what we can do is just press the execute button up here. Okay? Okay. So, we got this error because I, okay.

I don't know why I did that. So, this is, this needs to be the class, not a string of the class. Okay. So, now let's run that again. It should work. Okay. So, we get this. This is a development server. Do not use it in production environment. That's fine because we're just testing it.

And then we can see this is running at this address. Okay. So, if we take this, we'll copy it and we're going to take it over into Insomnia. Okay. And I'll just clear everything that I was doing before and we'll go into here. Okay. And if we send this, we won't return anything yet.

So, we'll just get this not found. But if we go to users, we should at least return this internal server error. So, this means that, okay, we are not getting anything back because there's an error on server side. So, in our API. But there is something there. So, before when we just had this.

So, remember, we have those two endpoints. We're not actually using the base URL. So, there's not actually anything there. We get this not found. Because there just isn't anything there. If we type in locations or users, we will return this internal server. Now, we're getting this because we haven't written any code.

So, it doesn't know what to do when we send that request. So, let's go back to our code and we'll start writing something. So, first thing I want to do is we'll define a get request. So, write find get plus self. And in here, what I want to do is load the user's CSV.

So, to get that, I'm going to write user's path. So, this is the specific directory that I have stored my user's data. Of course, it might be different for you. And what I'm going to do is down here, I'm going to use pandas to read in that data. Whenever I call this get request, I'm going to read it in.

So, this is similar to if we, for example, had a database on a server somewhere, when the API gets a request, it would read that data quickly and return it to us. So, this is a very simple version of that. So, I'm going to do data equals pd.read_csv. And we just go user's path.

And hopefully, that's the right path. I think it is. We can see. So, if I come up to here, yes, we have data. So, I guess that should be fine. User's CSV. Cool. And then what we want to do, we can't pass a pandas dataframe object through an API.

So, we need to convert this into a dictionary. So, we write data.to_date. Now, what we want to do is return. We want to return the data. So, we're going to write data, which is equal to data, our dictionary. And we're going to return the 200 code, which means the API request was successful.

Here's your data. Okay. I'll save that. And I believe it should update automatically. Let me check. So, come over here. Let's send that again to users. Okay. No. So, I think we need to restart the API. So, come over here. Here, I'm just going to control C and run it again.

Now, you can turn debug mode on. So, I think that will automatically reload everything. So, let's do that first. So, down here in app run, we're just going to set debug equals true. Save that. I'm going to press control C down here and execute again. Now, when we make changes, it should reload automatically.

So, come over here. Let's send that request again. Now, we see user path is not defined. So, see why that is wrong. So, it's user's path. So, I'll save that and that should reload automatically. Let's try again. Okay. Perfect. So, now we get a 200 response there. We get all of our user's data.

Okay. So, we're just returning that data frame in a dictionary format. Okay. Cool. So, that's our get request. Let's move on to let's put requests, I think. So, adding more data. Okay. So, this time, what we're going to need to do is we're using a post request. So, I put a second ago, I meant posts.

And in here, the first thing we need to do is we need to retrieve information from the user that they want to upload. So, the first thing we need to think about here is we are trying to get data from the user. So, they are going to send us like a new entry.

So, what did we have before in our users? We have user ID, name, city, locations. Okay. So, we want the user to be able to add a new row. So, each one is essentially a row in a data frame. So, we have locations, city, name, and user ID. Now, we want the user to be able to add locations or location, locations, name, and city.

Now, the I think the user ID, we don't really want the well, the user isn't going to add that. That's going to be generated automatically. So, we ignore that, but we do want the user to be able to pass those three things. Now, to do that, we're going to use the REC parser.

So, request parse that we use up here. And to initialize that, we need to write parser equals request. Sorry, why is it up here? REC parse, sorry. REC parse dot request parser here. It initializes our parser. And what this is going to do is when we add arguments into the request, this is going to read them.

And what we're going to do is parse them out into variables. So, we are going to within our within this request, we want to allow those three. So, location, name, and city. So, we write parser, add argument. This one will be location. And we're just going to do one location at a time.

So, do location ID. And then, of course, we use a put request later if they want to add more locations. We say required is true. They do need to add one of these. And the type of this is a integer. So, we'll write integer. Okay. We need to add another one.

I'm just going to copy this. So, let's copy here. Here. And we have name and city. Name, city. And the type is for both of those strings. Which I think might be the default. So, we probably don't even need to include that type string there. But it is there.

So, we'll leave it. So, now we have the three arguments that a user can use to post information to our API. And what we need to do to extract whatever the user has sent us is we do this. We're going to extract them into this dictionary here, which is called args.

And we write parser. Like that. Okay? So, now we have those arguments. And let's first just make sure this is working. So, I'm going to return what we just tried to send that. So, let's return location. Loc equals args location ID. Name. And the city. Okay. So, let's just make sure it's actually working.

And we'll say 200. Save it. And that should reload automatically. Let's go over to Insomnia. And we are sending a post request here. So, post. We have users. We enter this question mark. So, this allows us to start adding our parameters. So, location ID is going to be equal to I'm going to say 5.

And so, we added the ampersand symbol there to add another parameter. So, location ID and what are the other ones? User. User. Or name. James. That's me. And for the city, let's put London. Okay. Send that. Cool. And then we're just returning that request back to Azure. You notice here we put lock, not location ID.

So, you can tell it's not just returning what we sent. It's returning what we have processed through our function and returned back to ourselves. Okay. Cool. So, we know we're passing those arguments correctly. Now, let's use them. So, again, what I want to do is read our data. So, we do data equals data pandas.read CSV, again.

And we're reading the user's path. Okay. So, user's path. And we're doing users here. So, actually, do we want location in there at all? I don't know. Yeah, maybe. So, no. Let's say the user can specify that. User ID. We're not returning anymore. So, I want the user to I want to be able to post this.

Because then if we have a duplicate of a previous user ID, we're not we can say we can check if that already exists in the data or not. Which we'll do now. So, if args user ID. In the data that we just loaded. So, in data is it user ID, I think?

I'm sure it must be. Okay. I think it is. I want to return an error message saying this already exists. So, write message. And all we do is write args user ID. Already exists. And what we'll return here is the 409 code, which indicates that there's some sort of conflict.

And there is a conflict. We already this user ID already exists. So, we're saying there's a conflict. You can't create a new one. It's already there. If it doesn't already exist, that's great. And we go ahead and we create that new user ID. So, what we do is write data equals data.append.

And then we're just appending the new data that we've retrieved from our args. So, it's going to be we need user ID. Which is going to be args user ID. Let me copy this and make it a little bit quicker. For locations, it's just empty for now. We have city and we have name.

Okay. And then we need to I think we need to ignore. Yes. Ignore index. True. True. Sorry. Okay. And then we'll save that data. So, two CSV. It is user's path. And I'm going to say index equals false. So, we're not saving the row numbers. And then I just want to return that data.

Again, we can't return a data frame. We need to convert it into a dictionary. So, write to dict. And it's a 200 response there. I'm going to save that. Let's test it. See if it works. So, I'm going to let's see. Modify this to user ID now. Send that.

Internal server error. So, let's see. Date frame, no attribute to date. So, I need to add an underscore. Save again. And let's send that again. Okay. So, now we can see we have more items in there. The only thing is I've added five for all of them. Oh, did I?

It's in the code, right? What did I do? Yeah. Very smart. Okay. So, change that name. And this is city. And one other thing is that these are all this should also be a string. One thing I noticed. And this should also be a string. I'm going to leave it for now.

It's fine. So, let me send that again. Five already exists. So, at least we know that our conflict code works. So, let's create another one. Number six. I'm going to call this, I don't know, Jim. Let's go again. Okay. So, now we've created number six as well. Gosh, we've created a few there.

Oops. So, yeah. Now we've created number six here, which is Jim lives in London. His user ID is six. Okay. So, I mean, formatting isn't perfect, but that's good for now. We've created our first post request. Okay. So, it's pretty annoying that we have those kind of messy entries that we added to our data sets or data frame.

So, what we really could do with having in our API is a way to actually delete those. So, let's go ahead and build that. So, we need to first define a delete function method. And all we really need here is to say, okay, which entry do you want to delete?

So, we don't need to have a name or a city or anything. We just want the user ID. So, we'll copy that into here. So, we pass the input from the user. And then we say, okay, add argument, user ID. That's all we need. We don't need anything else.

And then what we need to do is pass those as well. So, just like before, pass args. Yeah. There we go. So, now we have so, at this point, the user has given us a user ID that they want to delete. And we need to go ahead and delete that.

So, to delete it, first, we need to say, okay, does it exist? So, we say if args user ID is in our data. So, we need to load the data first. So, let me load it here. Okay. So, if that is in data user ID, then we can go ahead and delete it.

And to do that, we're just going to use the typical Pandas data frame logic. So, we just write data equals data, data, user ID. And we just want to select the rows that are not equal to the user ID that we just pulled in. So, the args user ID.

Right? So, that's just going to return all of the rows except from the one that we've specified. Okay. So, we deleted that. Now, we just need to save that to file again. So, to CSV, it is I keep forgetting the variables, user's path, and then indexes false. Okay. So, we've opened the file, deleted the entry, saved it back to CSV.

If we have done all of that, we can return to the user a 200 code. So, we'll just return, let's return all the data again. We don't need to do this every time. We could just return a statement saying, hey, you've deleted everything. So, I'm just going to return so we can see what we're actually doing.

In reality, probably, I'm pretty sure you wouldn't want to actually do this. But I suppose it depends. Maybe you would. Who knows? So, return to 100 code, because it's successful. Now, in the case that the user ID does not exist, here we want to say, okay, we want to check.

Well, we've already checked if it exists. And then from there, we're just like, okay, it doesn't exist. What are you doing? So, we just return. We say, what did we use before when we sent a message? Message. Just message. Okay, cool. Message. And the message is going to be user ID does not exist.

Something along those lines. So, args user ID does not exist. Let me format that a little nicer. And then here, we need to add the code, which is, I suppose, not found. So, 404. Okay. So, I think that's it, really. So, let's save it. Again, it should reload automatically.

Head over to Insomnia. Let's, again, so let's go to users. We're just going to look at what we have. So, get users. What do we get? We have all these. Now, we want to delete user IDs four and five. Okay. These two are messy. We don't want those. So, we want to specify a user ID.

We don't want four. So, we change that to a delete request. We don't want four. Let's send that, see what happens. Why did that not change? A data. Why didn't that change? User ID. Is it because this needs to be a string, I think. Okay. I think. Yeah, probably.

Let me try again. Send. No, still nothing. Okay. So, that took me far too long to actually figure out what was wrong. But there wasn't anything wrong. There was the string issue. So, we did need to convert over to a string to compare them. But also, what I was doing here was reading the key value here, so the row number, and thinking four was a user ID.

It's not. We have two fives that we sort of messed up and made those earlier. So, there is no four for us to remove now. We don't need to worry about it. So, let's test it with five instead, which are the two messy ones that we need to remove.

So, let's send that. And now, you can see that we've actually removed those entries. So, that's pretty good. Now, the one thing I did also notice is if I try and send that again, we do get the 404 not found. So, that's pretty cool, right? We've got everything that we would expect with it.

Now, I think that's it for the code. The only other thing I wanted to show you was this other. So, this is the actual full API script here. So, in here, I also included another method here, or another endpoint locations. And we run that, and we can mess around with the cafe locations as well.

But that's everything. So, I think there's nothing else we want to cover in this video. So, we'll leave it there. So, thank you very much for watching. I hope it's been useful, and I'll see you again in the next one. Bye.