Introducing The GoTTH Stack - Go, Tailwind CSS, Templ & HTMX

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
it's no secret that modern web development has become insanely complicated and while these modern Frameworks are really good at what they do you often don't need a modern framework to get a lot of task done so introducing the goth stack now the goth stack features a very simple backend language being go and it also features a simple templating language and HTM X for interactivity and we've also got Tailwind in there for Styles so this video is going to be a Code walkthrough that's going to get you set up and running with the goth stack really quickly so if you have a look in the description below you'll see a link to the GitHub repository and you can clone The Goth stack from there so the first thing we're going to do is go through the file structure and I'm just going to explain a little bit about why things are where they are and as you're developing where you're going to put your different components then we're going to dive into the code and then finally we're going to run it and we're going to have a little demo so the first folder that we're going to include is our seam folder and this is just going to include our main.go the second folder is going to be the internal folder and this is where you're going to do most of your work so inside of the internal folder we have or now or only includes one package at the moment and this is token or and token or has some handy functions for creating a token and validating a token and that's it you'll notice a pattern in this file structure and one is that you'll have the folder name and then inside of that folder you'll have a file with the same name and then you're going to have some other folders inside of that folder as well the reason that we do this is because if you have a look in this o.o you're just going to see an interface for the packages so we have one package and that's token or and then we have an interface for this so this is something that's going to be repeated throughout the codebase and this is going to make it super simple for you to write tests for your features so now whenever you have something that uses token or you can pass in this interface and now you can mock it or you can pass in a real struct just something that satisfies this interface and that's going to be very simple for you to then test that the next folder is going to be our handlers and these are our HTTP handlers so these are going to be the handlers that handle all of the requests to the application so the files are prefixed with their method so you'll see here this is get about and then we have post login so the reason that I've done this is because there's two different scenarios in in terms of HTP here one is I just want to make a request to the homepage and I want to render a layout then the other is that I want to post some data back to the server and I want to get some hyper media back because we're using HDMX we're going to return HTML so I've just prefixed these to give you a good indicator about what it is that this is going to do so if it's prefixed with get then it's probably just going to render a template and that's it if it's prefixed with a post or you might have some puts in here then it's probably going to have some more interactivity in here so you can see the post login Handler is going to do a bunch of stuff in terms of login so I can get into this rendering stuff just a little bit later on after we've been gone through all the files okay so the next is our middleware and our middleware is just going to have two pieces of middleware in here at the moment one is our CSP middleware and this is going to add some CSP headers for us and this is really important in terms of security so you can see that we're creating three random strings here and then we're going to add those three random strings to context and then we're going to add a CSP header or a Content security policy that tells the browser what scripts and styles that we can run so there's one other thing to note here and I'll point it at now because otherwise it might be confusing this CSS hash here is a hash of the CSS that HTM X generates so when you run HTM X it's going to inject some CSS into the page and this is the hash for that CSS and then we're going to add this to our CSP header so we can tell the browser yeah it's okay to run this CSS the next middleware we have is some text handle middleware and this is just going to set the content type to text HTML and the reason that we do this is because we're using hmx therefore we're using hyper media and we're not going to be sending Json out to the client Okay so the next folder that we have is our store now in your application you're probably going to want to have a database but I don't know what database that's going to be it could be it could be postgress it could be redus it could be cockroach DB it could be anything and I don't want to put you in a box of using a specific database I want you to be able to take this goth stack and use it however you like so I've made a generic database package and this is just going to be an inmemory database and you should take this package and then you should inject your own database and then you can start using that database as you need to okay so we have this store interface here and we only have one store at the moment and that is going to be our user store and you can tell by now and we haven't even looked inside of DB store you can tell that our user store has two methods on it it has a create user and it has a get user so whenever you add a new store here let's say you're making a a CRM and you have a project model in your database then you'll want to have a project store and in here you want to create an interface for that project store and you'll want to Define all of the types like the project inside of this store and you can Define let's say you're using you can use your bon tags here and you can use your Json tags although you probably won't need to use Json and you can Define all of that here in this store package now when we go into DB store you can see this is part of the package DB store you can create your function to return a new store and you can create your store type and then you can have your methods in here that satisfy the methods in your store so this is another pattern you're going to see throughout the got stack one is we have a struct and then that struct is going to have all of the properties on it that you need and users can actually be lowercase we don't need to export that and then on that struct we're going to have some methods so if you wanted to change this to use a real database let's say you wanted to use in this user store here you would have your collection and this would be a collection and you would inject that into the user store I'm a big fan of dependency injection I think it makes testing way simpler and then you could start using that collection down inside of your methods here so another thing to note is when we create a new store here we're just creating one user in our in-memory database and that is going to have an email of one at example and a password of password and that's so we can start logging into the application straight off the bat we don't have to register a new user every time you restart the application because we're going to see later on that whenever we change a file the application is going to restart automatically okay and then finally we have our templates and our templates consist of two different types of files one is going to be this about temple. go and this is generated code this is generated by Temple so it says here do not edit and that's right don't edit this what you do edit is this Temple file here so Temple is going to include this templating language and the temple docs actually really good so I'm not going to go too much in how to use Temple because you can just simply read the docs and they're going to explain it much better than I can but basically you have a function here and you return some HTML it's actually that simple and you're going to see some examples later on of where you can create layouts and stuff like that actually let me show you that now so I have this layout here and in our layout of course we need a header we need a footer we need some navigation and then we can have our layout so our layout is going to include our header our nav and our footer so to render so this at header here just references this header template up here and then finally we have our contents and that's going to be inside of our main here so the last folder that we need to know about is going to be static so static includes two parts our CSS and our scripts so inside of CSS we have this input and this is going to be our Tailwind input and when we run our Tailwind compiler it's going to look at this file and it's going to look at all of our templates and it's going to figure out what CSS it should write and when it writes that CSS it's going to write it to this style that CSS so this style. CSS here you won't ever need to touch this this is generated automatically for you by Tailwind next is going to be our scripts so this first script is of course HTM X and this is a minified version of HDMX again you don't need to touch this the next script that we have is going to be our response targets so response targets is an extension of HTM X and this is going to allow us to use an extended response targets so we can have a response Target for when the server returns a 401 for example so if the user fails a login then we can update the text somewhere based on that 401 and I'll show you how to use this response Target it's very simple the docs are also very good then we also have some other files in here the first one is is going to be a air. toml and this air. toml basically is a configuration file for a program called air and air if you're familiar with uh node is like nodemon it allows you to start your application in a watch mode and then when you save a file it's going to restart the application we have a go mod and sum of course then we have a make file so this make file is going to include a bunch of commands that are really helpful for your development so one is going to be this Dev command and this is going to run the build command it's going to build into this temporary directory here and then it's going to run air and air is going to repeatedly build that whenever you save the file next we have a build command and this is going to run our tailwind and it's also going to run our Temple generate so Temple generate is going to Output these temple. go files here and then it's going to build our application next we have some Tailwind configuration so this is just normal Tailwind configuration you can put any Tailwind config in here that you like it's telling it to look inside of internal and templates for temple files to figure out what classes it should generate inside of the style.css okay and the next file we have here is our Tailwind CSS now if you're on an arm 64 Mac then this is going to work perfectly for you you won't need to change anything if you're on any other computer you're going to need to download the binary for your operating system so in the read me you'll see a link to this Standalone CLI so let's open that so you'll see here this is a getting started with the Standalone CI so by default it has the Mac OS arm 64 version but if we come over to the latest releases you can see there's a bunch of different releases for different operating systems so if you're on Windows for example you're going to download one of these windows binaries here or the executables and then you just need to rename it into t1. CSS and then you can execute it so you should then be able to execute it with the make file as usual okay so let's start walking through the code so we can get a more in-depth view of how this thing works so let's go into our main.go and the first thing that we'll note is that we're using GOI so you can replace this with any HTP framework that you like I personally like GOI because it's super simple and it doesn't add many things that are GOI specific so you'll see the handlers if I have a look at one of these handlers it's basically just a HTTP Handler where you have your respons wrer and your request there's no specific Chi object that you pass into these and that's one thing that I really like about Chi okay so we're going to first create a logger and we're going to use the new structured logger and I'm not sure what version of go this was available in but if you don't have access to the structured logger then you'll probably need to use a new version of go we're going to create our new router and then we're going to register our user store and you're going to register any other store that you have so register stores and the reason that we register these in our main is because we can then go in inject these into the handlers as we need next we're going to create our token or and again this is so we can start injecting this into the handlers as we need so you'll notice here that we're using a secret and so there's two things to note here one is you shouldn't do this you shouldn't use an algorithm that takes a plain text string for your JWT signing algorithm you should use something like rs256 that takes a public and private key and then secondly you should store this in configuration but we don't have any configuration yet if you do want that then feel free to make a pull request or I can add it in if enough people want that next thing we're going to do is to create a file server and this is so we can serve out our static content so our CSS and our JavaScript files the next thing we're going to do is to create a group of routes and then we're going to apply these middlewares here so we have our logger we have our text HTML middleware and remember this just adds a header to set the content type to text HTML we're going to add our CSP middleware and this creates some CSP headers as you saw before then we're going to add some JWT verify function as middleware and this is so we can use the user inside of our handlers automatically then we're going to register a bunch of routes so a route that is interesting is one that has some things injected into it so you can see here this login route so what we're going to do is we're going to listen to/ login then we're going to call the new login Handler function we're going to pass in some prams and then we're going to pass in some dependencies that that Handler might need so one is the user store and one is token or so this is what I mean when I say dependency injection this Handler has a dependency and we're injecting it instead of just requiring it or importing it straight from its package the reason that I'd like to do this is because you can create a test file for this Handler and by the way this has no test and I'm a big fan of tests and so if you want me to start adding tests then I can but my assumption is that you're not going to use the code that I have specifically here and so it's just another thing for you to remove yeah so the idea is that you can create a new Handler in your test and then you can just inject mocked versions of these stores here okay so the next thing we're going to do is to create a channel and this channel has a size of one and it has a type of os signal then we're going to listen for kill signals if we get one of these kill signals here then we're going to inject that into the channel and you can see down here we're just going to block the rest of the program until we receive something on that channel so if we do receive something on that channel it's because we've received a kill signal and we should begin the shutdown process so you can see we get a message here we're going to set some context and this context is going to be 5 seconds and this is just going to tell our server You've Got 5 Seconds to shut down if you don't shut down within that 5 seconds then we're just going to exit with a status code of one otherwise we're just going to exit the program as normally okay so let's go back up you can see that we're setting a port again if you want this in configuration let me know we're going to create a HTTP server then we're going to listen and serve on that server and we're going to do this in a go routine so we don't block the application right here so you need to block the application from exiting at some point and this point that this application blocks is with the kill signal otherwise it would be the listen and serve function that's going to block okay so that's the main let's go into the handlers and have a look at how they render templates so we'll have a look at a really simple one first this is just to render the about page so you can see here we're calling templates doab and templates doab is because we've put this in the package templates so if you call this package something else let's say you called it uh goth then it would just be internal SL goth and then this would just be goth thought about I've also seen some people put these in their main package I'm not sure why you would do that I guess it's so you can reference it anywhere maybe I'm not sure okay so we're going to execute that template and then we're going to get a temple. component back and instead of just rendering that component out we're going to wrap it in a layout so I'm going to say templates. layout and remember we had a look at this layout template earlier and we're going to pass in a title and so layout here here you can see takes a component and it takes a title so we're passing in our component here and our component is the about page and our title is my website then we're going to call render and we're going to pass in our request context and we're going to take in our response writer so so it's then going to render this template and pass it on to the response writer so we can respond to the user with some HTML and then of course we're going to catch some errors this error handling here there's probably something better we can do I don't think we just want to return a 500 error if the template doesn't render properly maybe we want to try render a backup an error page or something like that I'm not sure but I'm open to ideas here okay so let's have a look at a more complicated Handler so this is the login Handler and there's a few spelling mistakes which is fine I can fix those up so we're going to get a form values here then we're going to try get the user from our store if we have an issue getting the user we're going to render a template so this template is login error so we have a look at the login template you can see that I've created the login page template but I've also included a template for login errors and I think this is kind of a good pattern so if you wanted to include any other template that belongs to login I think this login Temple file is a good place to do that I think it's probably okay to couple these templates like that okay so we're going to render out the login error and login error is just a P tag basically and then we're going to hot swap that into the login error div and I can actually show you that in the login page okay so remember earlier I said that we're using the extension response to targets we have to put at the top of our page or the top of the component somewhere has to be this HX extend and then we want to put response Targets in here if we remove this then our response targets extension won't be applied to this page then if we respond with a 401 then our Target is going to be this login error and we have a div here with login error so if we respond with a 401 we're going to get the body of that response which is going to be this HTML here and we're just going to pop it into this div here so that's how we're going to show error messages to the user then we're going to try verify the username and password this is a very simple verification process there's no hashing or anything the password is just stored in plain text of course you want to change that then if they do match we're going to generate a token and we're going to set a cookie and all of that good stuff then we're going to use this HX redirect so this is a header that we're setting and then this is just telling HDMX when you get this header from the response do the redirect to the path that's supplied in the value otherwise we're just going to render the login so they've got their password wrong and we just want to render the login error which is invalid email or password okay so let's go to see this in action so we're can open up a terminal and if you're confused about what you might run so there's two places you can look one is going to be the read me is going to give you a bunch of commands that you can run and then the next thing is going to be the make file so you can see here we have a Dev command so we can say make Dev so you can see that our server has started successfully and we can go over to AOW and we can look at this port 8080 and see what we get so we get a very simple page here we get our footer down the bottom here we get our header and we just get this welcome guest so we can click on login and I can get this wrong so I can say something at something do I can pass in a password that's obviously wrong click sign in and you can see we get this error message here invalid email or password again this form is very ugly I know that that's okay because my guess is that you're probably not going to use the HTML that I put in here and so I don't want to make it super confusing to remove a bunch of classes and all that sort of stuff I just want to make it very simple for you to make this how you want it okay so we know when we start the server we get one user and that is one at example.com and their password is password so we can put that in Click sign in and you can see now we get a welcome one at example.com and we can continue to click around the reason that it knows who we are so we can refresh here A bunch of times and you can see that it still knows who we are is because we have this access token here and we have some middleware to look at that access token and it's going to verify that we are who we are it's going to read that access token and verify it and then it's going to put the user inside of context so we can use that and I can show you that now so if we come over to the index template you can see that we have two indexes one is just a normal index that says welcome and we have an email that we've injected here the second is just going to Be Our Guest template and that is just going to say welcome guest so let's go have a look at the Handler for this and this is going to be get home so you can see that we're trying to get the JWT claims from Context and from those claims we're going to try get email and we're going to cast that to a string and if this is not okay if we can't get email out of the claims and we're just going to render the guest index here so you can see we're doing this thing where we get the component and then we pass it into a layout otherwise we're going to render the index with email okay so I think the last thing is what if we change one of these templates what do we do how do we re generate these so let's say that guest now takes a title and that title is going to be a string and we just want to render the title so string so you can see that we try to render the guest template and go isn't complaining it doesn't care we've put a title on here and it's missing but yeah go just doesn't care so how do we make go care well we have to run make and actually forgot the command so I'm going to have a look at my make file and it's going to be Temple generate so make Temple generate and then this is going to generate the templates again we can go back to our Handler and you can see now that go is complaining that it wants one string here and we gave it none so let's remove that and we can regenerate the templates and then go will be happy now so what about Styles what happens if you're changing your Styles and you don't see them reflected well there's one of two things that you need to do one is you can watch Tailwind watch and this is going to run the Tailwind binary inside of a watch command and so every time you change a style it's going to regenerate your styles for you otherwise so if you're just developing normally you want to have this watch command running in the back ground otherwise you can build the Styles and this is going to build a minified version of the Styles so you'll want to run Tailwind build if that doesn't work remember you need to use the Tailwind binary that is suited for your operating system this Tailwind build command here you probably won't need to run that manually because it's included inside of the build command here the last thing I want to show you is if we have a look at the view page source and we zoom in a little bit here and we can scroll across you'll see these things inside of the HTML called nons and if you're British you might find this a little bit funny and it's just this random string here and this non tier is generated by that CSP middleware that I showed you earlier and this is just going to tell the browser when you see a Javascript file with this nons you are allowed to execute that the server has said that this is the file that we expect you to execute there's nothing being injected here the reason it knows that is because if we come over to our Network panel and we refresh we can have a look at Local Host here and let me zoom in so you can actually see and we should have a header here content security policy and you can see that we have a script of not 7 blah blah blah let's copy that and then find that and you can see that matches our script here we'll also have well we'll have two two because we have two scripts and we'll also have one for our Styles and then we'll have the hash for the Styles as well so that is a basic overview of the goth stack if you like this video please make sure you leave a thumbs up if you use the goth stack please make sure you go over to GitHub and give a star to the repository and let me know in the comment section below what you think I should add or change about this stack thank you for watching and I'll see you in the next video w
Info
Channel: TomDoesTech
Views: 18,903
Rating: undefined out of 5
Keywords:
Id: k00jVJeZxrs
Channel Id: undefined
Length: 28min 29sec (1709 seconds)
Published: Sat Feb 10 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.