Learn GO Fast: Full Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
how's it going y'all let's learn go today this is an awesome language to learn and so let's start this tutorial right away with six main points you should know about the go language first go is a statically typed language which means that you have to either declare variable types explicitly or they have to be inferred and these types cannot change afterwards at least not without type conversion which we'll get into in another video here's an example where I'm creating a variable named my variable in go I have to specify its type when I create it for example a string type or I have to set it to something right away so it can be inferred what it is in this case the value is a string so the variable is inferred to be of type string go is also strongly typed I.E the operation you can perform on the variable depends on its type for example in go I can't add an integer and a string together in a weekly type language like JavaScript on the other hand this is allowed go being a statically and strongly typed language has its advantages in that the compiler can do more thorough checking of your code for errors and force you to fix bugs before you can even execute your program you also tend to get better code completion and hinting When developing as it's more clear what each variable is in your code go also comes with a compiler which translates your code into machine code producing a binary file which can be ran as a standalone program this in contrast with some languages like python which use interpreters these translate the code line by line as the code is running adding some overhead which can make the execution much slower as compared to executing a pre-compiled binary file like go produces here's an example where I'm running a loop counting to 100 million in both go and python the execution time in Python took about 6 seconds in my machine and about 50 milliseconds in go that's a about 120 times faster this gives you an idea of interpreted versus compiled speeds a next feature of go is a compilation time itself is very fast being able to go from the code you wrote to a runnable binary without having to wait around a long time makes the testing process a lot nicer for developers like us we could write a piece of code compile it and test it pretty quickly go also has built-in concurrency you don't need special packages or workarounds to get parallelism working and go this is built into the language done with what's called go routines which we'll Explore More in a later lesson and finally Simplicity this is a general design philosophy of go its syntax is pretty concise you can do a lot of things while writing less lines of code and it also has things like garbage collection which automatically frees up unused memory so this is something you don't have to manage yourself so that's the main overview of what go is about so let's get into setting up our environment so we can start writing some code the easiest way to do this is to go to the Go website and go to slash doc slash install this will give you the installation instructions for Linux Mac and windows so you can choose the platform you're on follow the instructions there so I'm on Mac and the instructions tell me to just go download the installer and run it on my system to check if it's installed you can run Go version in a terminal or command line and you should get something like this all right so we're good to go let's now start our first go project but first make sure to hit the like button as well subscribe to the channel so you don't miss the videos in the series as they come out all right so the first thing we got to do is initialize our go project so let's start by making a folder called go tutorials and CD into that folder before we initialize the project there are two things we got to know about the structure of go code these are the concepts of modules and packages a package is just a folder that contains a collection of go files then a collection of these packages is known as a module and when we're initializing our project we're really initializing a new module so to start this new project slash module we type in go mod init and then the name of your module which can be whatever you want usually it's named to be its location in a GitHub repository that's what you'll see most commonly for this series I'll call it Goat tutorials though all the code for this and upcoming videos is going to be available on my GitHub this will create a go.mod file which is your modules package let's take a look at what's inside as far as an editor you can use whatever you want to Vim neovim vs code PowerPoint I'm going to use neovim so the go.mod file contains the name of our module as well as the Go version we're using in addition if we start installing external modules in our project which other people have created it will have those there as well as the version number we'll make sure to check back here when we install some we'll get into the conventions for structuring your folders for your go projects in a later video but for now let's make a CMD tutorial one folder in this folder let's create a main.go file and open it so remember every go file is part of a package and we identify the package that it belongs to at the top of the file by typing package and then the name of the package which has to be the same for all files within this folder here let's use the package name main this is actually a special package name that tells the compiler to look for the entry point function here I.E when creating an executable the compiler needs to know where the program should start from and it will look for a function named main within this main package which serves as the first thing that will get executed in your program you can see that my editor is indicating that there's an error here this is because I haven't yet created the main function which is required in the main package this only applies to the special main package for example if I had another folder with a package called Blue I wouldn't have to make a function called Blue anywhere to create a function in go you use the funk keyword followed by the name of your function this function does not take any parameters and we use the curly braces to signify where the code for the function goes you can see we created the main function and the error has gone away for this tutorial we're just going to print something to the terminal and for that we're going to use the fmt package which is built in to go to import the package we go just under the package name declaration and then type import and then the package name you can see right away that you'll get an error when you do this this happens because gold doesn't let you import packages and then not use them so let's make sure we use this Import in our main function and you'll see the error go away so to use the package you type fmt and then the name of the function you want to use inside the package in our case we want to use the print line function this will take the string that we pass in append a new line character and print that to the console you can see the error that we saw above has gone away alright so let's save and see how we can run this program we currently have the CMD folder and the go.mod file to compile our program you can use the command go build CMD tutorial one main.go this produces a binary file you can see here called main now we can run this file and get our hello world output pretty cool you could also use the go run command this will do the same thing we just did which is compile the code and run it but in a single command this is what I usually use okay awesome now we made our first go program RAM and we got the basics of go down now let's move on from the appetizer to the next part of this course here we're going to get familiar with constants variables and the various basic data types built into go all right here we are in our main package with our main function and fmt imported so we can print a few things as we go so to declare a variable we use a VAR keyword then the variable name then the type here I have a variable named int num with its type set to int into the basic built-in type and go signifying that the variable int num stores an integer you can see that the compiler is showing me an error of this because just like Imports you have to use every variable you declare this is part of go Simplicity design philosophy where the idea is that code should be easy to read and understand and part of that is that you don't have any pointless variables hanging out in your code so every time we declare a variable I'm going to use a print statement which will print its value to the console in addition to the intype we also have int 8 into 16 and 32 and n64. these are used to specify how much memory or bits you want to use to store your number 64-bit ins for example can store much larger numbers than 16 bit ins but take four times more memory the largest positive 16-bit integer which can be stored in an in 16 is 32 767. if I try to initialize a variable with a number larger than that I get a compiler error more specifically this is an overflow error note that the compiler won't throw any overflow errors at runtime so if I do this when I run the code I won't get any error show up in fact it'll continue to our print statement without any issues and print this so the program will still run but produce weird results moral of the stories think about what data types you're using as well as what values you might encounter and avoid late night debugging sessions where you're this guy note that int will default to 32 or 64 bits depending on your system architecture we also have access to unsigned ins which have all the same bit sizes as ins but only store positive integers and because of this you can store integers twice as large in the same amount of memory next we have float32 and Float 64. similar to int these are 32 and 64-bit floating numbers used to store decimal numbers 64-bit floats can store the largest and most precise decimal numbers but they take more memory in this case go doesn't have just a float type we have to specify either 32-bit or 64-bit if you want to know in detail how floating Point numbers are stored in your computer check out my video linked here but essentially most floating Point decimal numbers won't be stored precisely on your computer for example this 32-bit number when we print it to the console we don't get the exact number back we get actually how the number is stored in your computer this is a function of a 32-bit Precision that we chose if we store the same number in a 64-bit representation we get the correct number back it's good practice to think about what data types you want to use and not always going for float64 or int or n64. for example if I want to store 256 RGB and unsigned 8-bit integer is the best fit rather than using an INT which would be 64 bits in my system so by the way if you like these sort of fast no time wasted tutorials make sure to hit the like button and subscribe so you don't miss future videos as they come out now obviously you can add numbers together multiply them and all that stuff but two things to note about arithmetic operations one you can't perform operations with mixed types for example I can't add an INT and a float32 together or multiply an n32 with N64 for example if I need to do this operation I can cast one of the numbers to a common type like this which works by converting the integer variable into a flow 32. secondly integer division results in an integer and the result is rounded down so here 3 divided by 2 is 1. if you want to get the remainder here you can use the percent or modulo size next let's take a look at the string type so this might blow your mind but use the string type to store strings so you can initialize a string with a value using double quotes or back quotes So with double quotes it's just a single line I can't for example continue my string onto the next line I can insert a line break character using a slash n though so this is what would get printed to the console with back quotes you can format the string directly and this is exactly how it will print out we can also concatenate strings together by adding them now this is where strings get a little tricky we can get the length of a string using the built-in Len function but note that this isn't the number of characters in The String it's the number of bytes since go uses utf-8 encoding characters outside the vanilla ASCII character set are stored with more than a single byte so taking the length of this ASCII character will give you a one but taking the length of the gamma symbol for example gives you two so if you expect some fancy strings in your code and you want the length of a string in the number of characters you can import the built-in package Unicode utf-8 and call the Rune count and string function sounds like a weird name for a function which finds the length of a string but runes are actually another data type and go and represent characters if you use a single quote like this you got yourself a rune we'll do a deeper dive into Strings their utf-8 encodings and go and how they relate to runes as well as the byte type in a future video but they're not super useful right now and what we've covered is really enough for 95 percent of use cases so that's all I had to say about that for now lastly we're going to cover booleans these are super easy they can be either true or false You Now understand booleans and go up to now we've always initialized the variable where we've declared it but this isn't required we could create an into variable for example like this in these cases GO sets default values for our variables where the default value depends on its type for an integer the default value is zero so the default values for all ins unsigned ins floats and runes is zero for Strings it's the empty string and for booleans it's false we could also create a variable but omit the type if we set the value right away this way the type is inferred we could even go a step further and drop the VAR keyword as well and use the shorthand colon equals if you want to be real fancy you can initialize multiple variables at once in all the same ways we initialize the single variable now I tend to specify the type explicitly whenever I can and when it's not obvious so in this case initializing my VAR with a shorthand is fine because it's obvious that this is a string type variable but suppose I have some function Foo I imported from somewhere which returns some value now if I use shorthand here I really have no idea what myvar is unless I hover over it if I'm in an editor that can do that or I can go to the definition of the function to see what it's returning is this a string an INT or something else adding the type when it isn't obvious is good practice and will make your code easier to follow and you'll make the code in God's happy now constants are alternatives to variables everything we said before also applies to constants except you can't change its value once it's created also you can't just declare constants so for example I can't do this I have to also initialize it with a value explicitly constants are useful when you don't want code down the line to change the value you've set let's say I want to set the value of pi there really isn't any reason for anyone to change this and doing so will mess up any calculations we may be making so const is a good choice here foreign let's jump into functions and control structures in golang we've already gotten a preview of functions with our main function but let's start by defining another function right below it as we already know we use the funk keyword followed by the name of the function we use matching parentheses this is where our parameters will go in a second and curly braces to store the logic of the function note that I have to put the starting curly braces up here on the first line otherwise I get an error if I do something like this the closing curly braces you can put wherever you want let's write a print statement in here so we can see when this function is executed so I can call this print me function from our main method by just writing this if we run our main.go file we see that the main function executed the printme function which printed this to the console with functions we have the ability to pass in parameters and these go between the parentheses suppose instead of assuming what you want to print the print me function takes in a parameter of type string and we'll print that instead we can define an input value like this where the name of the parameter is print value and the type is of type string I can define a variable in our main function and pass it into the print me function and it will print this to the console now the parameter type is enforced so I can't pass in an integer to this function in place of a string now suppose I want to make a function which does integer division for me and returns a value let's call this function int Division and it takes two integers numerator and denominator so when you have a function which returns some value to the caller you have to specify the type that it's returning here like this so I just have to let go know that this function is going to be returning an integer back after it's done executing now I get an error here because currently the function is not returning anything so let's fix that let's do our integer Division and to return the results we use the return statement like this in our main function we can Define our result variable to take the value that the int division function returned and let's print that out to the console now you can also return multiple values at the same time so suppose I also want to return the remainder value of the division I can specify that this function is going to return multiple values by using parentheses and then multiple types like this again let's fix this error by honoring the return types we specified for this function like we saw we can get the remainder values using the percent sign and then we can separate our return values with a comma in our main function we can just insert our second variable like this which will hold the remainder value let's print the results of the screen except now let's start using the printf function instead of the print line function with the printf function we can format the string a bit easier using variables I can add a percent V to the string and then when it gets printed out it will replace this value with the variables I've set here so the first percent V will be our result and the second will be the remainder one thing you might be asking is what happens if somebody passes in a zero for the denominator let's try well we get an integer divide by zero error a design pattern in go is that if your function can encounter errors to have a return type of type error along with the values you're returning now error is another built-in type and go and if we initialize a variable of type error in our function like this the default value is nil so nil is a default value for a bunch of different types similar to how zero was a default value for INS we can check that the denominator is not Zero by using an if statement we use the keyword if followed by the condition and then the code that we want to execute it again this goes between the curly braces similar to functions so if the denominator is zero we want to return our error variable because this is an invalid input and we want to notify the caller that something went wrong to create an error type let's import the errors package where we can call the errors.new method this creates an error type that we can initialize with an error message we still have to return two integers so we can set those to zero since we're also returning the error variable we have to add the error type to our return definition at the top of the function so if the denominator is not zero the code will continue return the result remainder and the error which is nil now it's the job of the caller to check if an error was returned so we capture the error value with a new error variable and we check if it's not nil meaning an error was encountered in this case we'll just print the error message by calling the error method like this note that you can check if two things are not equal using the exclamation equal sign and check if they are equal using the double equal sign I also wanted to note that handling errors in this way is a general design pattern in go if you import functions from other packages a lot of the time they will return an error type in addition to other outputs so what we've done here is exactly how you would check if something went wrong and handle it accordingly let's flush this out a bit more by adding some more control structure here using else if an else if statement will only check its condition If the previous if or else if statement was false otherwise it gets skipped so let's print a different message depending on whether there's a remainder at the end we'll use an else statement which is the default if all the statements above are false in go the else if and else statements have to be on the same line as the last bracket now you can make multiple logical checks within the same conditional statement if you need to as well for example here's an if statement where we use the double Ampersand symbol to indicate that both these checks have to be true in order to execute the print statement here there's also the or operator with two vertical bars here only one check needs to be true in order to execute the print statement now go will only execute the checks it needs to and ignore the rest for example here since the first check evaluates to true there's no need to check the second since this is an or statement and it will continue directly into executing the print statement similarly for the and case since this evaluates the false it will not perform the other checks all right getting back to our code example we could have also written these if-else statements using a switch statement let's just paste that in and take a look we set up a switch block here using the switch keyword and within the curly braces we have the various cases exactly the same as with our if-el statements for example if the error is not nil we would print out the error message then the code would break out of the switch block I.E we wouldn't check the other cases if you're coming from another programming language with switch statements note that the break is implied so we don't need to write them out explicitly for each case and go so from a logic perspective this is equivalent to the if else statements we wrote before now go also has conditional switch statements which are slightly different suppose we want to print out additional values to the console depending on the value of the remainder if we write a switch statement but this time we insert the remainder variable between the switch keyword and the opening curly brace we have a conditional switch statement now we can check if the remainder is equal to zero like this if it's one or two like this and anything else will evaluate to the default statement foreign part 4 let's check out array slices Maps as well as how loops work and go so first let's start with arrays so an array is a fixed length collection of data all of the same type which is indexable and stored in contiguous memory locations let's explore these Four Points by declaring an n32 array and going through the syntax the first thing to note is a set of square brackets with a number three this indicates that the array holds exactly three elements note that the length of this array cannot change after it's initialized directly following is a type this specifies that all the elements in the array are of type n32 when declaring an array the default values are set to be the default values of the element types which as we saw previously for n32 was zero so we get an array here of three zeros I can also Index this array I.E axis the ith element in the array like this or access element 1 and 2 like this go with zero index meaning that the first element is at index 0 and the second is at 1 and so on I can also change the value of any element by using indexing as well since an n32 is 4 bytes of memory and our array stores three elements go allocates 12 bytes of continuous memory when we initialize the array we can print out the memory location of each element using the Ampersand symbol we'll do deeper dive into memory and pointers in a later video but for now I just wanted to note that these are stored in contiguous memory locations I.E the first M32 is stored in the first four bytes the second and the next four and so on the benefit of this is that the compiler doesn't need to store memory locations for each element it just needs to know where the first byte is stored and can just increment by four to get the location of the next we could have also immediately initialized the array using this syntax or using the colon equals shorthand like this we could have even omitted the three here and have this number be inferred by the compiler using the dot dot syntax so this is still an array of fixed size 3 because we set three elements here now related to a razor slices according to the go documentation slices are just wrappers around arrays so under the hood slices are just a raise with some additional functionality let's take a look at an example by omitting the length value we now have a slice with slices unlike arrays I can add values to the slice using the built-in append function this function takes the slices the first element and the value you want to append to the end of the slice is a second it then returns a slice with the new element in a pendant so what's actually happening here with respect to the underlying array well initially an array is allocated that can hold exactly three values when we go to append another number a check is done to see if the underlying array has enough room for four values in this case it does not so a new array is made with enough capacity and the values are copied there so in this case we got a totally new array back meaning one stored in a different memory location we can print out the length of the underlying array before and after using the cap built-in function this is called the capacity of the slice so initially the length of our slice in the capacity is the same but after appending a value the length of the slice is now 4 but the capacity is 6. so the underlying array went from this to this this is the length of our slice and this is a capacity note that I can't actually access the values here in the slice as I'll get an index out of range error now you can also append multiple values to the slice by using the spread operator like this another way to create a slice is using the make function like this we can specify the length of the slice as well as optionally specify the capacity of the slice otherwise by default the capacity will just be the length of the slice you might want to specify the capacity if you have a rough idea of how many values you're ultimately going to need to slice to hold this avoids your program having to reallocate the underlying array when it needs to store more values which can have a pretty large impact on performance we'll do a speed test at the end of this video to see exactly how much reallocation of arrays can actually slow things down alright so let's move on to Maps this is another very useful data structure implemented in most programming languages including go so a map is a set of key value pairs where you can look up a value by its key we can create a map using the make function again with this syntax this means that the keys are of type string and the values are of type unsigned int 8. alternatively you can initialize a map with values immediately like this now I have a mapping here of people and their age and I can get the age of Adam For example like this note that if I try to get the value of a map using a key that does not exist I'll get back the default value of that type I.E in our case our values of type unsigned into 8 which has a default value of 0. so here you have to be careful and go because the map will always return something even if the key doesn't exist luckily maps and go also return an optional second value which is a Boolean this is returned as true if the value is in the map and false otherwise this way you can handle the two cases by checking the value of the returned Boolean variable to delete a value from a map go has a built-in delete function where the first argument is a map and the second is a key this will delete by reference so no return value is given now we can iterate through Maps as well for that let's talk about loops and go if you want to iterate over something be it a map array or slice we can use a range keyword within our for Loop like this so the name variables initialized within the four statement itself using the colon equals syntax note that when iterating over a map no order is preserved so when I run this Loop multiple times I'll get a different ordering of the keys if we want to also return the values we can do that like this similarly we can iterate over arrays and slices like this where I is the index and V is the value in the array or slice so go does not have while Loops per se but this Loop type is achieved with the four keyword again for example this is your while loop and go it will continue until I is greater than or equal to 10. we can also totally omit the condition as well and instead use a break keyword here which will end the loop again when I is greater than or equal to 10. finally the same thing could also be achieved with this syntax there are three distinct Parts here separated by semicolons called the initialization the condition and the post so I is initialized here with a zero value and this is where the loop starts and it will continue as long as this statement is true I.E as long as I is less than 10. the post is what gets executed every time the loop is completed in this case I plus plus means increment I plus 1. note that there are other shorthands like this available like I minus minus which decrements I by one as well as a bunch of others for all four basic math operations which you could use as well alright as promised let's do a speed test showing the benefits of setting the capacity of your slice ahead of time if possible so here we're going to append 1 million elements to our test slices the first test slice is going to be empty with zero capacity and the second is also going to be empty both the pre-allocated capacity of 1 million so I have this time Loop function which takes in a slice and the number of iterations it uses goes built-in time package to measure how long it takes to finish the loop and then returns a result we can see that this takes about three times longer when initializing without a preset capacity so you get it I get it foreign let's take a deeper dive into Strings and see how they relate to runes and bytes and golang we'll use the word resume here as our demonstration string for this video mostly because it has these non-ascii characters which will help us understand strings and go a bit deeper so first we can index a string just like arrays using the same notation when we print this out to the console though we get something curious we get a number let's print out the type of the index value using the printf statement and percent T so even weirder this is an unsigned into eight if we iterate over the string with the range keyword where I is the index and V is the value we get an output like this the First Column is the index where we get 0 1 skips two three and so on and the second column is a bunch of numbers again to understand what's happening here let's make sure we understand utf-8 encoding which is how go represents strings on your computer so remember we have to represent strings as binary numbers in computers one such early way of doing this was using the ASCII encoding this uses seven bits to encode 128 characters so for example sample the letter a is a 97th ASCII character which is this in binary but what do we do if we want to represent an extended set of characters like emojis or Chinese characters for example one obvious way of solving this would be to use more bits for example we could extend our character representation to use 32 bits or four bytes this is exactly what utf-32 encoding does but this can waste a lot of memory for many characters for example here's the letter a in utf-32 it'd be nice if we didn't need to have all these zeros here as well to represent this utf-8 on the other hand seeks to solve this issue by allowing variable length and coding I.E using the appropriate number of bytes for the character utf-8 uses a predefined encoding pattern which encodes information about how many bytes this particular character uses for example you can tell that a character uses one byte if it starts with a zero two bytes if it starts with a one one zero and so on so the regular ASCII characters use one byte and anything beyond that uses two or more the acute e character has a Unicode Point number of 233 Unicode character is numbered between 128 and 2047 use two bytes and hence this pattern 233 in binary is this we need to pad this number with leading zeros in order for it to fit into the utf-8 encoded representation like this so this is now our utf-8 encoded value for our acute e character let's jump back into the code as we can now better understand what's happening the string variable we declared here has an underlying array of bytes which represents the utf-8 encoding of the full string which looks like this so when we were indexing our string here what happened is that we're actually indexing the underlying byte array this is why we got 114 which is the value of this byte note that if we index a string at the acute e index we get this number which is the first half of the utf-8 encoding of this character so we wouldn't get back the proper 233 we would expect for this character however when iterating over the string using the range keyword we got the proper 233 back so the range keyword is doing some extra work for us here it knows that our second character is a two byte character and decodes it correctly to 233 this is also why we see index 2 being skipped here so again a takeaway from this is that when you're dealing with strings and go you're dealing with a value whose underlying representation is an array of bytes this is also why taking the length of a string is its length in the number of bytes and not the number of characters an easier way to deal with iterating and indexing strings is to cast them to an array of runes rather than dealing with the underlying byte array of a string for example here's what we get when we do that with our string so runes are just Unicode Point numbers which represent the character now we can Index this at the acute e index and get back 233 as we would expect note that runes are just an alias for n32 also now when we're iterating we get back the continuous index note we can declare a rune type using a single quote like this as well finally let's cover string building as we saw before we can concatenate strings using the plus symbol like this note though the strings are immutable in go meaning I cannot modify them once created so it follows that when we're concatenating a string and assigning it to a variable like this we're actually creating a completely new string every time which is pretty inefficient instead we can import goes built-in strings package and create a string Builder instead of using the plus operator we call the right string method and finally called the string method at the end What's Happening Here is an array is allocated internally and values are appended when calling the right string method only at the end is the new string created from the appended values much faster foreign let's take a look at how structs and interfaces are used in go first you can think of structs as a way of defining your own type so to create a struct we can use the type keyword because again we're defining a type here the name of our type followed by the struct keyword and curly braces structs can hold mixed types in the form of fields which we can Define by name so here I defined a gas engine type it holds a miles per gallon field of type unsigned into eight and a gallons field of the same type which represents how many gallons of fuel are left if we go to our main function we can now create a new variable like this which is a gas engine type because we haven't defined the miles per gallon or gallons field yet this is a zero valued struct meaning default values are set for the fields so these are zero for our two Fields since these are unsigned into eight types one way to set these values is to use the struck literal syntax like this we can also omit the field names and the fields are assigned values in order we can also set the values by name directly like this as well the fields can be pretty much anything you want even another struct for example here is our owner struct which has the name of the person who owns this car engine you can create an owner info field here of type owner so the owner information can be accessed using ownerinfo.name we also have another option which is to directly put in the owner type like this now instead of having an owner info field with a name subfield we're adding the subfields directly in other words we can just use the dot name syntax so the gas engine has these three Fields now actually we can do this with any type so for example like this with an n-type now we have a field called int which is also of type int alright let's just revert back to our miles per gallon in gallons Fields again note that you can also declare Anonymous structs where you aren't defining a name type like we did here with an anonymous struct we have to Define and initialize it in the same location so we could create a similar struct type to our gas engine type like this the main difference is that this is not reusable if I want to create another struct like this again I have to rewrite the definition now structs also have a concept of methods which we can use as well these are functions which are directly tied to the struct and have access to the struct instance itself let's say we want to make a method which calculates the miles left on a gas tank let's paste that in and go through the method except for this part here a method is just like a function in this case we return an unsigned into eight now what we're doing in this first part is we're assigning this function to the gas engine type this function or method now also has access to the fields and even other methods I've assigned to this gas engine type so when we go down to our main function we can call this method like this if you come from other programming languages this is very similar to classes where we're instantiating a class here and calling one of its methods now I can pass in this new type to functions like this so this function takes in a gas engine type parameter and a miles parameter and checks if you can drive that distance I suppose I have different engine types instead of just a gas engine I also have an electric engine type electric engine has different fields for example instead of miles per gallon now it has miles per kilowatt hour and instead of gallons it has kilowatt hours which specifies how much charge is left in the battery it also has a similar miles left method now currently our can make it function only takes in gas engines but what if we wanted to make this more General and allow it to take in any engine type well this is where interfaces come in let's define an interface and see how this can help us here we use similar syntax to defining a struct except we use the interface keyword and our can make it function we see the only method we really need here is the miles left method which takes no parameters and returns an unsigned into eight this is called the method signature we can specify this signature within the interface like this and our can make it method instead of having a very specific requirement that our e-parameter must be a gas engine we can replace this with our interface this function can now take anything for this parameter with the only requirement being that the object has a miles left method with the signature we specified in our interface this is now much more General and we can apply this function to a wider range of engine types so we can see why this is a really useful tool in go foreign so let's answer the question what are Pointers and how are they used in golang to start pointers are actually a special type these are variables which store memory locations rather than values like integers or floats for example to create a pointer we use the star syntax so this line of code states that the variable P will hold the memory address of an n32 value let's also create a regular n32 variable and call this I here on the right is how these variables might look like in memory the variable p is going to store a pointer or memory address which itself takes up 32 or 64 bits depending on your OS in my case this is 64 bits or 8 bytes now this pointer is initially nil because we haven't initialized it yet in other words this pointer doesn't yet point to anything or yet another way to say this this pointer does not have an address assigned to it in which to store an n32 value yet to give this pointer an address we can use the built-in new function what this does is it gives us back a free memory location which is 32 bits wide which P can use to store an n32 value now we can see p stores a memory location I.E it points to this memory location down here so the beginning of the video I said pointers are special types of variables this is true but pointers still share a lot in common with regular variables we've seen before they still have a memory address themselves and they store a value at that address in the case of a pointer this is another memory address if we want to get the value stored at this memory location we can use the star symbol like this this is called dereferencing the pointer we can see that we get a zero back because this is a default value for n32 in fact when you initialize a pointer with a memory location it zeros out that memory location in other words it sets the value at that memory location to the zero value of that type for example 0 for n32 the empty string for Strings and false for booleans to change the values stored at the memory location of a pointer we use the star notation again and assign a value so this line says set the value at the memory location P is pointing to to 10. note the star notation does double duty which may be a little confusing here we use it to tell the compiler that we want to initialize a pointer but on these two lines we use it to tell the compiler that we want to reference the value of the pointer so these are two separate roles the star syntax has that you should keep in mind another common source of headaches is trying to get or set the value of a nil pointer so if we don't call the new function we don't see any compiler errors but when we run this code we get a nil pointer exception in other words we get a runtime error What's Happening Here is we didn't assign a memory address to our pointer so we obviously can't get the value at a memory address that doesn't exist so make sure your pointer isn't nil before trying to assign values to it next we can also create a pointer from the address of another variable using the Ampersand symbol like this the Ampersand symbol here means that we want the memory address of the variable not its value so P now refers to the memory address of I in other words both p and I reference the same in 32 value in memory if I use the star notation again to change the value of P the value of I is now also changed this is different from when you're using a regular variable for example if you create a new variable K and set I to K so what your program will do here is copy the value of K into I's memory location the main exception of this copy behavior of non-pointer variables is when working with slices let's say I copy a slice in the regular way without using a pointer now let's try modifying the slice copy variable and printing out the values of our two slices again we can see that actually the values of our original slice have changed this is because under the hood slices contain pointers to an underlying array check out my award-winning video here which goes into details about how slices work but basically with slices we're just copying pointers when we do this so both variables refer to the same data just something to know so you don't pull your hair out when debugging all right so moving on to using pointers and functions these two go really nicely together similar to subscribing and hitting the like button let's see why by looking at an example so here's a function which takes in a float64 array of size 5 and squares all the values let's print the memory locations of Thing 1 in the main function and Thing Two in our Square function here we can see that their memory addresses are different meaning these are two different arrays therefore we can modify the values of thing two without affecting the values of Thing 1 in our main function but we're also doubling our memory usage of the variables passed in because we're creating a copy for use in our function so we're potentially using way more memory than we need well not anymore instead let's use pointers we can make our function taking a pointer to an array instead now we can see that the memory locations of these two arrays are the same pointers are really useful when passing in large parameters so you don't have to create copies of the data every time you call a function wasting time and memory instead you can just pass in a pointer to the data the only thing to be mindful of now is that since Thing 1 and Thing 2 refer to the same array changing the values of Thing 2 means the values of Thing 1 also change how's it going y'all in this video Let's jump straight in and learn about go routines let's start the insanity giddy up so go routines are a way to launch multiple functions and have them execute concurrently so first off concurrency is not the same as parallel execution concurrency means that I have multiple tasks in progress at the same time one way I can do this is by jumping back and forth from working on one task to another let's say task one involves the database call which takes three seconds to return data in concurrent programming while I'm waiting for the database to respond the CPU can move on to working on task 2 in the meantime then when I get a response maybe I move back to finish up task one then maybe move back again to finish up task two another way to achieve concurrencies through parallel execution instead of having one CPU core working on these two tasks I can have two then their execution can happen simultaneously so the execution here is still concurrent because we have multiple tasks in progress at the same time but also these tasks are running in parallel so we can see that a program may be running concurrently but may not necessarily be executing tasks in parallel in practice though and go we usually do achieve some level of parallel execution using go routines as long as you have a multi-core CPU which you probably do okay let's check out how to use go routines in our code here we have a function which simulates a call to a database retrieving an ID from the list here this takes a random amount of time up to two seconds per call and we call this mock database five times now we can call this database sequentially one by one which will take an average of Five Seconds to complete a better way to do this is to let these database calls run concurrently to do this we use the go keyword in front of the function you want to run concurrently now our program won't wait for this function to complete rather it'll keep moving on to the next step in the loop if we run this code like this though we see that nothing happens so our programs spawn these tasks in the background didn't wait for them to finish and then exited the program before they were complete so we need a way for our program to wait until all these tasks have completed and then continue on to the rest of the code this is where weight groups come in which can be imported through the sync package we can then create a weight group like this weight groups are pretty simple they're pretty much just counters whenever we spawn a new go routine we make sure to add one to the counter like this and inside the function we then call the done method at the end like this this decrements the counter finally at the end we call the weight method this method is going to wait for the counter to go back down to zero meaning that all the tasks have completed and then the rest of the code will execute now what if instead of just printing out the results of the console we want it to return them to the main function well first off let's make a slice where we can store all the results from the database in the DB call function let's append the value we get back from our fake database I'm also going to remove the randomness from the delay and set it to two seconds this way we can see what happens when our slice is modified at the same time by multiple go routines now when you have multiple threads modifying the same memory location at the same time you can get some unexpected results for example two processes writing to the same memory location at the same time could lead to corrupt memory as well as a whole host of other issues so we can't run this code scratch that we can but we really shouldn't run this code like this instead we can use what is called a mutex to control the writing to our slice in a way that makes it safe in a concurrent program like ours we can create a mutex again from the sync package like this this is short for Mutual exclusion the two main methods are the lock and the unlock method and we'll place them around the part of our code which accesses the result slice what happens here is that when a go routine reaches this lock method a check is performed to see if a lock has already been set by another go routine if it has it will wait here until the lock is released and then set the lock itself once it's done tinkering with our results array the lock is released again with the unlock method and now other go routines can obtain a lock as needed one thing other tutorials don't seem to mention is that it really matters where you put your lock and unlock statements if I were to put the lock statement here this code would work but it would totally destroy the concurrency of our database calls if you don't see why that is pause the video and try to figure it out yourself using what we just learned about the lock and unlock methods one drawback of this sort of mutex is that it completely locks out other go routines to accessing a result slice now you might want this but you might not go provides another type of mutex called a read write mutex this has all the same functionality of the mutex we saw before and the lock and unlock method work exactly the same but we now also have a read lock and read unlock method as well for readability and improving our code let's make a few separate functions here to read and write from the result slice the part here where we read the result we can add a read lock and a read unlock so when a go routine reaches this line it checks if there's a full lock on the mutex by full lock I mean the lock type we saw before where we called the lock method if this full lock exists it'll wait until it's released before continuing this way we're not reading while the results are potentially being written to if no full lock exists the go routine will acquire a read lock and then proceed with the rest of the code note that many go routines May hold readlocks at the same time these readlocks will only block code execution up here when a go routine hits this line in order to proceed all locks must be cleared that is full locks and read locks this prevents us from accessing the slice while other go routines are writing to or reading from the slice just like before so in summary this pattern allows multiple go routines to read from our slice at the same time only blocking when rights may be potentially be happening this is in contrast to what we saw before with the lock and unlock method with a regular mutex in that case even reads of the data can only happen one at a time all right let's move on to look at performance improvements in a little more detail for the sake of illustration let's just simplify our code here so all our DB call function does is sleep for two seconds now I can call this method a thousand times if I wanted and my program will still finish in about two seconds the reason for this is obviously not because I have a thousand cores to process all of these go routines in parallel but because this function really isn't doing anything after two seconds and the CPU can move on to starting the next go routine if I have more computationally expensive tasks though the performance gain we get is going to be limited by the amount of cores we have for example here's a task which just counts to 100 million generally this takes about 40 milliseconds on my machine now if I call this a thousand times within this Loop it takes about 5.8 seconds to finish so in this case these go routines need to actually do some work and because I have eight cores in my machine I can run eight of these go routines at once the rest of the go routines need to wait until I have a CPU core available here I have plots of the execution time by the number of iterations set for the loop now with the DB call method we got pretty much constant time no matter how many go routines we spawn together you can expect similar results of the bulk of your function time as waiting for a database response on the other hand if your function is computationally expensive the amount of improvement you'll get with parallelization is the function of the amount of cores in your CPU in this video we're going to cover channels and how they work with go routines so at a high level you can think of channels as a way to enable go routines to pass around information the main features of a channel are number one they hold data for example an integer a slice or anything else really two they're thread safe I.E we avoid data races when we're reading and writing from memory and three we can listen when data is added or removed from a channel and we can block code execution until one of these events happens let's see how channels work in code so to make a channel we use the make function followed by the Chan keyword then the type of value we want the channel to hold so this channel can only hold a single int value channels also have a special syntax I.E we use an arrow like this to add the value 1 to this channel we can think of a channel as containing an underlying array in this case we have what's called an unbuffer Channel which only has enough room for one value now we can retrieve the value from a channel using similar syntax and we can set that to a variable so here the value 1 gets popped out out of the channel the channel is now empty and the variable I now holds a value one suppose we just ran the code like this we end up getting a deadlock error now understanding why we get this error is really instructive as to how channels work when we write to an unbuffered channel the code will block until something else reads from it so in effect we'll be waiting here forever unable to reach the line where we actually read from the channel luckily goes runtime is smart enough to notice this and we'll just throw a deadlock error rather than your code hanging here forever so clearly this isn't the way channels are meant to be used instead let's use them properly in conjunction with go routines let's create a process function which takes in a channel and writes a value to it next let's call this as a go routine up here and print the value that gets set by our go routine note that here I'm directly printing the value popped out from the channel rather than setting it to a variable first okay so let's step through this code so we start our go routine and the program moves to the print statement where it waits for a value to be set in the channel in our go routine we set the value and exit the function then our main function notices that a value has been set and the print function gets called and the value gets printed pretty simple now what if we add values from 0 to 4 to this channel in a loop like this so of course we have to read from the channel multiple times in our main function as well we could surround this with the same Loop here but now the process has to know how many times it needs to read from the channel instead for Loops can actually work with channels in a really nice way we can iterate over the channel Itself by using the range keyword now I here is the value of the channel so let's step through this code again we create the channel and we start the go routine in the main function we wait at the top of the for Loop for something to be added to the channel in the process function we set up the for Loop and add 0 to the channel we wait until the main function reads from the channel and then in a concurrent way both the values printed and the number one is added to the channel at about the same time this then continues until I is equal to 5. now if we run this code our old friend the deadlock error will happen again this is because after we print all of our values from 0 to 4 the main function will go back to weight at the top of the loop for another value but just like me on 10 under after five messages it'll get ghosted by the process function which won't send any more messages and we get the deadlock error what we have to do is before exiting a process we can close the channel like this this notifies any other process using this channel that we're done and our main function will break out of the loop and exit in fact let's use the first statement and go this just means do this stuff right before the function exits okay so let's expand on the topic of channels slightly and look at a buffer Channel these are very similar to what we saw except now we can store multiple values in the channel at the same time we can create a buffer Channel which can store five integers like this okay so how is this actually helpful well let's say we want to do some work first before printing out the value this takes one second when we run the code with the regular channel the process function stays active until the main function is done with the channel but there's really no need for the process function to hang around it can finish its work quickly and just exit and let the main function do its thing by using a buffer of 5 the process function can add up to five values in the channel without having to wait for for the main function to make room in the channel by popping out a value here running like this we see that the process function finishes almost immediately while the main function is still running reading the values in the channel alright now obviously these are all pretty dumb programs so let's make a more realistic program and see how channels are actually useful let's make a little program which mocks checking for sales on chicken fingers at Walmart Costco and Whole Foods if it finds a sale it'll send me a text message with this function notifying me of the sale on chicken fingers so this channel is going to hold the website we found the sale on let's also make a check chicken prices function which takes in the website we want to check and the channel we're going to make a for Loop which every second checks the website for the price of chicken and if it's below our threshold it will set the value of the channel to the website let's spawn three go routines and then send a message when a deal is found so there are three go routines running at the same time checking these three websites and the send message function is waiting here for Value to be added to the channel to send off the text so the first go routine to find a deal on chicken will trigger the text message in the program will exit all right so let's also cover the concept of Select statements using this example suppose I also want to track the price of tofu we have a similar check tofu prices function which we also run as a go routine as well as a separate tofu channel so when we find a bargain on tofu we write to this channel let's also pass this channel into the send message function now if we find a deal on tofu we don't really care that much so instead of sending a text we want to send ourselves an email so let's paste in a select statement which is going to help us here so this is like an if statement for channels what happens is if we receive a message from the chicken Channel we set the variable website to the value in the channel and we execute this statement otherwise if we receive a message from the tofu Channel we execute this statement so this select statement will listen for a result once it gets one it'll execute one of these statements and then exit all right so let's check out how generics work and go if you've worked in a statically typed language before you might have ran into this problem here I have a function which sums up the values of an integer slice but a bit down the line I also want a similar function for a float32 slice all right so now we can rename this function to sum in slice and then create a sumfloat32 slice function and for float64 the same thing and so on now these are all pretty much the same functions and so it'd be nice not to have to repeat the code like this now different typed programming languages solve this problem in different ways but for golang we use generics so we can collapse all this into a single sum slices function which handles all cases let's explain what this code does so we can think of generics here as a way of allowing a function to receive additional parameters Within These square brackets except instead of a value we're passing in a type and here this function will only accept an INT float32 or float64 type we can see that we pass in an n-type up here so everywhere we have the letter T you can replace it in your head with int this function takes in an input of a slice of ins and returns an INT the sum variable here is also of type int now we can call this same function for float32 for example and it's all good now we also have the any type which we can use in certain situations with generics as well we can't for example use it here to allow all variable types this is because not all types are compatible with the addition operator for example you can't add booleans together but here's an example where it does make sense to use the any type this function checks whether a slice is empty this works no matter what the slice is made up of note that actually in these cases we can even omit the square bracket type inputs here this is because the go compiler is smart enough to infer the types as if we pass in a slice of ins then T is inferred to be an INT but there are cases where we can't infer the type of our generic parameter for example here's a function where I'm loading some Json file and then unmarshalling it into one of these two struct types now here on marshalling by the way means loading the Json string into a struct for example if our Json looks like this then we end up with a struct that looks like this this is a case where we need to pass in the struct type because otherwise the function won't know what struck to unmarshall our Json string into for example we may be loading many different types of jsons using this function and the compiler needs to know what struck type to unmarshall the data into speaking of struct's generics can also be used with struct types not just functions for example here we have a gas engine and electric engine type we can create a car type which has a make model and also an engine field we can make this capable of being both a gas and an electric engine by using generics in a very similar way now we can instantiate two types of cars by setting the engine value to gas engine here an electric engine here to be honest I tend to use generics almost exclusively with functions but you can use them also with structs like this as well and that's about it for generics very simple and very useful which is a good combination all right let's put everything we learned in the previous tutorials together and make an API and go before we start I just want to note that I'm going to assume that you have some level of knowledge of how restful apis work it's not strictly necessary for this video but it's a nice to have alright so this is what our API is going to look like in the end here I'm using Postman for testing this API we're going to make an account slash coins endpoint which we can use to look up the amount of coins someone has in some imaginary account we got a parameter which is a username and the authorization token in the header which we're using to authorize the call so this tutorial is going to cover go code structures authentication middleware and installing and importing external packages let's create our project with go mod in it and the name of our module so in my case I'll use my GitHub repo for this project where you can check out the code for this tutorial so first let's start with the structure of our project many go projects adhere to a standard layout which I'll vaguely follow here the note you don't have to follow this structure for your future projects do what works for you not to worry I have a permit this just says I can do what I want you can read more about this by Googling golang project structure and you'll find a GitHub repo detailing what code should go in what folder okay so we'll have an API folder here we're going to have our specs things like parameters and response types for our endpoint this is also where you could put your yaml spec file I'm not going to cover creating a yaml file as it isn't strictly needed and it's not the topic of this video next we're going to have a CMD API folder which will contain our main.go file and we're going to have an internal folder this will contain most of the code for this API let's start in our API folder and let's create an api.go file here let's write out the parameters and the responses of our endpoint let's start with some imports which we're going to use in a bit we'll make a few structs which will represent our parameters for our endpoint and responses first we have our coin balance param struct which represents the parameters our API endpoint will take in this case we'll just require the username second we have the coin balance response struct this outlines the successful response from the server containing a status code and the account balance next we have an error struct representing the response returned when an error occurs so we got the Response Code as well as an error message okay so now let's define our main package in CMD API main.go by the way if you like this series or this video so far make sure to tap those like And subscribe buttons if you haven't already alright let's note a few things from our package Imports first in our API we'll use the chi package this is a pretty flexible and easy to use package for web development though there are a bunch of others you could use second we're going to be importing a package from our own module here in the internal slash handlers folder lastly let's use logariths to log errors for debugging when we import an external package like logress or Qi we can install it using go mod tidy now in our go.mod file we can see that we have these two packages listed out alright sweet so now let's go back to our main function to start let's set up our logger so that when we print something out we get the file and the line number to do this we call the set report caller function passing in true to turn this on now we create a new Qi mux variable using the new router function which returns a pointer to a muxtype which is really just a struct which we'll use to set up our API we're going to pass this into our Handler function which we'll Define in a second in our internal slash handlers folder remember we imported this package up here this will set up our router I add the endpoint definitions that we want let's add a print statement and then another cooler print statement and then let's start the server with our HTTP package this takes the base location of your server which in our case is just localhost on Port 8000 as well as a Handler which are muxtype satisfies and then of course let's log any errors we might have when starting the server okay so now let's actually create this Handler function which will set up our router this is in our internal slash handlers package under the api.go file so let's define our Handler function which takes in that mux type we just created note that our Handler function starts with a capital H so this just tells the compiler that the function can be imported in other packages if I started with a lowercase and this would have been a private function which could only be used in this Handler's package the main package would not be able to import it as we did now let's cover the concept of middleware so middleware is essentially a function that gets called before the primary function which handles the endpoint let's look at a few examples of how we're going to use middleware you can add middleware to a route by using the r dot use function so first we're going to add a piece of global middleware this is going to be the strip slashes function this is a pre-built function we are grabbing from cheese middleware package Global middleware means that this middleware is applied all the time so in other words to all endpoints we make this strip slashes function will make sure that the trailing slashes will always be ignored otherwise we'd get a 404 error if we included the slash like this all right so time to set up our route to do this we call r dot route which takes in the path which is slash account for us as well as an anonymous function which takes into Qi router as a parameter we can now use this router to Define our get method but first let's add another piece of middleware to this route where we can check if the user is authorized to access this data first we'll create this authorization function in our middleware package later now every request which wants to access an endpoint starting with Slash account has to pass through this authorization function first if the authorization fails the function will return an error as a response and the rest of the code won't get executed so this function essentially acts like a nightclub bouncer if you don't have the proper ID you can't come in here we'll create a get method inside this route with the slash coins path which will be handled by the get coin balance function we'll Define this function later as well so we just created an endpoint that's now at slash account slash coins alright so now we got to Define our authorization and our get coin balance functions let's start with the authorization function we'll put this in our internal slash middleware slash authorization go file first let's create a custom unauthorized error which we're going to use in this function so because we're using our authorization function as middleware this needs to follow a certain signature I.E it needs to take in and return an HTTP Handler interface now we can make this function return in HTTP Handler like this we're using the Handler funk in the HTTP package so this takes in a function which itself takes in a response writer and a pointer to the request so the response writer is what you use to construct a response to the caller for example set the response body the headers and the status code the request is what contains all the information about the incoming request for example headers payload and other information about the HTTP requests which came in now in here is where we're going to Define all our Logic for authorizing the HTTP request now we can grab the username parameter from the request pointer by calling r.url.query.get and then the name of the parameter which for us is username we can also grab the auth token from the header like this now if either of these are empty we can return an error to do this let's go back to our API api.go file here let's create a function which we can use to write an error response to the HTTP response writer in other words this function is going to return an error response to the person who called the endpoint we want to make a function here because we're going to reuse this in multiple places in our code when we want to return an error let's call this the right error function so it takes in the writer the message we want to return and the status code we're going to use our error struct and write a few things to our response writer so here we set the content type IE we want to return to Json the error code like this and finally we write the error struct out which is going to be what the user gets back in the response now we're not going to call this directly in our functions when we want to return an error because we want to have a couple of types of error responses so let's make a few wrappers to this function so this request error Handler is going to take in the response writer and the error we're going to use this when we want to return a specific error in our response this might guide the caller to fix a request so for example if the username wasn't passed in we might want to return a message which says username required or something like that on the other hand we'll use the internal error Handler when we want to return a generic error message for example if the error is a result of a bug in our code we don't need to return a detailed message to the user because it's not all that helpful for them okay so if we go back to our authorization function we can log out the error to the console and then call our new request error Handler passing in an unauthorized error and then exiting the function with return now if we have both of these things we can proceed to getting data from our database and checking the username and authorization token is correct we're going to instantiate a pointer to our database like this using an interface type and call the new database method don't worry we're going to Define all these functions and types later if we get an error back we'll return an internal error in this case now let's actually query the database using the get user login details method then finally if we didn't find a client with a username or the token doesn't match what we got back from our database again we're going to return a request error at the end of this function we need to call the next.serv HTTP method what this does is it calls the next middleware in line or the Handler function for the endpoint if there's no middleware left so in our case this will call our get coin balance function at the end which we'll make in a bit and there's our authorization function let's go ahead and create our database interface now we're going to create this in internal slash tool database.go so first let's Define the types of data our database should return these will look like this so login details and coin details the former contains the auth token for validating the request and the latter has the coin balance now the database interface is going to define a few methods that are required for our API we're using an interface here because we can swap out our databases really easily as long as we Define a get user login details method a get user coins method and a setup database method with these signatures alright now that we've defined our interface and the return types let's create a function called new database which returns this interface inside we're going to create a database variable and set it to a mock DB struct this is a struct we're going to create which is going to implement our interface then we call the setup database method do the standard error checks and return the pointer now let's create this mock database an internal tool mockdb.go we'll create a mock DB type and let's create some data here we have some login details data as well as some coin details data now these just represent some fake data and in the real world app this would probably just be stored in some form of database remember in order for this mock DB struct to conform to our database interface we need to create a get user login details get coin details and set up database methods so our two Gap methods we'll just look up the data and the maps we defined above and the setup database function for our mock database just does nothing okay so if we go back to our API Handler we still need to Define our get coin balance function now in order to use this function in this get method we need to Define it such that it takes in a response writer and a pointer to the request as parameters so let's do that as our final step here in the same Handler package let's create a getcoinbalance.go file here we're assuming the call is already ran through the authorization middleware so we just need to grab the username from the parameters passed in the easiest and most go-like way to do this is to decode the parameters into our coin balance param struct which we made earlier we can use the gorilla schema package for this calling the new decoder function so this line is just going to basically grab the parameters in the URL and set them to the values in the struct now in this case it'll just grab the username from the URL and put it into the username field in the struct and the rest of this function is very straightforward so we again instantiate a database interface call the get user coins method set the value to our response structure and write it to the response writer and that's it let's go test our awesome API so if we call our API we get the token balance back for Alex if we enter an incorrect authorization we get a 400 error if we enter in a username which doesn't exist another 400 error we can also query another username like Marie and get back her coin balance as well alright so there we go we just built an API and go with authentication make sure you like And subscribe as I got some new stuff coming out less tutorial oriented but should be a lot of fun I'll see you then [Music]
Info
Channel: Alex Mux
Views: 337,462
Rating: undefined out of 5
Keywords:
Id: 8uiZC0l4Ajw
Channel Id: undefined
Length: 67min 53sec (4073 seconds)
Published: Mon Sep 04 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.