How to Build a Terminal App in Go/Golang with the Bubble Tea TUI Framework and Urban Dictionary API

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey what's up y'all in this video we're going to use the bubble tea library to build a CLI app to query the Urban Dictionary API and display the results if you're not familiar with the elm architecture already I covered a high level overview of it in a previous video so definitely check that out and just a heads up for the sake of this demo I'm going to put all the code in the main.go file but in a production environment you'll probably want to consider breaking this out into separate files all right so let's go ahead and get started so first thing we're going to do is kind of just lay the foundation of what we want to do with some comments we're just going to say new program with initial initial model and program options and then we'll say run and then now we want to Define all the parts of bubble tea so we'll say our model is our App State um we'll want to have a new model function that returns an initial model we want to have an init function using the model as the receiver type and this is going to kick off the event Loop we also want to have an update function that will handle messages we also want to have a view that is going to basically return a string based on the state of our model and then something I'll kind of put on the back burner for now are commands and messages so let's go ahead and get started um we'll just kind of add some boilerplate here we'll say we have a program and um actually we need to go ahead and get some stuff first so we will open up our terminal and we'll say go get github.com bracelet slash Bubble Tea and then we will say we also want the bubbles library and the bubbles library is basically a bunch of components for bubble tea so let's see we'll go ahead whoops we'll go ahead and close this and now we should have this available Okay cool so we have our program here um that we can pass our initial model in and then we want to go ahead and run it so we can say p dot run we can see that it Returns the model and error but we don't really need to use the model for anything we do want to handle the error though and if we do have an error we will uh just log fatal that error um and as far as our model goes our model is going to be a struct with any number of fields so we'll say type model is a struct and we'll just say for right now we have a a title which is a string um and then let's say a new model will return our model and this is kind of going to uh this is going to be our initial model so we can say our title is um we'll just say a hello world all right we'll go on to our knit we'll use our model as a receiver type and the init is just going to return a t command which is a function that takes in no arguments and returns a message for now we won't return anything so we'll just say return nil um let's see the update we'll also use the model as a receiver type and this is going to take in a message and it's going to be a T-Type message and this should return let's see what does this return I think it returns a model and a command so for now we'll just return the model and nil um and then we also need a view which is going to return a string and we'll just say this is going to return the title and let's see so now that we've so the model um aside from being struck is also an interface and to satisfy the model interface and bubble tea we need the init the update and the view functions okay so now that we have a new model method we can basically say new model um and then we can pass in our model here and let's see let's run it let's see if this actually will run Okay cool so we got hello world um and now we want to put some other other functionality into it so let's think about what we want to do um what we want to do really is have an app that has two main components one is going to be the text input and the other one is going to be just a component that can display some text so let's work on the text input so for the text input we don't really need this title anymore I'll just go ahead and leave it in there for now we want to say we have a text input and that's going to be of type text input.model um and then we'll go into our new model we'll initialize this text input dot new and then we want to make sure we want to make sure that we return it here and we're not going to be able to see it unless it's in our view so we'll kind of use this as the base View um we'll say oops for s okay let's just say s is M that's X input and it does have a view and we will return this view for now so let's run this and let's see if we get anything going here okay so we are missing some stuff here that we need so oops so let me run into my tidy let's see if this this didn't oops let's get out of this got my shortcuts all mixed up um okay so I think we have our text input here so um what we need to do is let's see what do we want to do here so on a knit I think we want to say text input blink and we also want to focus here so we should uh sub placeholder and we can say [Music] um enter search term for now maybe we can think of something better later and we will want to focus our input so let's go ahead and run this again and cool um we have we have the makings of something happening um I think also in the update we are going to want to say and text inputs um let's see so let's define a command so we'll save for our Command is LT command and we will assign that to text and but I think M text input update message let's see if this will compile uh command is declare not used okay so we'll just return command here um cool so it looks like we have what we need going on so far um but this is not very useful at the moment because we have to wire some stuff up so ideally what we want to happen is we want to type in some sort of search term like Yeet hit enter and then hit the Urban Dictionary API so let's see um in the update function we want to um say message is let's think about this so we want to switch on the message actually and we want to switch on the message type and we want to say um if this is a key message so basically if this was some keyboard input we can switch on that message type and we can say on the enter key press we want to call the Urban Dictionary API with our search term so we can say V is M text input dot value and this will return us a string of what's in our input field and we can basically call a let's say handle query search which we need to implement with the value so I'll copy this and this is going to talk to the outside world outside the event Loop so we want to make this a command and we'll say um font candle query search that's going to take in a query string and we want to return a command we're going to want to return an inner function of let's say I guess it can be an anonymous function that takes another input that returns a message and this is where we want to [Music] um make our HTTP request so we'll have the URL here um and let me goes check out what the URL is okay so let's see the URL it's https oops this should be wrapped in a string https API dot Urban dictionary.com slash P0 slash Define term percent s and then I should wrap this since we need to do some formatting in a Sprint f and we'll just say um let's see I want to query escape this so we'll just say query escape and then we want to create our HTTP call first I'll do a context with timeout and we'll just say that if the uh the HTTP request is taking longer than five seconds just cancel it um then we'll just defer that and let's see so we want to create a request um so we'll say ACP new requests with contacts we'll pass in our contacts this is going to be a get method uh we'll pass in our URL and we have no body to send up we'll do our good old golang error handling and let's see so instead of returning an error here um let's return a message so we can see that we're returning a message a message can be basically of any type that we wanted to find so we can even say uh error messages of type error [Applause] and then we can say if error is not nil here we can return error message um wrapping our error um let's see so next we want to create the request and capture the result of that request or yeah the result of the request and our response so we'll say default client do request and then we'll kind of do the same thing with error handling here we'll copy this pasta there um and then we want to make sure that we clean up our resources so we'll say res.body.close and let's see so I kind of just want to see what the request looks like so what I'm going to do is I'm going to read in the request and that returns a bite slice and an error um let's see I'm going to basically just get this to compile and let's just set a break point here I just want to see what the request looks like we'll run the debugger whoops let's see okay so not enough arguments to call read all oh duh so we need to pass in the res body and I'm missing a return see I think I'm missing a return in this inner function so we'll return nil here I don't want to run it I do want to run the debugger so that we can break uh when we get the response so we'll type in whoops so let's jump to our debugger we'll type in yeet okay so we have nothing happening okay so let's think about this for a second so let's see on key enter um let's set a breakpoint here and let's type in eat okay so we do reach this we just have an issue um inside of our inner or inside of our Command function so let's go down to our Command function oh I'm in the wrong place uh let's go to the command function there you go all right let's just see if we can reach this one here we'll resume okay that looks good um let's see okay let me just put a break point here and then we'll resume Okay Okay cool so I just had a break point at the wrong place um so we'll go ahead and remove this breakpoint here and now we're going to take a look at what our response looks like we can kind of see um for Yeet the definition is a one word a word one may scream um so I'm gonna copy this and I'm just going going to uh let's see I'll paste it above and it's a nice little thing I like about go land is when you paste some Json in it automatically uh automatically defines a struct for you um so let's call this uh let's just call this terms all right so all right now we are going to um we're going to decode the Json so if you want to create a variable called terms that we're basically going to decode our Json and assign to our terms variable here so we'll say we want a new decoder with the res dot body we'll call it the code um and we want that to go to our terms variable we'll handle our error here and we'll go ahead and copy this so that we're sending our message here and then let's create a new message type we'll just say terms is going to be or terms message I should say is going to be of type terms and now we can say terms message terms here all right um and now we got to go back into our update function and make sure that we can handle that so let's see so let's say case uh terms message and we actually want to put this on the model so let's just say terms is of type terms and we're going to say uh when when we receive the terms message from the Urban Dictionary API we want to assign it to our model um that let's see maybe I need to do let's just see what our our compiler says here cannot use variable of type terms message as terms in assignment uh it doesn't want see terms terms message [Music] uh let's see okay so think what I want to do actually is Define this as a terms response message this is going to be a struct with a terms field and a error so that way we can basically just handle it in a single case it's a little bit cleaner I just gotta clean this up oops error error copy pasta here copy pasta here oops that's not what I want to do okay and then we'll I guess I could copy pasta here and just change this part terms terms cool all right uh 66. let's see okay cool so we've got our terms message [Music] um say terms message oh this change so this is now terms response message and this should be message.terms um and we can probably say if message dot error is not nil then we can also have an error on our on our model um um let's see so we'll just sign error to message that error um and then let's see let's think about this so maybe we'll want to return the model here and nil um all right let's just see if we can compile we can compile let's make sure that uh nothing here is broken we'll say yeet um nothing's happening uh because we do have to wire up the view now so now that we have the uh the data available on our model we should be able to automatically trigger re-render uh when the model is updated in order for us to update the view so let's say um if m dot terms if we have if we have any terms then we should basically look at this we can say MDOT terms dot list and I think what I want to do is actually check the do I want to check the terms let's let's think about this um I think the list is what we want to check because this looks like a slice of structs and I think these structs are the definition types so yep so we can see there um let's see so we'll just put the definition add a couple new lines for formatting and let's see let's see if this will work so we'll run that we'll jump to our um command line here say yeet and looks like it looks pretty terrible but we see something something is happening um I think what we wanted to is add a couple new lines here which is kind of you know we'll clean that up at some point um we'll jump to our command line Yeats cool uh let's see and we can also say bet pull drip cool all right so one thing we can do also is there's some other info on the struck that um you know is kind of interesting to see and that is like the upvotes and the downvotes so we can basically let's do this we'll say about um and then this will be an end let me make sure this is in it thumbs up and let's see yeah it looks about right thumbs down it yep okay so of votes we'll put a new line for down boats um and then this should be this should also be uh what am I what am I trying to say here this should also be an INT that we want to format with percent D we'll throw a couple new lines in here and we will say m dot terms dot list uh zero thumbs up um and then we'll just say oops we'll just say thumbs down uh let's see if this actually worked uh we'll jump to our command line we'll say yeet and it looks like it's working a word one may scream blah blah blah upvotes 4800 downvotes 538 um tried that 893 and 137 I'll try drip um for 473 and 55. so it looks like it's working um and let's see if there's anything else we want to put so the definition word current votes run on oh let's put the example so that would probably help so what we can do is concatenate the example here um and I think this should work let's try to run it all right so oh let's see uh let's see what else trip cool um but it's kind of annoying because you can kind of see that uh my actual terminal is getting in the way so uh one thing we can do is if we go up to new program um we can pass in some optional commands so we'll pass in um TDOT with alt screen and that essentially gives us a full screen terminal UI we'll go ahead and run it again and we can see that my terminal is no longer in the way and we can type in whoops keep doing that so let me jump to the command line type in yeet we can type in bet we can type in trip and now we're full screen cool the MVP for this app is pretty much done there's definitely more that we can do but hopefully this gives you a pretty good idea and a pretty good overview of how the bubble tea architecture works and how we can structure our apps
Info
Channel: gzip Christ
Views: 7,019
Rating: undefined out of 5
Keywords: go, golang, bubble tea, terminal, command line, cli, tutorial, elm
Id: DEeFnVj3cv8
Channel Id: undefined
Length: 27min 26sec (1646 seconds)
Published: Fri Mar 24 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.