Create a REST API with Go | Simple Blog App

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey welcome back to nerd academy i'm your host james and in this video we're going to learn about restful apis and we're going to implement a very simple one using go postgres and docker so let's get into it so let's define what rest is rest is an acronym and it stands for representational state transfer way back in 2001 a phd student named roy fielding wrote a dissertation and basically kind of created rest so i'll leave a link to his paper in the description below now basically rest is an architecture it's just basically how you design your application's interface so when a web service or api conforms to this rest architecture it is considered a restful api there are six okay there's five one is optional but there are six guidelines to be considered a restful api your api should have a uniform interface the resources should be uniquely identified through a new unique url and they should be kind of manipulated with the standard http protocols so if you're using puts to update or even post update it should always be that way for all your resources your rest api should have a well-defined client and server there should be an absolute clear separation between your server and whatever client there is this allows the server and the client to grow and evolve independent from each other your api should be stateless that means requests from the client to the server should have all the information necessary to perform the request and clients should manage the state and the server should not do that your api may or may not be cacheable the server should let the client know if a resource is cacheable because a client could reuse one of those resources you should think of your api as a layered system responses and calls go through many different systems the server or the client cannot assume they are speaking with either the endpoint or an intermediary and the sixth but optional is having code on demand so some resources could actually return executable code but in most cases resources are going to be json or xml or even html so what is a resource good question now roy fielding just said it best the key abstraction of information in rest is a resource any information that can be named can be a resource a document or image a temporal service example today's weather in los angeles a collection of other resources a non-virtual object such as a person and so on so let's say you're using the twitter api a tweet could be a resource or using open weather's api the weather tomorrow afternoon in oslo norway could also be a resource let's touch on endpoints really quick the url in your api that has the resource is kind of known as your endpoint for instance let's say website dot com slash api slash book slash 324 this would be a book with the id of 324 and a get request would probably return more information about the book so using common http methods we can interact with our api a common term you've probably heard is known as crud which is create read update and delete using a post method we can create a resource using the get method we can read a resource using put or patch we can update a resource and finally using delete we can delete a resource well you might be thinking that sounded like a bunch of gibberish and buzzwords best thing to do is try creating a rest api so you get a better handle on it so let's go build a restful api for that blog you've been meaning to finish and a website you've been meaning to finish okay i'm projecting that's me let's go so i took the liberty of already starting some of this code so let's walk through some of it in main.go normal package main we got some methods here in our main that's gonna just kind of start up our application we got a model.init so let's go there first this db.go file kind of just sets up our connection with our postgres database that will be running in docker so we have a config file we're going to load up and then we're going to create a connection info and then we're going to try to open our postgres connection and if that has an issue it's going to return out and then we're going to try to ping it just to be sure and that's pretty much that and let's go look at our controller we're going to start we're going to use the gorilla mux router and then we're going to we are going to create some handlers here for our endpoints and then we're just going to listen and serve at 3200. so let's go look at our env file this is all for our postgres database and then for our database we're going to initialize a very simple very basic posts for our blog so we got an id and then we're going to have a title and then we're gonna have the content very exciting and we initialize a couple posts here got our hello word hello world and the obligatory hello world post and another post you get the idea all right let's go look at our docker file uh this is going to build our golan code so i'm pulling from golang alpine alpine is a very small lightweight linux container so we're gonna make a directory of our www we're going to make that our working directory and then we're going to copy everything which is in this app folder so config controller model and our main main.go so all that will be available at inside this directory and then we're going to basically build it i should note here that my linux machine went bonkers again so i am on my m1 mac mini and i had some serious issues just trying to make sure it worked before i started this video so if you're on the m1 ship there is a bug you'll need to use the go this is go architect so i'm on arm you probably are on a regular maybe linux or even windows or even regular mac so you'll probably just need amd 64 for your go architecture so we're just going to build this main.go and the output is going to be an executable just called app then we're going to expose our port at 3200 and then the entry port for entry point for this container is at var www the app so that will be run when we start this container all right let's go up one level and look at our docker compose don't get too wrapped around the axle here it's not too bad um we're creating some services so the first service we're creating is postgres we're pulling postgres 12. i think there's 13 out but i just stuck with 12 for now i should probably update soon huh so restart is always so if it fails it's just going to try to restart the network for these two these guys to be able to talk to each other on the docker compose network we're going to create a network called the back end and see down here in our app there's also a back end here and here's just kind of where i define that network and then we're going to create two volumes uh it could be a file or a folder so we're good for this first one it's a folder called db data you can see it right here and then we're going to put that in var led postgres postgresql data so if we stop and start the container again any anything we've done in our database will be saved and it'll be loaded again and then our init sql file that was just our very basic table and a couple entries just to have some starting data so we're going to put that in the docker entry point init db uh 10. 10-init sql so when the container starts up it's going to initialize the database with that table and those values and then we have an environment file which is all that fun stuff the username and password and the database we're using and the url and the ports and another issue i had i'm kind of glad it happened but i was on my linux machine just yesterday and i kind of moved my desk and now my linux machine doesn't work it's not connecting to my monitor whatever for whatever reason but on this m1 i was having an issue because i am using docker desktop on linux docker is native so this has got to run in a vm so what i had to do was a health check so what i'm doing here is running a command and there's a command for postgres pg underscore is ready so that's just going to check if it's ready then we're going to pass the db and the user so i had an issue with this dot env file um i should be able to just use like uh postgres db but that wasn't working there was something going on i ran out of time so i just want to get this video done so for now i'm just putting these in plain text here and not from the env file if i figure it out i will definitely put it in the description so this health check is going to run every 10 seconds and after 45 seconds if it fails out that's it it's going to retry 10 times so our application our app this is all our go lane code and this folder app so to build a docker image using docker compose all your container data in a folder so this is going to build it's looking for this folder app and it's looking for the docker file so that's how that kind of works and this we're exposing the ports inside the port to outside the port 3200 no need to change it for our set our purposes and that network i talked about so it can actually connect and talk to postgres and then that depends on postgres and we want the condition for service healthy so this is that health check so if this fails out then we can't we can't run anything and this thing won't run so we're hoping this works and then our volume is defined and our network is defined so let's get to writing some code let us start in the model so i went ahead and created a struct since we're going to pass json back down to our client from our api we're going to create a struct with all the json fields this correlates to our table id title and content so let's start with just getting let's say all posts so this will return a slice of articles so first let's declare articles a slice of articles and we'll be empty and finally at the end we'll return those articles so the first thing we got to do is query our database it should be up and running i should also note we're in package model we defined this var uh this db object which is in which is a sql database object we have access to that in blog.go since it's in the same package but let's start with our query we're going to use the the backticks and we're going to select select id title and content from posts and then we're going to run db.query with that query and this returns an error and something called rows so let's get our rows and our error and then we'll check if the error is not nil that means we have an error so let's let's actually just return this error golang lets us return zero one or more uh types or objects or things let's just return an error also so if you uh if you see me checking if air not nil so we'll go ahead and return our articles and nil if we get to the bottom of our methods that means everything worked out okay but if air is not nil let's just uh return our articles which should be nothing this is an empty slice and then we'll return that error so what we want to do is defer the close of our rows so rows dot close we'll we'll defer that to the end of this once it gets here it will finally close that out so we're going to do a for loop so it's for rows dot next so as long as there's another row from our query we will continue looping so now we we have the row what we want to do is extract all the id the title and the content from our uh row so we're gonna go ahead and declare those now id is a uint64 we got the title and content which are both just strings so now we're going to use row.rows.scan and we're going to pass the address in to each of these and the order of these does matter the order should match our select query so id title and content when we scan it it'll it needs to be in that order so if we switch the title and content here the content will be in our title variable and then the title will be in the content variable and this does return an error so let's get that error and if air is not nil let's return our maybe empty articles slice and oop and our error so if we scanned all of our objects into our variables let's create an article from our article struct and we're going to put our id from our id and our title is our title and our content is our content that should make sense right now what we want to do is put this article in our slice so we take our article slice and then we use a built-in method called append we're going to take our articles and then we're going to put in the new article so this will take the slice articles and it will take this article and put it at the end of the slice so as we leap through this it'll keep adding more until there's no more and then we exit this loop and we return our articles and nil because there was no error at this point alright from here let's make a our controller let's go ahead and make our endpoint for our controller so let's call this get all posts just like we did before but this is an http endpoint so we need our response writer and we need our request object so we're going to return json so we need to take our response writer we're going to get the header and then we're going to set the content type oops this has to be capitalized and we're going to set that to application slash json we're going to use that method we just created model git all posts remember that returns a slice of article so we'll call it posts and an error so if the error is not nil then we have an error here so let's take our response writer we're gonna write our header and we're gonna say http internal server error status so that returns 500 so we know something went wrong and then we can even write out two back to our our api if we want to we can write out the error.error this will this will take this string error and turn it into a byte slice so we can write it down to our write it out to the h the response writer i only suggest writing out those errors to your client in development mode not in not when you're live so otherwise we're good to go so what we need to do is use the json package we're going to create a new encoder and we need to pass in an i o rider we're going to pass in our response writer and then we're going to encode posts and that's it if everything works out it's going to take all those posts put it in this uh the slice and then we're going to take take that slice and encode it to json and pass it down to your client so let's make our endpoints in our init handlers method so we're going to take our mux router and we're going to handle func and we're going to do let's say api posts and then we're going to use that method we just created in our controller so controller get all posts and then we can make sure that the client can only get use a get request we can chain the methods so this allows us to set this uh this endpoint to let's say git only so someone cannot try and use post here or put or delete it will only work as a get method i'm getting an error here but let's just let's try it out and see what happens so how you run a docker compose make sure it's installed a docker compose version all right so we have docker compose so at your level where your docker compose.yaml file is you need to run docker compose you know bring it up and i'm going to pass the flag build to it those are going to take everything in our docker compose read all the instructions and then try to build it and bring it up so right now it's waiting for postgres to come up and once this the health status comes back it's good there we go we're all ready to go db is open ping successfully and we're now initialized and listening on port 37200 so i'm using postman um there's another app called insomnia if you want to follow along and kind of test with me what i'm doing you can use postman and install that otherwise insomnia i haven't used it but i'm pretty sure it's pretty similar so if you don't see this and there's a different uh kind of thing in this part here all you have to do is hit the plus sign and it opens up a kind of request we have a drop down we can do all the different kind of http methods right now we're going to do get we're going to do localhost colon 3200 for the port api and post that will map directly to this function right here which is get all posts so see if it works all right so this is a slice and it has the first one the id of one hello world the obligatory hello world post and id2 another post and content yet another blog post about something super exciting directly from our database from our init sql all this right here so that is working just fine all right now we can get all let's get a very single simple one so let's go back to our model and let's create another method right below get all posts let's get a single post so font get post and we're going to need an id so we'll say id it's a uint 64 because we need to match the type here and we're going to return an article and again an error so like we did before let's create our article declare it at least and then return the article at the end and become a nil so if it makes it to here like we did here everything worked out okay and there should be no error so once again let's create our query using backticks we're going to select title and content we don't need the id because we already have it from posts where id equals our id and a placeholder we're going to use is dollar one i'll get to that in just a second as we did before we're going to get we should only get a single row and it's going to return an error we're going to do db.query we're going to pass our query in and then our id what this method will do it will take this query and then it will find this dollar one and it will pass this id to it when there's more they have to be in order when you pass it into this query so if there was if we did multiple where id is one and then we had some relational databases going on we had some other stuff you'll have to uh you'll you'll increment this to dollar two twenty one i'm not gonna put emojis in there what's going on dollar two and then you'd have your other variable here as the second one and so on and so forth until you get all your variables so if air is not nil we're gonna return our empty article and this error we're also going to differ closing our row so instead of a for loop i'm just gonna do if row.next means at least we have one we do not expect more than one because there should only be one in our sql it's serial not null and unique so there cannot be more than one if that's true we have a good id we're going to do what we did last time we're going to create our variable title and content of type string and then we're going to use row.scan and we're going to pass in the address of our title and our content and if the error is not nil then we'll return an empty article and the error otherwise we're going to create our article title is title and content is content and we may as well put the id back in there we'll return the article and no error so let's go over to our controller and do what we did last time and create another method for our endpoint responsewriter and request object we also need to set our header for content type to be application slash json but this time we got to do something a little bit different in our request we're going to have so our request is going to look something like api post and then an id right here so this id we can grab using using the gorilla mux variables so get a param from mux dot vars and we're going to use a request object so this returns a map of string of string so i'm going to do something a little fancy here i already know it's id so this will return whatever is at id right here as a string into the param so we need a uint64 so we gotta convert that so id and this can cause an error string convert we're going to do parse unsigned int we're going to take the param it takes a string the base is 10 and the bit size is 64. so if there's an error here there's something wrong with the id passed to it so we're gonna say sorry we're gonna write a header at this point we'll return we'll leave this method otherwise now we have our id we can get our post so post an error from model dot get post let me scroll this up and we have that id we should also check this error if error is not nil we'll just do exactly what we did here before let's copy and paste that otherwise we're going to do what we did before again json.new encoder we'll pass the writer in and we'll encode our post and that should return back to the client so let's go over to our server and create another endpoint using handle func this time we're gonna do api post and for the param it's curly braces and the fram which will go to that map of the string and then we're gonna do model nope controller dot get post and once again we only want the methods allowed to be a get request so i have no doubt that will work so let's go on to the next one before we test we'll just go ahead and finish them off let's create one so in our model blog.go let's create a post so we're gonna have a post called article and this only needs to return an error so at the end like we've done before if we get to the end of the method there should be no error so we'll just return nil so this one is actually not too bad we're going to create another query though and we're going to insert into posts our title and content since the id is auto-incrementing it will do that for us and our values one and two so one will be title and two will be our content so all we do is db.exec we're gonna pass our query in and then we're gonna do our post up title and our post.content so that will take the title and replace it with one and the content replace it with two and it should put that in our database but this returns two items an sql result which we don't really care about and then the error if you don't care about a return type or return item you can do underscore so this just tells the compiler we don't care about this um sql result so it basically is ignored but we do care about the error so if that error is not nil then we're going to return that error so we're just bubbling and all these message methods we're just bubbling up the error up to our controller to determine if there's a problem or not so let's go back to our controller and write a method for just that create post a responsewriter and we need our request object once again we'll do our header set um application json again so this one's a little bit different than before so the client needs to send json of our struct so we're going to have to use a decoder so we're going to create the doker decoder can i spell maybe not tonight so we're going to create our decoder using the json new decoder we're going to pass in our request object actually we need the body because it needs to find that in there we need to find our struct basically in json so we need to do is declare our posts from our model objective article and we're going to take our decoder and decode using the address of our post that way it will take the json values it finds from our struct here so it's finding this id and this title and this content and it'll map it to our struct and go ling but if there's an issue this will return an error so we'll capture that error if error is not nil then i was going to just format and print so we're going to do what we did before we're going to write the header http internal server error and we'll write out the error again at this point we should return now we're going to use our method we created in the model to put that into our database so that returns an error model.createpost and we're going to pass that post in uh it doesn't like this let me uh sometimes these errors it doesn't like you just reusing the same oh yeah i can just do it this way there we go so if air is not equal to nil we'll just do this again else everything's okay so let's just do write header http status okay so that will return 200 let's go back to our server and we'll go ahead and create another handle funk this will be api post let's just say new controller dot create post and this time we are going to use the methods of posts so you can't get anything or delete anything from this endpoint only post to this endpoint all right so we can create and we can read let's try to update our model and create a new method to update so funk update a post so we'll just accept an article it will have the id and we'll return an error so if we get to the end we turn nil like before we'll create our database query so we'll update posts we'll set title equal to one content equal to the seconds and where id is equal to three so we'll do db exec we'll pass our query in and we'll do post dot title post dot content and post dot id so the order here does matter so the first one is title we got title here second one is content and we got our content and the last one is id and this returns an error find out if there's an error and return it sorry we gotta ignore that uh result all right let's create our let's copy this create our update post so we gotta do something very similar to our create post so i'm just gonna copy and paste to save some time and update where we need to so we're still going to need application.json we're going to need to decode we're going to take the article we're going to decode that and if that's an issue we'll we'll send in we'll return if i have status 500 we got to change this to update post and that should be it so let's go over our server create a handle func here we'll do api post update now if you wanted to you could do something like id and then update but i'm just passing up the json so i have i have the id so i already know it and update post this time our method is going to be put and finally before we go test everything out let's delete a post so we'll need the id to create our query that will delete from posts where id equals dollar sign one and like we've done in the past db.exec and query and our id so if error does not equal nil then we're going to return that error we need to return an error otherwise we're good when we return nil so let's create our handler method here responsewriter we'll do content type application json again look at our param again using the mux dot var this time i'll do it kind of like normal way so our id str is equal to param id and then we're going to take our id string and string conversion parse unsigned int we'll take our id str base of 10 and 64 bits so if error is not nil then we got it we got an issue we're going to copy this because we're going to do this a couple times here otherwise we're going to use our model and delete the post at the id this does return an error of errors not equal to nil again else we'll just w right header http okay and let's go over to our server and make sure we can actually access that router handle func so we'll do api post delete at that id and controller delete post and only methods allowed here is delete all right i think we're ready to test everything out so let's go ahead and stop using ctrl c and we'll bring it back up again okay we're all back up let's go back over to postman and we'll go ahead and try getting all the posts again all right we're good let's get post at id 2. that works oh see we got in there back to our model not yet content i need the pointer there i need the address of the content to actually put it in there so stop and start again alright let's go back over and try this again there we go let me pull up our endpoint so i know what else we can do let's go ahead and create one post new yep so we're going to need a post and in our body raw so it needs to have title say hello from postman and then content this is content how exciting all right see if that works out oh did i mess up yes i did posts post new so i got uh 200 back so let's see if we can see that in there ah yeah i get so you got a change to get and we have id of three hello for a postman this is content so let's try to delete that one oh actually let's try to update it and that is going to be a put so we have our id of three postman updated now with more content all right let's see what we get we get a 200 so let's try to get all those posts again and three hello from postman updated this is content now with more content actually let's try to get just that post see if that works yep and let's go ahead and say goodbye to post number three using the delete method got a 200 let's see all the posts again and see oh can't delete posts we have to get the posts yep only two in there we made it that's awesome you're well on your way to understanding rest apis i challenge you to try to consume and use a rest api out there on the web there's plenty of free ones out there i'll leave a link to some in the description below and if you made it this far thank you so much it means so much to me so if you could help me out hit that like button down there to help the algorithm out remember always keep learning and if you want to watch more about software development just watch the video right here and we'll see you in the next one [Music] [Music]
Info
Channel: NerdCademy
Views: 865
Rating: undefined out of 5
Keywords: REST API tutorial, Simple Blog REST API tutorial, go programming tutorial, go with postgresql, golang, golang programming tutorial, golang with docker, golang with docker compose, golang with postgresql, postgresql with docker, postgresql with docker compose, postgresql with golang, programming tutorial, rest api with go, rest api with go and postgres, rest api with go in docker container, rest api with golang, software development, software development tutorial
Id: joj4RKC1urc
Channel Id: undefined
Length: 33min 21sec (2001 seconds)
Published: Thu Nov 11 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.