How To Build a Complete API In Golang (Docker, JWT, MySQL)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right guys so in this video I'm going to be teaching you how to build a complete restful API in goang we're going to be building a project manager like jir and we are going to be adding features like authentication creating and connection to a database so it's going to be my one could be anything we're going to add dependency injection we're going to test all of our services and even Noize the whole thing so we can ship it to the cloud and all of that good stuff I was debating between making this video or not because I see there is a couple of videos out there about this already but I've been creating all of these projects and apis in the channel but I've never really explained you how to create an API from scratch so this video is going to be a reference point for that regarding what we are building it's a simple project manager like jir where we have a couple of endpoints for authentication uh managing the projects and the tasks and so this is the rudimentary structures so we have the dependencies which is going to be a store or repository that is going to be injected into all of the services and then the services are going to be registered into a MX router that we're going to see how we are doing in the bits and then this is the overall structure has shown you so these end points have are guarded by dedication and then here on the AP server uh we are communicating with the relational database which is going to be my SQL but you can add anything uh you would like all right guys so let's get coding and the first thing that I already did is that I initialized the projects with the go in it and I have created a mic file with just a simple run which is with a build and a test as well so the run is going to build the project and then is going to execute the executable and here I have a an empty mind F that we can start coding and before we even start coding I want to mention that in this channel I teach you goang so i t I try to avoid using Frameworks as much as possible so you can learn the fundamentals in order to become a high value engineer so that means that we're going to use as much as possible the standard library of course we're going to add packages like my SQL driver and JWT token as well but those are important to make sure that we have a secure application and not Implement those things ourselves otherwise the video would also be hours long right so the first thing that we're going to be doing is that we need an API file where we're going to start the server and anything related to that so here um let's skip everything again on the main uh package so here we have an API server the API server is going to hold the address for the server so for example Port 3000 and then at the pendency which is going to be the only one that we have which is going to be a store the store as I shown you it's the it's the repository that's going to have all the connection to the database so this is the only layer where we have a connection to the database and we can inject this so we can easily test it right so let's have here a Constructor and I've noticed I have cality enabled let me just disable that all right so here I just created a Constructor for the server so we can go here to the main and say that we want the the API and let's just say new API server and we pass in the other which can be for example 3,000 and then a store which you don't have it so let's just keep it nail for now so just below this Constructor we need to want the only method that this server is going to have which is the serve so let me just add the method which is is going to be called serve or run or wherever you want to call it but the idea of this method is to initialize the router then register all of these services and their dependencies and then at the end we just listen for the server so we just start the server and walk for any errors so it's going to look like something like this so router. max. near router so for this I'm going to using Gorilla Max which is the only package that I like to include uh for HTTP handling all of that stuff if you are familiar with that you know that it's way easier to do this than uh manipulating the the path and the query parameters from the standard library on GTP Nets right I like to keep it on all my projects it's a standard for me then I'm going to create a sub router here and the idea for this sub router is so that we can prefix the API with/ API and slv1 which is also a good practice to have and then at the end we just log the error that might come from the HTTP listen and serve which I'm going to be listening at a port from the other so it's going to be from a parameter and then the Handler is going to be the sub router and this is it the very simplest um way that you can do this and then here we are registering our services uh later in the video okay I'm just going to also add here a print line just so that we say starting the API server adds and then just so I have some logs when you start it so the next thing let's go ahead and create the store file just so we can get ahead with this error so the store because it's an interface we can easily inject it inject Ed into the services and then because of that we can easily test them because we can mock this interface as well right you're going to see that in the bits so a method that we can start thinking about is about the users for example let's have the create user just so we don't have an empty interface for now and let's just return an error we're going to come back to this and change it later but for now this is it then just below let's create a structure called storage or Repository which is going to be a struct and this is going to be the struct that is going to be having the methods to communicate with the database for example it's going to receive the dependency of the the communication tool for example it's esql and then just below let's also create a Constructor new store and then we have theb DB we can also just go ahead and create the method for the star so that it implements the the interface storage and let's have a create users doesn't take anything and it returns an error let's just return nil and keep this empty for now so here on the main file what we need to do is create the store new store and then here we pass the database which you don't have it yet and for that let's create a new file called DB . go in here we create a struct called MySQL storage this is going to be the implementation for MySQL and it's going to receive a database as well then let's just create again the Constructor it's going to receive a config and this config is going to be coming from my. config and it Returns the MyQ config storage um my sqle storage so here this is where we initialize the database and make make the connection so let's go ahead and do an SQL doop and here we pass in the driver name which is my SQL and then the connection string which is going to be connection uh config format DSN and then when we just it returns this I need to import the package otherwise I am getting the errors and then when me just simply handle the error which at this point we can just fat log it because if the database connection fails the application is going to fail so we can just stop it then the next thing we do is we go to database so we can ping the database let me just remove this one and again handle the error so if you look here at the documentation you can see that open m just validate the arguments without creating a connection and to do that you need to call Ping here so that is very important then let me just do log. Sprints avenges so that we know that the connection is successful connected to my SQL and at the end let me just return the uh storage with the dependency in there right so back here at the main file let's create the myql connection so for that we need to have the configuration my SQL do config let's go back to this in a bit but let me just get here the SQL storage and get the my SQL storage and pass in the config then here with the sqle storage let's go here and actually get the database from it so SQL storage dot init this is a method that we still needs to create and if there is an error here I'm just going to file tell again with the error and then here we have the database dependency and here we can just say that we pass in store and there we go and here we can actually count the serve because we have already created this serve api. serve and so the last thing that we need to do is actually create the init method on the storage so let's go back to the database create the init so this method is going to return the actual pointer to the database and an error and the reason for creating this method is that we need to initialize the tables for the database and then we just return the the database so we already have it created but the tables are not created so here we're going to initialize the tables in the bits but now we don't have anything to initialize so let's go back here and finish the configuration so for the configuration we need the user going to be the roots for example the password my passwords address I'm going to keep an empty string for now the database name it's going to be project manager or something like that it's going to be DCP and I'm going to add some properties that I usually adds to avoid errors in the future and the par time as well okay so the first thing is that we should not keep these variables here as strings so what we can do is get them from the environment variables so let me create here config Doo this file is going to be responsible for having a hold of the environment variables in the run time of the server from the injected variables that we're going to have so it's usually best practice to have the environment the environment variables injected into the runtime instead of just having them hardcoded into the uh the project right so this is what's going to be the responsible of this file and the idea is that we have this variable called Ms it's going to be Global and then just going to initialize them from a function it's going to call in it config here and it's going to return a config object so the first thing is that we need the port and to do that let's create a function that actually um gets this from the environment Barrel so get M it's going to be the key name and a fback both of these are strings and it returns a string as well so this is the trick so let's do an os. look app f for this key and if it's okay if it exists then we just return the value otherwise we just return the file back and we need to import OS but this has an error here so I me just complete this and the key is going to be Port uppercase and then the file back could be like 8080 and now the project doesn't have eror so we can have the import here the next one is the DB user so these are the configurations that I've just filled in so the first one is that we have again the port the DB user the password the address for the database connection and then the name and lastly we have have the JT secret that we are going to need for the authentication so with this we can come up here and change this all together with Ms do users in this case it's the DB user like so and I think the main file is complete so we have the MySQL storage configuration we initialize it and we have a store and we are injecting it into the server where here we are listening so the last step thing that we need is of course start creating our services and the first one that we can start off is for example the uh the users or the tasks let me start with the tasks so we can have some functionality because the users is going to be the authentication so here each service is going to have their own service structure so they are going to be look Alik because all of them as you saw in the diagram are going to receive the store has a dependency so This Is How They are going to be structured and then we can have a Constructor for each of them ask service which is going to receive the store and it's going to return a task service pointer then the method the only public method that we have for the services is a register routes which is responsible for registering all of the routes for example here we have the handle creates task and then the get single task for example as we saw here on the diagram again these are the end points that we're going to be creating for the tasks and then each of these is going to be also a method from the service so handle create task it's going to be receiving an HTTP do respon writer and as well as a request a pointer to the request as you might be familiar and then the other one is the same so let me just copy it there we go so here we can go back to the main inside the serf and actually register our first service so it's going to be the tasks service and new task service where we pass in the store and here we just initialize it task service do register rout and we pass in the router and there we go now we have our API with these endpoints so before running our application there is one thing that we need to make sure is that uh because we're running this locally and my setup is that I have a Docker um Docker image running my SQL locally and these are my credentials and then what we need to do is make sure that we have a database actually called project manager right so for that let's do that manually so I'm here at my SQL clients and I'm just going to do a create database project manager there we go now if you run the project we should connect be connected to the myle and then we are listening at Port 3000 which is what we are awesome all right guys so here back at the implementation for creating a dusk the first thing that we need to do is actually get the payload from the body so what we can do here is we needs to access the the body from the request so you're going to do iio do reads all and here pass the request. body like so let just handle the error than so so here I'm just going to return for now and then we're going to create um a helper function that we can use on all of the responses that we write Json for it so let's keep this for now and then let's close the body very important but close like this so the next thing we need to do is actually converts the slice of bites into a structure that you can read which in this case is going to be like a structure of a task so let's go ahead and create a new file called types. go inside here let's create a task struct and the task struct is going to basically uh hold the The Entity or the information that the task is going to have but before that let's go ahead and initialize the tables in the database because we haven't done already and that way you can already translate that into a structure so here the first thing that we need to do is actually create three tables so for the the projects the tasks and the users so here I have created three functions and we are handling the errors one for the projects and so on so on so for the create projects table here we are creating a table so this is we just executing raw SQL here and we create the project which has an ID a name and a created time stamp as well as for the tasks we are creating with an ID it has a name so a task has a name a status and the status can be to-do in progress testing done and the default one is to do so we don't need to pass it when we create a task and then it is associated with the project ID and Associated again to a user ID as well everything with forign keys here and finally we have the create users table very simple a user has an email first name last name password and a time stamp as well so we can save this next time that we run the project all these tabls are going to be created right so back here and the types if we check what we already created here in the database we know that the task it has the structure and I have just translated that into going here right so here what we can do is actually get a hold of a pointer to task and now we going to UNM Marshal this so we're going to do Json do and Marshall and we're going to send in the body and we're going to UNM martially into the task and handle these errors appropriately which just going to return for now we're going to go back to this in a bit and then we have a task if everything is all right to have a task written here but there is a problem which is there is no validations inside the task so the name could be empty or either the projects or the user could be empty so we need to have a way to validate very easy let's create here a function that is going going to validate the task payad okay and let send here a task then if the are is different than nil we are going to return um yeah so let's go here below and create this function that's going to receive a TK and it's going to return an error so for the validation this is what I have done so if the task name is empty I return a predefined error that I have created here so there is three ones um this is just so we can easily access them on the tests and match them in case they change in the future they are a constant here so the project ID the same and the assigned user as well so here let me just fix this so if everything worked out we can go ahead and go to the store and create the task in data database so for that let's do s. store. create task it doesn't exist yet and we're passing in task so let's go back to the store and add the create task uh to the interface right so ites the task Returns the task and the implementation is going to be pretty simple as well so here I have the function signature and the first thing we do is that we execute an insert into the database we just values and then we basically handle the error and we get the W inserted ID and append to our structure and return it to the user so we have a new ID um updated so back here let's just handle the error return it and with that we are done so here at the end we'll just return to the user the task and this is where um it makes sense to create some reusable function to handle Json to the user right so here let's create a nos. go and the first thing that we do do is add the package main of course and then we are going to create a function called right Json like this so it receives an HTTP response writer so we can actually respond the requests to the to the writer a status could be 404 200 and V which is the payload of anything right just import the HTTP the first thing that we do is that we set the header to be do set and it's going to be a content type so we need to set has Json application SL Json then we need to set the status header so we do right header with the status and finally we encodes the pilot or the response has the V which is anything so we can just send anything we would like um there's another thing that I want to add here actually it's not going to be here it's going to be on the types and it's going to be uh type error response and this is going to be to be consistent when sending errors to the user because we might just throw uh different message but we want to send ad Json with the same structure so any front end any client can consume and expect the same output for an API and that is very important to be consistent when writing any sort of APM so back here at tasks we can just call the right Json send the W and do an HTTP do status created and then we can send just a task right so this is going to work out and then on the errors what we can do is so for this one I just write Json i s that it is an internal server error because if the creation of the tasks fails it's probably on our ends because the validation should take care of that and here we just send the error response like we had created and we're going to do the the same one for the for this one so instead of being an internal it's going to be a b request it's probably because the user entered about p and here we say error um could be actually this one is dynamic because the error comes from the validation so it's error. Error like this and then where do we have more we have here on adjacent which you can say invalids request payloads and it could be the same one for this one right here okay so this is how you're going to handle uh this so here I have uh an HTTP client open you can use Postman or something like that but I use these clients on vs code which is called tender clients and basically here we have the the URL and let's hit sends and I'm getting a 404 um so something is wrong with the TK URL which I don't think it is uh let me see on the API because I think yeah I see I see so instead of passing the router we need to pass in the sub router because it is SL API SLV if we just pass the router it's without that so we want to use the prefix so make sure that we correct that as well and let's try again so we got an invalid payload and it makes sense because if you take a look at the task validation you see that we are failing here because we are missing the name the project ID in this case we actually have the name but we are missing the project ID and the assigned to ID as well and the reason that I'm not passing it yet is because we need to create projects first in order to pass them and users as well right otherwise we are getting uh an SQL error because those are foreign keys and they are validated against the tables so we can go ahead and move on we can make sure that this is actually somewhat working but I'm not 100% confident yet so what we can go ahead and do is create a test for this right so let's go and create a tasks test and this is going to be where we actually test these end points and see if it actually works before we move it to production so let's start with it so here I have a test create task which is going to handle the testing for this Endo that we just created and the first thing that we can test against is should return an error if name is empty for example and the payload is going to be something like uh the task with the name being empty for example and how do we do this test is that first we need to encode this as Json so let me go ahead and transform this into byes which Json do Marshall pass in the payload and now we have that we can actually just make the application crash here the test so let's do a t do fatal with the eror in case that fails which is unlikely and then let's actually make a new request so this is how you test HTTP handlers HTTP post to SL tasks so here we don't need the prefix for the SL V1 because we are creating a very small router for this test we are not using the api. go so this is not it is not being run when we run this test to just make request for this and Below um we are going to be writing the let me just show you I think it's easier so here we have a new HTTP test dot new recorders and then here we have our router right max. new router so for each chest we are creating a very small router and then we just say router. handle Funk and we say for example this is the only route it has and it's the service do blah blah blah so in this case it's going to be the handle creates task and we need to First create the service as well which going to go back to it in a bit but here let me just en code the the P has byes so here you do by. new buffer and we pass in the bites that I have here so the reason for this that if you look at the new request it actually requires a reader and not just btes so we need to make this reader of bytes so how do we actually get the service from this one let me just go ahead here and do a new task service and this is a problem now we need a store and this store if you remember it's the interface of the dependences and we cannot pass the storage that we just created on api. goo on the on the main not go actually sorry we cannot pass the storage because this is communicating with mySQL and we are building unit test and not integration test so we don't want to be mutating data on the database so we need to create some sort of mock for that interface so here I have a memory storage which I'm going to go with and I'm just do mock store which is going to be a stru that we're going to be creating um we can create here for example uh store. D.O and this is where we're going to hold all all of our mocks of the uh the storage so let me just call this Mo here and this is the structure and every time that we change that interface we need to change this as well to match the interface implementation so if you look at the store here it has these two functions so we need to implement these two functions in order for the mock store to to work so let's have the create which just returns an error and this is whatever you want can just return return no and then again we need the mock store for the cre create task and we can just return nil nil or wherever you want here I'm actually just returning an empty task like so and so this is the beauty of the pendy injection in go which is you can just Implement any interface and you can just inject it however you like so we could create here an implementation for this test or something like that and it would work so if the implementation is implemented correctly we are not getting an error if it is so if I go here into the the start. test again and I remove this one you can see that here we have an error saying that it does not Implement story it's missing the create TK method so it's very nice as well that we can see these things and here below it's not doesn't have an error and we actually have the handle creates task uh ready for that then what's missing is that let's go to the router and serve HTTP for these and these requests so this is is what the request required that we just created and that this is the request that we just made up so we just cooked this uh fake requests and here we have a recorder that we sent for this router for this test so this is how we create isolated tests in gank uh using Gorilla Max for example but it works with any package as long as you understands what's happening behind the scenes so the first thing that I want to check is if the code is not in http badri because these should fail if it's not one then let me just do an error um inv valid status codes right and let's go ahead and actually try this test we have here an error why it's because of the error so let me just handle this error really quick errors and this is it so let me go ahead and do a make test and all the test pass if I change this to be a status accepted for example and run the test again this is going to fail it fails so we are more confident that the validation that we just tested is working as well I have just created here very quickly the happy path so it should create the task here I'm passing a valid pad I do the same thing I Marshall it I send it and then at the end I just check that if the it is different from created and if we test this we see that everything passes right so if you want to get a hold of all the tests I'm going to be leaving the whole project on the description below so you can follow from there all right so here on a get request the first thing that we need to do is if we look at the signature of the endpoint we are receiving an ID so we need to get that and this is why I really like gorilla ma because we can just do this and it's way easier to pass it than if we would do with the standard Library we can even create a function to abstract all of this if you did that many times but I want to show you how you can do it first um so this is how we get the ID um if the ID is empty then we also need to add a validation for that so let's just write Jason and say that the ID is required and then let's just return out of here then we got an ID what we need to do is get the task from the store so let's go server. store.get task we don't have this method and we are passing an ID and that should be fine let me just handle here the error really quick so I'm just going to say here that the task is not found um yeah I think that is fine then let's go here to the store so here on the store interface what we need to do is add the get task so here we're saving a task um actually it's going to be an ID right and then we return a task and an error if there is one so let's go here down below and do like so so this is what we're going to do so we're going to have a variable to hold the task right and then the way that we do this is uh let's do select ID name status and everything that we want that we have in our structure from the tasks and we filter by the ID and then we scan the results from this into the task like this so here we are passing the pointer for the properties that we have selected and then we just return this to the user and we are done with this let me just as well go to the store. test and add a new mock for that here so that the tests don't fail and here let's just return task and no like so okay let's go back here we don't have an error and we can just return um to the users that we have on the task so let's do an HTTP do okay with the task can hand and this is the get so it's very simple we could explain a little bit more but this does the job for it and then here on the test let's also add a case for the get so let's do Funk test get task and I'll fill up the rest so we can move a little bit faster and here is a simple test for the get task I just noticed I have here a typo so it's very simple the difference is that we are doing a get method this is the different signature as well and then again we search if the um the code is okay instead of um created now there is a thing that I've noted while creating these tests and I think this is one of the benefits for creating tests and goang and why so easy to create test is that there is no way actually to have an invalid ID here of course I could put a string but let's not go into that deep so I think this validation here can go away and just like this because if we deleted this if we make it like this what would happen is that it would throw 404 or it would go to another request because it would not match this string so it's completely fine to just remove that validation there okay so if we record back to the initial diagram we've just created these two endpoints for the service and they are guarded by authentication right now we have no way of doing that here right so these are authenticated anyone can get and create tasks so let's fix that and add authentication to our API to do that let's create a new file called author. go and you can see the partent that I'm going for so anytime we need a service a bit of functionality we just create a file here on the main package and we add it uh very simple so without much trouble so what I'm thinking of doing is creating a higher order function some sort of way that I could drop each end point that I want to add authentication and say that uh for example this could be with JT authentication or just with JT o and then I would drop this Handler with a pre-check so this would run first and then it call this function if it would be authenticated if not it would just throw some sort of error to the user um some permission denied so I think that this is what we're going to go with so here let me just create a function with this name it's going to receive a Handler fun so that is a Handler function and that signature is from HTTP Handler Funk pretty standards so let's just return the Handler Funk and then inside here we're going to have the algorithm the logic that we need to validate the token so the first thing that we need to do is actually get the token from the request so inside the cookies or something like that um actually it's going to be from the header so we're going to have an authorization authorization header we're going to get the token from there then we validate the token of course if the author header is not there we just show unauthorized then we Ved the token then we get the user ID from the token so inside of each token is going to be encoded the user ID so that we know from which authenticated user is and then we just call the Handler Funk and continue to the end points right this is the overall seure that we're going to be building so to get a token from the request we need a function so let's create it down here I called it get token from request so we just pass in the request and we can get the token we can use this here or even on the functions as well so the first thing is that we get the token we can get it from the authorization header or it could also get it from the URL has a a token query string so it would be for example SL API SL blah blah and then we could get it like this um the token then we check if the token is not empty and then we also check if the query token is not empty because if this one is not empty it exists and it just gets the the header otherwise we get the one from the query else it's empty and we just return it um like that so let's consume this here let's call this token string pass in the request and then we need to validate the token so let's have a token and error and let's maybe create a function called validates um GT where we pass in the token string let's create this function below it receives the token and it returns a JWT do token this is from the JT package for goang so let's just import it here it's this one that I'm using there is multiple flavors of this but uh this is the most used one then we need to get the secrets so this is the secret that we created on our config if you remember this one this is how we are going to check um and decode the token so we need this key to open the token otherwise anyone could open it so let's go to Ms do JBL secret to already had this set up from before that is awesome so let's just return at JBL t. pars we pass in the token string and here we need a function that is going to run whenever we pass this then sign here what we need to do is check if the uh the signing method is an h mark because it could be RS or something like that so here we check if the method is an h mark and then we just return an error if it starts um and then we just return a slice of bytes with the secrets so the last thing that you need to do here is then handle the error so let me just check if the error then we return from this F because you don't want to continue um calling the end point if he's not authenticated and then we need to say to the user that he's actually not authenticated so let's have a log here that so that we know what's happening and then we just write Json with an authorized error and the response that permission denied um so here could be a print LM and this is what we do then now that we have a token so we have a token of type able our token we need to validate it as well so now that we have a token we can start to see and get the claims for the token and inside the claims is going to be the user ID but first we needs to add here a token so that's where token do um valid I think this is it so if the token is not valid copy these lines here and paste it here so you can see that this is going to be repeated so when you see that repetition let's add a function to do that so permission denied and what we can move here just this one and we could even I'm just going to move the log because the the right Json because the log could be different so this is going to be an HTTP writer there we go and then we just replace these with permission denied and that's there we go much cleaner so to get the claims what we can do is go to token. claims and map this to a map claims type and then we just get the user ID from this claims uh has a string then we need to go to the store and get the user but at this function we don't have access to the store as you can see up here so let's go add the dependency into a store and now we can get the user by ID so this method does not yet exist so it's something that we are are going to create but let me just handle the error here just like the same above let me just copy it actually if ER is different than n we just return this and here I'm going to say failed to get user and then at the end if everything works out we call the Handler funk by passing in the writer and and request and this is going to call the end point by at the end so let me go to Star real quick and add the get user by ID function so this one is going to receive a string and then we just return a pointer to user which you do not have yet so let's go to types let's create a user struct and the user is going to have an ID the first name the last name and then also the password and I think it also had let me check on the database it also has the cre that so let me just copy this line here as well so this is going to be the user stct and I create the functionality for the storage get user by ID here we also need to theil almost forgot and very similar to how we get the task uh we get the user so just select the user and filter it by the ID and this is done so let's go back here to authentication and it should be completely done so we have this IR order function that validates if a request has a token associated with it right so let's go back here to the handle Funk and see how this works so we need to add the store dependency and there we go so let me also add authentication here to this end point because it makes sense as store and there we go so this both end points are authenticated as we designed it so let's test it out now if he tried to create a task let's hit send and we got an error because the server is not running of course so let's hit send again and we get an authorized because we don't have a token so we have a permission denied and here are the logs that we wrote so that is working we have authenticated our services so let's make a way to create a user so we can Al also get a token but as well as a user in our database so again very quickly I have created a new file which is the user service and just like we did before this is the structure so we have the user service truct and as well as a Constructor I registered the rout for the resistor which is not authenticated which makes sense right we're authenticating right here and here we are uh with the resistor endpoint so the first thing that we need to do is get the payLo so it's going to be the email and the password so let's start reading the body and then we close the body and then here we Marshall everything uh into the user Str right then now that we have the user payload what we need to do is create that user but just before that I think it's wise that we validate this payload again so we should see if the um the user is actually filled correctly so here we have the validate user P just just like we did with the tasks and here we check if they the email is empty if the first name last name and the password are all filled in so because they are required if you want to make the first name and last name optional you can just delete them from here I want to make them mandatory so I'm going to leave them and then here just validat and thr an error if not so the next step would be to stort the user directly right so here we have a new method that we're going to create the create user and then we just validate the error but there is a problem here which is is if you take a look on the payloads we are sending the password has plan text and we are storing as plain text which is not a good approach to do it so we should encode the user's password before storing it in the database here um so that is what we're going to do we're going to Hash the password so we're going to have a hashed password and let's have a method for that called hash hash passwords where we send the payloads do passwords then let me just handle the error right now and then the last thing we do is go to the uh payloads do passwords which is going to be equal to the harsh passwords like this so that password is never going to be sent to the database only the hashed one so let's create this function inside of our off service just below here we're going to pass in a password as a string and we're going to turn another one has a string like so so the way that we're going to do this is that we're going to be using a package very famous one called bcrypt and that way we can make sure that our RPA is secure because this is a very thrusted package and anything related to cyber security and all of that um and encryption we should not be handling that ourselves and leave that to someone who knows it better so this is what I'm going to do so bcrypt do generate from passwords where we send our password and a default cost as well I handle the error and I just return a string of the hashed um value right so back here I just need to import our package and then on the users we should have the hashed password on the payload so back here in a store this is the very first method that we actually wrote in our store and we need to update it with the payload so it's going to receive a user and it's going to also return a user because at the time we didn't have any of this so let's go here and delete this implementation and implement it ourselves again this is the implementation it's very similar to what we did before for the task so I'm just going to skip it so I'm inserting into the users and I get the last inserted row like this and now we have a user stored in our database and the last thing that we need to do is actually create a token and a session for that user so I'm going to go here and do a token error and let's have a function called create and set of cookie so we're going to set directly in the cookies for this user ID this is what we need and let set here the response because we're going to need this I'm going to go down here below and create this function so for this method what we are going to do is create so here I have the secret from the or virus and we're passing it to this implementation that we're going to see in a bit I handle the error and then I set the cookie for authorization you could set a header it could set whatever and then we just return the token so let's go back to other dedication and create that function so this is the function for the create receives the secrets as we saw and then the next thing that we do is let's start by creating a token with claims so let's do the do new with claims and here is a signing method that we are checking before that you saw it and here we have a map of claims we're going to send two claims the first one is the user ID and then the second one is an expires so it's the expiration date for the token you can add here the time that you want and here we just get the token has a string so that we can just return it here as token string and an error which is not going to happen so we can just return return n I've also noticed that here on hashed password is not returning the error so let's go here to the users and let me just fix here I'm going to ignore the no I'm going to read the error and then here have user ID which should be ID and we are returning a string string and an error just like this so this function is complete here we got a token and an error and we can just return to the user finally that the user has been created and we can pass in the token for the payload for example um just need to handle these errors really quick error creating session for example and this is the register and let's go ahead and test it so I'm back here running the server and I'm on my ther clients so this is user SL register so there's one thing missing before doing the request that is that we need to go to the API and register this new service so let's go here and let's do user service and do a new user service where we pass in the storage and then let's just register the routes like so now we can restart our server and go back to the HTTP clients so there we go we created a user we got a token let me copy to my dashboard and I also to my clipboard and I also have here the cookies set has authorization and if you go back to the database let's refresh the users table and you should see the users and the password which is hashed as well so we we're not staring it as a text file so now let's try again and create a task so if you send we still get permission T night but if you go here to the headers and add a token let me go ahead and try to send it we got project ID is required so we got authenticated but we are still failing because the payload is wrong but that is not the point right and as you guys can see the API is basically working it's getting shaped and the only last things that we need to do is actually complete the project service so it just need to basic basically add this logic and I'm going to leave that to you if you want to complete if you don't the code is going to be on the description but this video is getting longer so the last thing that we need to do is add a Docker image and I'm going to show you guys how we can do this let's do Docker file so the first thing we do is we say that we want to get the docker image the off one from this version so in my case I'm running goang 1217 I geted version and I'm going to call this stage has the build stage then I'm going to set the work there has SL app like so then let's copy the go mods and the go sum as well to the current uh directory then let's run go Mod download after that I'm going to cop everything that is a go file into the current directory and finally let's just build the project so it's going to be go build slash API but before that we need to pass in some parameters which are these ones let me just indent like so and um I think this is it for the build stage then for the okay so let's add the Des as I said so let's do a from build stage and let's call this has run test stage and all we do here is run go test SL veros everywhere okay so then finally let's create the release stage so here I'm importing from scratch scratch is an empty uh it's basically doesn't have anything there so we get it we call it release stage and finally we say again the work there is going to be slash app on this stage as well we copy from the build stage like so yeah it's correct so we get from that build stage we get everything that it got so those binaries and we are exposing the ports 8080 and at the end we just call the binary that we just copied this/ API so to test this let's open the console and do Docker builds goang rest API something like this this is the name and the docker file is located here let's see if it works so here we got an error and I think it's because this is wrong it's not like this it's like this there's also a space here but that doesn't matter let's try again there you go so the docker image has been built so we just need to do Docker run and the name of the image and you're good to go but yeah guys so this is is it we have built a complete restful API in goang with multiple Services authentication and we have even others uh dockerization so we can Shipe this to the cloud very easily and even a relational database uh to the server so this is how I usually structure my projects if I want to go uh really fast hopefully you learn something valuable that you can use on your future projects and if you liked it give a thumbs up and subscribe if you want to see more of this so see you on the next one
Info
Channel: Tiago
Views: 28,447
Rating: undefined out of 5
Keywords:
Id: 2JNUmzuBNV0
Channel Id: undefined
Length: 59min 29sec (3569 seconds)
Published: Wed Feb 21 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.