Loading
Lesson 74
Courses / Software School
Intro to Golang (Building a JSON API Pt 2) - Software School (2024-08-31)

The lecture picks up from a previous lesson and further develops a backend server JSON API with the Go programming language.

Besides reviewing concepts and code from the previous lecture, you also learn to read a file with Golang.

The lecture uses a book as a resource for a bookstore application.

Then you learn to make an endpoint to send back a list of resources back to the user.

Then you learn to make an endpoint to send back a specific resource (if it is found) back to the user.

The lecture also teaches how to restart the program automatically after any code changes with CompileDaemon.

You also learn to set an HTTP header for all desired routes by defining a wrapper function that returns a decorated function.

Video Transcript

Okay, so today I'll talk more about the Go programming language in a particular example of building an API backend server that serves data in JSON format. If you don't have Go yet, you download from this website, paste to the Zoom chat. The code picks up on what we wrote before, so this depends on you having this code from the repository here. And the KTAC world slash intro dash to dash goaling. I'll be using several tools to test the API. So I use Postman. This is the one I use, but some people like mentioned the last time using Somnia, so this is the website. If you like using that, you prefer to do so as well. Or you can just use Carl on the command line if you're comfortable with that. Okay, let's go. Let me share my Visual Studio code. That's what I'll be using today. So I have my project directory. Which one do you recommend? Postman or Somnia? I personally use Postman because I'm used to it, but either one. I just mentioned in Somnia because somebody said they liked that last time, so I was curious about sharing that. Anyway, use whatever you're comfortable with. So the repository is like this, basically we already had some code written out. Basically main.go is the main thing. So in the Go programming language, we have to define the package main that has a function main that will be the starting point of the program. And we saw that we can run it in two ways. We can just do go build and specify the files, which end in the extension go. So if I say main.go, that's going to compile that into an executable file. And I think this one is dependent on, I need setup as well. Let me see. Yeah, if I just do main.go, it's not going to work because I'm referencing this setup function that we created the last time just to demonstrate that I can have code separated into different files. You don't have to have everything in a single file. So I created this file with the function hello, sorry, setup. And I just demonstrated that we can call that from a separate file like this. Okay, so we also have to specify setup.go here. Some people might consider using a make file if you ever heard of make the tool. You can build a make file and always have this command execute instead of you having to always write it yourself. And this is one way we generate main.exe, I'm using Windows, that's why there's an extension. So I can execute .slash main.exe and that should bring up the server. For me, I'm in Windows, it's asking firewalls block some features, I'm clicking allow. But I actually know how to bypass this, so I don't have to be clicking allow every time. Let me just start off with that, okay? So in the main function line 47, we have a call to this function listen and serve. That's part of the package HTTP. Now this package is important if you go to the top, there's an import for net slash HTTP, that's where it's from. Now what I want to do here is for the first argument is basically the address IP, and I omitted the IP or the alias kind of thing. So I can put local host here on the port 8080, so I don't have to be clicking allow all the time on Windows. I notice if I do that, that doesn't bring, come up again. So because the second argument, by the way, if you're not familiar, if I'm using right now the extension, if you click extensions, if you're using Visual Studio Code, go extension, go team at Google, this is the one I'm using so you can install it. If you're using something else like some other IDE, maybe Golan from Dreadbrains probably has the same functionality. Basically with this extension, I can point my mouse over the function name, it will tell me some documentation, so I don't have to go to the Golan website and read it there. I can see the second argument is a handler thing here, and I put nil, which is basically the null value in go, so I don't have to specify that. If I don't do that, it's going to use the default serve mux. If you read this line here, the handler is typically nil, in which case default server mux is used. So that's why. Anyway, if I do another way of running the program, let's say I delete main.exe, because I changed my source code, I have to rebuild, compile again, it's not, go is not an interpreted language in that sense, you always have to, if you change the code, compile again. So I could do the same command, or I can do go run, main.go setup.go, and go run is basically the same, but it builds, it creates an executable somewhere in a temporary location, and then the program runs, and when you close the program, it's going to delete the executable, and you don't even ever see it. It's meant to be like, to make you think it's an interpreted language, but it's really not. Okay. All right. So it's working here, the server, if I go in my browser, see here, localhost, ADA port, separated with colon, slash, I think we have a hello endpoint. I should see hello world. By the way, I'm using Firefox browser, but it can be any browser, or you can use postman, right, to test it out. This is a get request. Okay, let me see, we are all, let me show you how to do a postman, in case you never use that. Here's postman, if it's the first time using postman, you're probably going to ask to login or something, you don't have to do that, you can click on the bottom to skip, and then it should bring you to this thing, you can click new or the plus, and you can change the verb of the HTTP request. The browser is always get, so let me do get, hp colon slash, localhost, colon, 8080, and it's giving me 200, so I know it's okay, slash, hello, it's hello world, let me make this bigger, if you think it's too small for you to see, let me know. Okay, make sure to do the HTTP, we're not using HTTPS. Don't make that mistake, sometimes we forget, all right, great, yeah, so you would change, if you want to post request, change the post, and that will become post, which is something we cannot do through the browser address bar directly. Don't think we have a low post, right anyway. I think we do have get slash books, slash one, that's the one we did, but we want to be able to make two, three, and so on, which we don't really have, all right, I'd set up multiple methods for routes, yeah, so as we go and see the code, make sure I'm sharing the right screen. So from the code, let's recap, we have the main function, the entry point from the top of the body, we are calling the handle func function from the HTTP package, and this one is basically, if you ever use other languages, like for example, JavaScript, or don't just express is basically the app dot get or dot post or whatever. So this one is just a catch all in case and no other route matches, so the slash is actually catch all is not really the slash. So it could be slash unknown, it would hit this one, basically, this is the path. And this is the function that I pass as an argument. That will be the handler meaning when I hit that specific endpoint, it will call this function here. Now let's look at the second one definition here. So this one called to end of function, that one has a method. So starting go 1.22 version, you cannot actually do this one. Otherwise, if you had a previous version of go, you couldn't do that, you would have to look at the, in the handle function, you have to look at our dot method and check if it's post or get or whatever, or you could use gorilla mox, which is a module package that you have to download third party, and you would do dot methods on that. Basically gorilla mox, you do like this, if you want this to be posed, if you want get, which is default, not default, sorry, you would do like that. Anyway, we're not using mox, we're just using the default, but since 1.22, make sure you're using version 1.22 and above, I think right now is 1.23, and that should be okay. Anyway, this is, the function is not defined in line here, it's actually defined elsewhere. And let's, where's the definition, if I scroll up, it's right here. So this function definition is defined what we call package level, you can think of it like the global scope in other languages. This is the package level when it's outside the function. And usually the functions also always have two arguments or parameters. The first is what we usually call W, which is a type response writer, that's in the HP package. And the second is a pointer to under request, that's part of the HP package as well. If you have the extension, you can hover over it to see what it's like. So the type response writer is actually an interface that has these definitions here. And the request is actually a struct, which is a structure. If you ever use C language, it's very similar. I don't know how new you are to Golang, but you notice many things have the first letter capitalized, like for the properties or fields of a structure. And there's a reason for that, usually when you have a package and you want to use a function that's defined in another package. For example, let's say this handle func here, that's a function defining the HP package. And if you want to use that, like as a consumer of that, right now we're in package main, right? That's not HP. You have to have this function defined with the first letter capitalized, otherwise it doesn't work. It's just the way the language is. They chose to use the capitalization of the first letter to make the function available to those outside of the module or the package to consume. I guess you can think of that as, what is it, public versus private, members of a class. Golang is not really object oriented, there's no class. So you have to go pretty much follow these things. Like I said, even when I have to be careful with my words, because I'm very used to talking about objects when you have things, right? But it's not really object and go, it's just like a type. For example, if I were to, what is W, right? Usually I would say object, that's not really object, it's a type, the response writer. And it just happens to be defined as an interface. And this type definition here is like the type def, it's like an alias. I don't know, if you know about C language, there's a type def that you can alias. So you don't have to type the thing over and over again, you can give it another name for a specific type. In this case, the response writer is a type in itself, but it's actually an interface and then request is a type too, but it's actually made using a struct. Anyway, let's try to make this better. I don't really like, let me read your comments. You're just a PHP, first time really hard, yeah, learn go. Okay, PHP is, I guess, a little bit different than go. Yeah, so the goal, like in go, I guess the initial goal is to be very simplistic in a sense that they try to, they try it only in terms of language constructs. There's only one way of doing one thing, like if you want to do a loop in go, we can only do it with a for loop. There's no keyword while, so they do everything before keyword. It's a bit weird, they have the for that behaves like while, they have the for that behaves like the traditional three section go for with the initialization condition, and the command increment. Anyway, I was going to say that many things are kind of strict in go, for example, you notice that I always put my races in the same line as the function declaration. That's actually gives compiler error. If I try to do this, it's going to give me an error you can see from the extension. It doesn't like that. And even if I try to compile it again, it gives me syntax error. So it's really strict, you got to put that in the same line. And then if you notice if I put spaces, they also the extension and when I click to save will auto format for me. And in terms of indentation, they always use tabs, you can see I highlighted this, there's a hidden character there, I don't know if you can see there's an arrow. And that arrow character means there's a tab character backslash D, which might be weird some people because some people for me, I use always two spaces for for indentation. But the go people, they always go stabs. So it's very strict about that. You got to follow their convention. Yeah. Okay. Enough of that. I really don't like what's going on here because I don't have a way of getting a specific book. But before I do that, let me explain the setup thing. I think I wrote this in a previous lecture to show people that I can actually define all of these in a separate file. I don't have to define it here in line. So I put in setup.go, which is part of the same package. So if you want to define a function in another file, it's fine. You can define it anywhere, but you have to follow the following rule, for example. In this case, setup is supposed to be a function defined somewhere in the package for main. So I have to go to my other file and also make it package main at the top. That way, go when it parses through this, it will put everything in the package level. So when I say setup from the other file, it understands that it's this function here. Even though they're in separate files. But you've got to make sure that you're there in the same package here, okay? Now some people want to do it in a separate package. That's also okay. They want to organize it that way. And I demonstrated that before of this math package that I created here. I created a director called math3. And I put a function sum here. And it's now part of a different package. Now if I want to use this, remember, if you want to use a function from another package, you've got to make the first letter capitalized, otherwise it doesn't work. So I went to main.go, which is in the package main. And I use the math3.sum like this, right? The package is math3. And the function or like a call is sum with the first letter capitalized. So that's why you, how you can create a totally separate package and call it. Now you've got to import, obviously, up in the top. You've got to import it right here. Now this might be a little bit weird. Where's all this stuff? Well, in go, when you want to create what's called a go module, you can type go init. And then you have to specify the name or path to your module. Usually the way it works with go is it has to be, the source code has to be somewhere on the internet, like a GitHub repository. So that's why you always have to do something like github.com, for example, or GitHub, wherever it is, slash the owner slash the name of the package, the module here. And then when you press enter, that should create what we call the manifest file. And that's the go.mod. And that will create with whatever you specify for the module name. And here's the go version that I'm using for this module. And once you got that down, you can import things. Whenever you import something, it's like this will be there because it's part of your module. And this is funny because this is actually the directory name, not the package name. So I think if you were to rename this to math like 3-1, you would have to change it here. Even though the package is still math 3, let's see if it still works, that experiment. I should see the number here. Number 3, so that still works. So that's what's a bit confusing, like you have to be careful. So this import is just the directory name. And the package name is actually math 3. So that's why this confusion, that's why people would like to change the directory name to always the same name as the package to avoid having to think about those weird things. But be aware of that. I guess in PHP, what do you have? Manifest file, Composer or something. That's the package manager. You have a manifest, yeah. Yeah, they created that for go. And I just want to talk more about that. You can install dependencies with go install. And the package path and the add sign and some version, usually the latest. And it's supposed to get directly from the source code instead of going through like a package registry. But there's actually what they call a proxy server that basically the first time you download the package and it's not in the proxy, it will pretty much cache that in the proxy the next time you download that. Basically, when I say go install some third party to install somebody else's code, I will look, is it in the proxy? If so, I'm going to use the cached version. Otherwise, I'm going to go directly to the source and download that from the GitHub repository. Something like that. There's a mechanism there. Anyway, okay, and set up. I made this route hello. That's fine. Let me see your question. I couldn't understand why you didn't use Math 3 as directly with package name. I'm going to use GitHub name. Yeah, let's try that. Like this, you've been, just Math 3, and you notice they type Math 3 here because I had a different name. Yeah, let's try that. Yeah, so it's saying Math 3 is not in STD. I suppose the standard library, which is in my C program files, go source slash Math 3. So it's looking there in the standard library. Let's try dots, you said dot slash maybe, let's try that. Dot slash is relative, relative parts are not supported in module mode. Yeah, so it doesn't really work. So you have to go, because you need to go mod, you have to copy this module here and put it there. If you find a way to do, to make that work, let me know. Just the way it is. Okay, do we have a function to gather the books? Let's see. We have single book. We have set up. Okay, let's do get all books. Let's show the postman get slash books. I don't have it, right? Oh, actually. I forgot to power up my server. Go around main go, set up go. Okay, let's do slash books. And to do that, I'll create a file. Okay, I don't have a database right now. So let's create a file, JSON file, and let's just add a, like an array of objects. And let's add ID with a string here. Let's say one. And then I guess we're dealing with the example where you were working with is like a bookstore. And we have books. Like it has an idea of a title. So title would be, I don't know, whatever book you like, put it, put it there. And the straight three of them. And the straight three of them. This is just dummy data for us to have something to work with. And it's like a database in a file. Let's obey the JSON format. Okay, this looks good. I'll copy for you if you don't want to type it. I pasted the zoom chat. Basically an array of objects and each object has an idea title property and a value for each is a string. Okay, the idea is a string. With that in mind, going back to main, let's define an endpoint for this. How can we basically, we want to read the file, right? It's going to be a string. Then we have to decode that basically make a kind of object with that string of JSON and take that and then return back to the user through the response. So I'm going to go up here. Let's before line 41. So to define an endpoint, we have to call handle funk, which is part HP module or package handle funk. And it gives you the extension gives you the hints. So you can know what argument is what the first argument is a string. So you have to say the path, for example, slash books for the resource. I'm using rest. Representation straight transfer convention. And you have to have a method, right? If I don't have a method here, it will basically match get, post, delete, put, whatever method. Now, before go 122 that you couldn't do this, but now you can do it. Otherwise, you got to use border box like I told you before dot methods. Get like this. So if you ever use gorilla mugs, that's just you got to do like that. Anyway, now it's nice. We can do get all caps space here. That way only matches the verb get. And then second argument is the function. So how do you pass a function as an argument? So you have to define a function of fun keyword. And then you have to define the parameters. Now the first is w the seconds are, and then you got to define the types of space after the name. And then this first one is what we call writer response writer, but it's part of the HP module. So I got to say the HP dots. And then the second is the response. And it's a pointer to a response. The star means pointer. I don't know if you come. You said PHP. I don't know. PHP has pointers. I don't remember having pointers. And then HP dot response. Yeah, like this. And yeah, maybe. Yeah. Okay, great. Yeah, you have to be kind of explicit in the search for the type for go. In this case. Okay. So what is it complaining? Spunk. Did I do wrong here? I wrote it right. HP response writer our star. And the funk. I don't know why it's complaining. Anyway, let's see. So we got to read the file first. So to do that, we got to use read file function. I think there's to read all. It used to be I owe you to dot read all. Let me check my thing. There's beeping. Sorry. All right. I think they changed it. It's deprecated now. Let's see here. Let me check the docs. So let's check the docs. I'll show you how we can reference the docs. Let's make practice. So the docs, let's go to the browser. I can go to the programming language website. I think docs or standard library. There you go. I owe you to this one. I'll send you the link. Make sure you're using the right version. Click version and choose the right one for you. Maybe I'm using one 22 five. So I'll click that. But although I think it's the same, nothing's changed dramatically. And let's see what function we're going to use. So one moment. Yeah. It's read all. So this one. So this is part I owe you to. But if you read is deprecated. So they want you to use instead I owe dot read all. So we click there. And here you can read about the function. So basically funk keyword. The name of the function, the parameter list. And then this is the return value. And the return value for this function is two things, which might be odd to you. Usually functions return only one thing in general, but this one returns two. And in go language, there's no exception like throw exception or raise error mechanism. They all they do it via basically return values. That's why you see all usually the second argument return value is an error. And here basically you see the brackets. That's actually called a slice. You might be used to think of it as an array. So when we come to go, we have to make sure we say slice instead of array because in go you have arrays, but arrays have fixed size. So if there's no number between the square brackets is a slice, which can have an arbitrary length. But if there's a number between the square brackets, that's an array. Okay. So basically it's a slice of bytes. And yeah, there's an example here. You can read as basically I owe read all and it can pass whatever there. And this is actually reading the body. Sorry. It's not for read the file to read the file is read file. I messed up there. That that's this one I was going to use to read the body when we create a book. Right. We read the body of the request, but actually I want to use read file. Where's that one? Let's go back to I owe you to read file this one. This one is for read file. And it's actually just if this is actually the source code. Now I'll give you the link. And basically it's calling OS dot read file, which is another package. So basically it's deprecated. If you read here deprecated as of go 116 simply call I owe dot read all. So we got to use that that one. Sorry. Not that one. This one. Keep messing up. Simple call OS dot read file. So not I owe but always. So let's go to. Oops. OS read file. So basically this is the one read file and the path and it gives two things. A slice of bite in the year. Okay, let's go. Make sure I'll share the right screen. So read file and this is instead of I owe. I think this is also telling me it's deprecated. It's OS. And let's do db.json. And by the way, go. We cannot use single quotes. It's not allowed. You got to use double quotes. All this. Okay. So it's two things, right? By the way, you have to import it at the top. If I click controls s, it will import for me because I have the extension basically import OS. Okay, so the first thing is the slice of bytes. Let's call it file content, comma, the second one is the error rate. Let's call the fine content error. Now to create a variable and go, there's different ways. Okay. The first way you could do like this var name of variable and then the type. And the second way is you can put name of variable. Column equals the whatever. The details is you cannot use colon equals outside of a function, the package level. I think you cannot do that. All right. So let's just do the colon equals here like this. And if you notice it's complaining, that's because when you don't use a variable and go, it's a compilation error. So if I try to compile again, it gives an error saying I'm using this. Okay. And oops. Let's see. Why is it complaining about something else here? Let me see. I wrote it response read R star. Oh, I put response here. Request, right? Did you notice that? Sorry about that. That's why I was complaining. Should be request here, not response. Maybe it's because I said that because I program a lot in JavaScript, Node.js, and it's always rec, res, request, response. That's why I confused. Sorry about that. So request there, make sure it's response writer and request. Now there's no error. But if I try to compile, I'm going to complain, hey, you declare but didn't use the variables. All right. So usually when you get errors in go, you got to handle it like this, like you're going to check if this variable is not nil, which means there's an error. And we have to handle that somehow. And you might find in go, that's very repetitive. There's no like try and catch block. So you got to do literally if the file content error is not equal nil, meaning the error is something, there's an error. Then we got to do something. Maybe you can return 500 internal server error. To do that, you can say w, which is the response writer type. And you can call write header, which is basically to write the status code, send a reply back with, for example, 500. Right. You can pass 500 here, or you can use a constant from hp.status internal server error, which is the same thing. If you, I think if you go here, you see untyped int 500. It's just a constant that has the value. And then you can return. So you don't continue the function. And the user will get 500 if it fails to read the file. Maybe the file doesn't exist. Right. And also you should log a report to your error monitoring system. In this case, I'll just log dot print the error content error. I'll simulate an error just so you can see out a curiosity. And for that, let me make these file content ignored for the front of compiler. If you want any unused variables to be ignored by the compiler, you can type underscore. Let me read what you said. Golang forces you, quote, don't make any mistakes. Check error. Is error new or not? Okay. Yeah. All right. So let's test this out. Let me say the file is DB two, which doesn't exist to simulate the error. And then we're going to basically this log comes from the log package. I make sure to import at the top. And let's try to do get slash books. I should see the first the console log, right, which goes to standard error, because I'm using that log package, s t d e r r standard error. Postman. Get books. I get 500. You see that 500. And then going back to this, I see this, this is the log dot print Ellen, which is basically prints the to the standard error says can I find a file. So that's good. Okay, let's assume a file is okay, we get the file read it into a string, right? This is file content. Now we got to turn that into like an object, right. And for that, we have to use the Jason package dot. Okay, and then we got to figure out, okay, usually we have to do what's called, you know, a Marshall, or, you know, serialized serialized, they call it Marshall on Marshall. Okay, so on Marshall. Okay, where's the data, right? The data is file content, right? And then let's see. Actually, do we need to do this? We only need to do this when we're saving, right? Because we just want to send the file content to the user, right? As Jason, it's already just a string, right? So we don't need to do that, actually. So actually, we don't have to do a Marshall that only for later when we register a book, because we read the file already and it's a slice of byte. And we can just send that as a response, right? So let's do that. So I'm going to W dot write the file content. And I think that should do it. Yeah. By the way, W is a response writer. There's the write function that takes a slice of byte. And it also returns errors, so I didn't handle the error there, but you have to be aware of that. Anyway, let's go here. Check. Now that's working. But there's one detail. I don't think it's treating as Jason. The content type, if you look at the header, that's plain text. I want to make it to be application slash Jason. So we have to set the content type to that. How do we do that? Let's go. Go back here. Basically, before that, you got to do W header function call dot set. Then first argument is content type, which is the name of the header. Second argument is the value application slash Jason. And that way, we will, the client will interpret that as Jason. Let's try again. Watch the header change application, Jason, right? Here. And then the body, it kind of highlights now because it knows it's Jason. Somebody asked before, hey, I don't like every time having to do that. Is there a way to always set the application Jason the header? Yes, there's several ways. If you're as Gorilla Mux, you can use the middleware. Or I can show you a different way, which is basically decorating the function, your handling functions to always make sure to call the set content type to application Jason. So bear with me. The syntax might be a little bit ugly to you, but we can do it. Let me see here. So what we're going to do is create a function that will decorate our handlers to always call W header set content type. And then after that, it calls the actual handler function for us. Okay, so let's go here. This is what we call a funk handle for. And this has to have the same signature as a thing, HP response writer and then the artist request, not response. R pointer to HP request like this. And then let's see. We're going to return another function from this, actually. And then we're going to return basically the function, it's kind of weird. Let's see. And this should be a function, actually, not the arguments, because I want to take a function and decorate it. Basically, I want to go here where I call, let's isolate this to a separate function. Before I do that, let me put this on hold. I want to just create a function to get books, which will basically be this one here. And I'll just cut that and put get books there. And I'll paste it here. Okay. And I just isolated it to make it easier. So we isolate the function and package level, we call it like this. Somebody asked, can we separate codes? I mean, I don't want to write code in this function because it's coming at 100 light. Yeah, yeah, yeah. Yeah, definitely. So the next step, like I said, you can put this in a separate file, right? Because it's just too messy, but I'm just doing it for the sake of time and education. We can do that later. Actually, let's try to do it now, actually. Let's do... I actually did the setup thing. Let's do file routes.go. Let's call it routes. So I've cut this, put in routes. And then what I'm going to do is I'm going to say, you see, it doesn't know what it is, but if you put package main, it will know what it is. And it should import everything here. And then from there, it now understands what it is. Okay, let me put this handle for... So we're going to take that function argument and return another function, which is the same. Bear with me, it's kind of tedious to write the same signature, rstarhp.request, writer. And yeah, basically, we're going to call like this, handle for, pass this function. So it's going to take the function, okay, great. And then it's going to return the function after calling the stuff. So basically return a function, w, again, I know it's ugly, but it's one way of doing that. And then we're going to do that, w header, set, content type, application, JSON. And then you're going to call the function that was passed in, which is kind of got to give a name, handler, before the font, because go is a weird language you got to put the type after. And then we're going to do handler with wr. Does that make sense? Why did you say r? Alas, r? Oh, yeah, thank you. Sorry about that. And basically like this, handle for, handle for no r here. And then when it, the first time it goes, it calls this, okay, returns the function right away. And then the next time when this is hit, it's going to call this function, which always sets the header. That way you don't have to go into routes, let me put it, split down. You don't have to go here and always do this part. Okay, somebody asked about this before, but this is one way. Of course, there's other ways, okay? This is not the only way. Okay, now let me show you one way. Somebody asked before, how can I always run stuff like node mon? Like if I change my code, I don't want to do manually. I want to have hot reload or something like that. There's actually something called compile demon, but you actually can use node mon as well. Doesn't matter. Compile a demon. There are other packages. I'll show you how to do compile a demon. So basically you're going to install the dependency, go install, and you have to pass the path where this package is. And it's github.com and github-nemo, that's the name of the person, slash compile-demon. I got it right, right? github.com slash github-nemo slash compile-demon. And then add for the version latest. Enter. Let me copy that for you in case that's too hard to type. And let's go to gomod. Oh, what happened? Did I do it wrong? Why didn't they do it? Let me remove the latest. No required module provide package. To add it, go get. Okay. Let me try go get. Okay, go get worked instead of install. These things are always changing, so I get confused. But I guess go install is when you don't have a module. If I didn't have go.mod, I'd have to use that. That's my guess. So we got to go get instead. Okay, so you can see it changed my gomod and added all this stuff. Alright, so how do we use that? compile-demon-option-build. This is based on the build command, so like go build main.go, set up.go, routes.go. And then we got to pass what's the command you want to execute. And it will be the name of the executable. I guess it's dot slash main. That's right. So first is going to build. Do you see the executable there main.exe? Now it's going to run. And where is it? Is it running? Why didn't it run? Am I command wrong? main.exe Why is it not going? Oh, you need a slash, not slash dash before command because it's an option. I put a dash command. There you go. And that's calling standard out of three. Correct. That way when I change the code, it will build again and rerun. Let's do some refactoring. You see this is too long. I don't like this. Can we make it better? Yes. There's actually a type called hp.hender.bunk that I'll show you in a moment. So basically if we go to the docs, let's go to the docs. And I want to find slash hp, not hp. This is the package. If I scroll down here, there's a type definition for handler funk. You see this? Read the definition. Type handle funk is a function with two remers, which is exactly what we're using there. So if you want, you can replace that whole thing with just handler funk as the type if you want to be lazy. So here, this whole thing you can call handler funk out of hp and this thing as well here. You can do that too. Let's save it, observe the terminal. It's running build again. And then it will run the server. And let's test out slash books to see if it's still working. We got JSON content type. Okay. Let me go here back to browser. Somebody asked about object relational mapper. Yes, there are several choices. I looked it up. One of them is like gorm. You can try that one. Obviously, I don't have time to go over it today, but this is one you can try and look at the docs. It's very pretty much what we do usually with the JSON stuff, define a type with the model with the structure and put the fields. And then you can open like that. And then if you want to create a record, you call it like this. Yeah, the syntax might be weird to you coming from another language because you got to pass the address, the ampersand is address. And they are creating a structure here, a literal structure, and you always have to put the name with the curly brace. Yeah, it's a little bit weird if you're coming from another language, but just like that. All right. And okay, let's try to do the get specific book because we didn't do that properly last time. So I'm going to leave this running here. I can hide it and close this. Save this routes. So the next one is I want to work on this handle sign single book, which I don't think it's great. I think I want to make a parameter here so that I can type one, two or three, whatever. So that one is easy. You put a curly brace and give it the name that you want that to appear later on if you want to reference it. I'll call it book ID chemo case. This handle single books defined here, like we did before, let's move that to routes.go. So it's a little bit better. Save that that I added my imports accordingly. And if I save this one, it's going to remove many parts accordingly. Okay. Just so you know, because I have the extension that does that for me. Okay. How do we access first of all, we don't need this JSON anymore, if we call the handle four. So we're going to do handle four here to always add the header. Save it. That's okay. Going back to routes. I don't need this. How do I access that book ID? Well, you got to call. Let's see here, you got to reference the request dot. And let's scroll down. This gives you some hints of what you need to get. I think the one is path value, this one. And you have to give the name of how you typed it between the curly braces. That's book ID. Now this thing returns what returns a string can read from the thing here. So you can put that as the book ID colon equals. Let me show you the different way of declaring a variable and defining it. When you declare a variable, you can do it like bar keyword name. And then the type like string. When you do this, this variable will have what's called a zero value. It's something different in goal. If you're in C language or C plus plus, when you declare a variable, it doesn't have a specific value. It could be garbage in memory. But in go, it always sets what's called a zero value. If you declare a variable without giving a value, it will be set to the zero value, which for string is an empty string. So actually, when you call to access book ID, it's going to be empty string. Okay, now if you want to define another value, you have to use the equals like that. Now it's complain, it's not used. Anyway, we're going to use that to go through the list of books. I've got to read the file. So let's copy the same thing we did before. Of course, you can put in a function and all that stuff. I'm not going to do it. Okay, so we're going to paste it here, read the files. And I got the file content. Now I got to search through that, but it's still text. That's why I got now we got to do that Marshall thing on Marshall. We got to convert to like an array of objects quotes. But for in go, it's a slice, right, the slice of structs. And I had this truck here to define the book type like this. So we can go outside and define it, or it can use like this way I did. But I think it's preferable to always be declare the structure. So basically would do type book, it's a struct. So we got the ID, we got the title and other types would be string and string. But the problem with go usually we have the first letter capitalized, right? So when you're going to take the text file and convert to this truck, you have to let the Jason package know, hey, when you get the ID, I want the name of the property to be lowercase, we have to pass what's called a tag to the field. So the tag in back ticks like this Jason colon ID. Jason colon, I have to have double quotes, by the way, closing the like this. I've seen the docs, I referenced this in the previous lecture. Basically the Jason decoder encoder will go and if there's a Jason tag like this, instead of saying ID, like in the output, it's going to do ID lowercase, which is what we typed here. Okay, so that's why we need that thing. You know, it's weird, but just the way it is. Okay, great. So let me put a variable here called books. And the type is going to be a slice of book type. Like this. Now we got a Jason dot. So there's Marshall and Marshall, right? So let's see which one. Jason dot think. If you do it, we did before when we did the operation of getting this into a text, we did Marshall. So we got on Marshall. Marshall parses Jason coded data and starts result and value pointed to by V. Yeah. So where's the data, right file content. Where do you want that to go? You have to pass the address and percent of books. Like that. And this returns to things. Let me see. No, it doesn't. Just the error, right? Okay. Books error. If books error, not nails, something happened. You can print the books error. And then right header. I don't know. Internal server error. Something messed up parsing return. Notice I did the same thing here. Maybe isolate that in a function. Anyway, once you got that, we got books. The books will be populated with that so we can go over it and look for the one that we're looking for based on book ID. So we got to do a loop here. Okay. Let's see. So for loop traditional war. I colon zero initialize a condition as long as last the length of the slice. So we want to do the length of something go is just the alien function past the slice. And then semi colon increments. And that's the traditional for loop. And then you can access books like this. And this one will be a book. Right. So we can have the book. So if the book. ID right is equal to book ID. We found it. So maybe want to find what a variable here. Somebody. Okay. Sorry. Miss the point. If you accept an int valent dynamic path. In a sense string. Are you there is there an error. So the path value is always string right. So if you put doesn't matter if it's a number visually. It's always going to be a string right. There's no parsing here. Yeah. You can convert it if you want. You could convert to an int. I think str com dot a to something to I or something. I forget the name, but it's str com package. But here we're using strings not integers for the value. If you I intentionally did string not integer. Otherwise we'd have to convert here because this it wouldn't compare string to integer. Right. If this was an integer. Oh, actually, this is wrong. Actually a point. Why is it saying int here? Why is it saying int? That's weird. I don't get it. Field ID int. I typed int. I type string right. Is it defined somewhere? Let me see ID int. I don't see it. What is the error here? Re-declare. Oh, somewhere is declared. Where is it? Oh, that's why it's confusing. I had already declared it. Let's let's make it a separate file. How about that? Let's call it a book.go. To avoid this mistake. I'm going to remove this. That's from the previous class. I forgot we have that. I'm going to take this and put it here so we don't confuse it. Save it. Save it. There you go. Make sure to add package main. Otherwise it doesn't know what it is. There you go. Now it's going to be okay. It's not int. It's in string. Okay. I think I got to reload that or something. Now it's string. See string. Anyway, yeah, if that is the case, let's put a variable here. far. Found. Book. Let's call it type book. By the way, I think the zero value might be of a struct might be nil. I don't know. Not mistaken. Let's see. Where is it empty struct? Not empty. Not empty interface. I think it's, you got to look with type or something and you got to print it out. Anyway, found book equals book. And then break, right? We don't need to keep going. And then here we have to check if found book, if there is no found book, right? I think it would be no. Let's see. Let's try to see if it's, if it's nil. Let's see if there's the zero value. Found book, mass match type untyped nil. What if I say here length? No, because it's not an array, right? Because I just try and check if it find it or not. So what's the zero value of this non Boolean condition? Let's, let's fix that later. But basically I wanted to do right header with a four or four, right? Status not found and return. Otherwise I found a book and let me comment all this out. And yeah, this part here would be encoded book to make a text again. So you can put found book here and that will make that text. We can write that, which is basically a slice of bite. And this log fatal, I don't want to log fatal, maybe print Ellen with the error and then return. W write header, HP, internal server error, something like that. Now this part, let me see. Let's look at the doc thing. Zero value of struct going. I think it's a struct, right? So I think what happens is when you have an empty struct, it will zero value all of its fields. So I think what's going to happen is the book ID will be zero value to empty string. The title is zero value to empty title. So I think if we check the ID here, it found book that ID is empty. Because it has to be an ID, right? Then it doesn't find it. Yeah, that's it. When you have a declare like this, it doesn't find anything. It's basically a struct with all the values, zero value for each field. Yeah, there's not nil. Yeah. Okay. Let's test it out and see. We didn't make any mistakes. Gats slash book slash one that were two. Oh, this is not working. What the heck? Let's see. Did I save all my files? I'm calling and no single book. Yeah, this is just complaining that I can do this, by the way. Why is it doing that? Did it not restart the app? Found book. We got the file content. Let's see again. Maybe it's just a... Let me check the... Any clues? What's going on? Let's check the... Oh, there you go. Is that error? If you use var, found, book, star, book. If I use that, it would mean the verb is a pointer to a book. Line 30 is an error. Undefined book. Oh. The problem is we added a new file. And I have to include that in my build command. Book.go. That's why. There you go, book.go. Try it out again. Okay, now it's working. 404 for four. One, two, three. Now it's working. Like I said, 404 for the others. Nice. Yeah, so that was it. I added a new file. And because of that, my build failed. And I had to include that in my build command. I mean, for new controls instead of empty string. Oh, okay. Okay. So you're saying I should go here and put a pointer. So instead of doing that here, you would call it new. Like that. Cannot use variable. Yeah, yeah. Okay. Address. Nice. Nice one suggestion. Thank you. So that way we don't check ID. Okay. Let's test it out. Still working 404. Nice. Yeah. Great. And yeah, I think that's it for this lesson.
No comments yet (loading...)
No comments yet (loading...)
Did you like the lesson? ๐Ÿ˜†๐Ÿ‘
Consider a donation to support our work: