Lesson Description

The "API Route Handlers" Lesson is part of the full, Complete Go for Professional Developers course featured in this preview video. Here's what you'd learn in this lesson:

Melkey adds API route handlers to get a workout by ID and create a workout. The HandleGetWorkoutByID function uses Chi to access the ID URL parameter and parse it as an integer. Method receivers are added to each handler, and the Application struct is refactored to include the WorkoutHandler struct.

Preview
Close

Transcript from the "API Route Handlers" Lesson

[00:00:00]
>> Melkey: We are going to now leverage the structure that we have developed using our Qi router and the HTTP server to start building out our handlers. And we're gonna do this by continuing to organize our application. So here in the internal folder, let's create a new package called api, okay?

[00:00:23]
Within the api package, we're gonna create a new file called workout_handler.go.
>> Melkey: Let's define the package api name, okay? And in here, again, with every new package, I always ask myself, how am I gonna handling be data? Now, at an api layer, it's not obvious because if you really think about it, we accept requests from a client, we do some stuff with something bad.

[00:00:55]
What data are we gonna really have, right? Data doesn't have to be just like a string, or anything like that data, as we saw with structs, could also be just functions and different access layers. And because our API is going to be directly talking to our database, we know that our api needs to have access to the database layer.

[00:01:21]
So I'm gonna jump back and forth a little bit, but here in internal, let's make a new folder. Let's call this store, okay? It's gonna be package store, and within the store folder, let's make a new file called workout_store.go. The naming pattern is intentional. So you know that workout handler will most likely have some relationship with a workout store, okay?

[00:01:49]
Again, this isn't like a hard rule. Here, let's just create package store. Now, what I typically do when I write functions or code is I always, and this is personal preference, I love starting from the lowest level. And for me, the lowest level is a database layer, right?

[00:02:05]
If I know how my database layer is gonna look and like how that world works, I can go one level above, which is like api level calls the database layer. When I satisfied there I go one level up, which is like the routing make sure everything is corrected there.

[00:02:18]
In this case, in the first example, we're going to start api letter first, so we're going to leave the data store as is, but just be mindful. We're gonna come back to this file when we tell our app exactly what to do and how to communicate with our database.

[00:02:33]
So here, because I said we are gonna be handling data, we're gonna create a new WorkoutHandler struck. Let's leave this empty for right now, but with every new struct, let's go ahead and create our constructor. So NewWorkoutHandler. Right now, it's not gonna take any argument, but it's going to return a point to WorkoutHandler and so we can do return the address of WorkoutHandler like so.

[00:02:59]
And because of this, and we have this struct available to us, let's create methods that will live on the workout handler. This is a CRUD app. We need to be able to create, read, update, delete. So we're gonna create those methods. But we're gonna start with getting the workout and creating the workout.

[00:03:19]
Those are gonna be, first we're gonna do, then we'll do update, and then we'll save delete for last. So here, let's go ahead and do func. We'll do a receive to we'll call it wh and this will be appointed to our WorkoutHandler, and we'll call the first one HandleGetWorkoutByID, okay?

[00:03:38]
This is gonna take two arguments which we've seen before, http.Responsewriter and r http.Request, okay? And the body of a function, if we take a step back, when we think about how we access the things, the name is kind of HandleGetWorkoutByID. I wonder what that does. Probably get to work out by the ID.

[00:04:06]
So if you ever seen, if you go to a blog post, and you guys have probably built stuff like this already, you have UUIDs, right? You go to a blog post, is that long hash. That's the ID. That lives in the URL query. We could extract that cuz we wanna know if someone on our website, our app clicks workout one versus workout two.

[00:04:23]
We wanna make sure that we direct them to the appropriate workout. So what we can do is actually retrieve that parameter from the URL, and we can do that using chi. So here we can do paramsWorkoutID is going to be chi.URLParam, we're gonna pass it that pointer to r, and then we're gonna extract the slug, which will dub as id.

[00:04:49]
And we define the slug when we define it in our routes. Here, following, can just do some preliminary checks if paramWorkoutID is empty, okay? We can just simply respond, hey, this doesn't exist. Can do http.NotFound, okay? w, r, and then return. The return is absolutely critical. If you don't put a return continues.

[00:05:11]
Now that we have the workoutID, the one gotcha is when we use chi.URLParam, it returns us a string. WorkoutID is not a string. WorkoutID is going to be an int for this example, okay? Now, typically this could be something like a UUID, that's probably more valid. For the sake of this course, we're gonna stick to using an int, just because when we start querying, posting, updating, it's easier to put one or two versus a UUID string, okay?

[00:05:42]
So just for the sake of teaching, it's a little bit more digestible, I guess would be the best word to use. So here we're gonna do workoutID, and we're gonna do error. I'm gonna bring in a new standard library package called string conversion, so S-T-R-C-U-N-V, okay? And the function we're gonna do is parse.

[00:06:06]
I'm gonna bring this one cuz my editor,
>> Melkey: And do parse int.
>> Melkey: There you go. Okay, so parse int. It's going to take an argument, which can be the params or workout ID, which is type string. It's been converted to base 10. It's been converted to a 64 bit int.

[00:06:34]
Here, if our error does not equal to nil, what we're going to do is, I'm gonna kind of cheat this one I'm going to do http NotFound, w, r, and return, okay? I know some individuals may have questions about error logging and stuff like that. Hold those questions until we're done, allow this module.

[00:06:56]
I'm taking a lot of liberties right now to kinda get a dirty version of working and we'll follow it up with properly formatting this, giving the proper utils and how to handle errors accordingly. So now if we have no errors, we know that we have successfully converted our workout ID and we have the workout ID.

[00:07:14]
All I wanna do is format.f, printf, we can do W, and let's just say this is the workout id module OD, break the line, and let's put workoutID. Cool, so TLDR, here we call this. We have a slug. We extract the slug, we respond with the slug. Let's all read about it right now.

[00:07:37]
Okay, as you finish writing that up, I'm going to start on the second handler. Don't feel rushed because this one is gonna be even easier. So all I'm gonna do here is handle CreateWorkout, not by ID cuz when you created workout we did not care about this, we handled this on our side.

[00:07:54]
We'll do again the ResponseWriter, a pointer to http RequestAll I wanna do is format We handled this on our side We'll do again, the response writer a pointer to HTTP request, okay? And then here we're just gonna do is format.f, printf, we can do W, and we'll say created a workout, just to tell us how things are working, cool.

[00:08:15]
Now that we have our handle workout, the struck, we want to essentially allow people to curl these requests, right? We want to get and do a post. How do we do that? In routes, we can put in routes, but even if you put in routes, how does routes know about these functions?

[00:08:32]
And this is where that structure of our app comes back. So here we have our type application struck, what we're going to add onto this is now a new field called WorkoutHandler. And this workoutHandler is gonna be a pointer to API workoutHandler like this, and so we're gonna pull in that package, we just created, melkeydev then project, internal/API, all right?

[00:09:00]
And over here, we gonna create our stores will go here. And then right below it, our handlers will go here. And we can define our first handler as a workoutHandler as api.NewWorkoutHandler.
>> Melkey: And once we define that, we can feed it into our application like so.
>> Melkey: So I'm hoping that the structure is now becoming a little bit more obvious, right?

[00:09:36]
We're gonna have multiple handlers, workout users, whatever you want, and there's a kind of exist in their own package realm. The way we bring them in is using our application struct to be this kind of one crux that now has the ability to be called throughout our application.

[00:09:52]
We pass that application shrug. So instead of passing each individual work on handler across to routes.go we just pass app and that app has access to all of the underlying fields, okay? Now we have this. We can go to our routes.go, and very easily, just hook this up to our functions.

[00:10:11]
Gonna be two function we're gonna add. We'll do r.get, okay? The first one is going to be the slug. So the workout, so we do /workouts/. And then this how you do a slug in cheese specifically, it's curly braces Id, okay? And then the function is gonna be app.workoutHandler.

[00:10:33]
The thing we just passed into our app struck, and it's going to be HandleGetWorkoutByID.
>> Melkey: Okay, once you finish that up, the next function is going to be the createFunction, which is gonna be a post, r.post. The path is just going to be /workouts. And in here, I'm gonna app.WorkoutHandler, handleCreateWorkout like so.

[00:11:08]
So if we're done with that, we can just quickly go back to our server, go ahead and rerun this again, okay? And now we can validate those new working paths. So here we can do curl localhost:8080/workout/2. You can see this is the workout id 2. And if you want to try out the POST method, you can do curl -x POST localhost:8080/workout.

[00:11:43]
And you can see here, created a workout, so we have full confirmation, and now our workout handler is routed correctly. We're able to query the routes and they work as we expect.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now