Module 1 - Introduction
1. Welcome to the course2. Why Go3. Why start and build a blog?4. What about React/Vue/Angular?5. Getting setup and source filesModule 2 - Tech Stack Walkthrough
1. Introduction to Golang Part 12. Introduction to Golang Part 23. Introduction to Golang Part 34. Structuring Golang Applications5. Templating with Templ6. Just enough interactivity with HTMX7. Getting started with postgres8. Servers, routers and endpointsModule 3 - Creating the MVP
1. What are the minimal requirements?2. Doing some initial plumbing3. Embedding static assets4. Creating our first views5. Tailwind & Utility-first CSS6. Styling the Landing Page7. Styling the Article PageModule 4 - Managing Content
1. Choose your own adventure2. Writing in Markdown3. Parsing Markdown to HTML4. Frontmatter and Meta Information5. Making our code examples look nice6. Adding error pagesModule 5 - Adding the Database
1. What is a Migration?2. Our first migration: articles table3. Creating the Database Layer4. Showing the Latest Posts5. Slugs and Human Readable URLsModule 6 - Managing the Blog
1. What are the minimum requirements?2. A new layout approaches3. Creating a rough outline4. Our second migration: Users Table5. Introduction to authentication6. Storing passwords securely7. Authenticating users8. Remember me9. Managing posts using a hypermedia API10. Our third migration: Altering Posts Table11. Only show posts marked readyModule 7 - Adding Subscribers
1. What are the minimum requirements?2. Our fourth migration: Subscribers Table3. Creating the subscription form4. Adding some interactivity with HTMX5. Saving new subscribers in the database6. Verifying subscriber emails7. Our fifth migration: Tokens Table8. Email validation view9. Email validation tokens10. Sending validation emails with SES11. Making it all come togetherManaging Content
Parsing Markdown to HTML
Summary
In this video, I show how to build core functionality for embedded articles in our blog. I demonstrate how to store articles in binary format and pull them from the binary, implement markdown content handling, and display properly formatted articles in the UI. I create two directories - one for code and one for articles - and show how to embed markdown files using Go's embed feature. I implement functions to list available articles on the homepage, parse markdown to HTML, and render the content with proper styling. While the basic functionality works, I note that we still need to add syntax highlighting for code examples and handle front matter/metadata in the next episode.
Transcript
In the previous episode, we explored different approaches to storing block content. And for the simplicity we are gonna be going with the binary approach. So in this episode, I want to start building the core functionality to make our embedded articles work. So we will create the foundation for handling markdown content for pulling out these files from our binary and then implemented in the ui. So we can actually start seeing some properly formatted and rendered articles from markdown content. So by the end of this video we will have all of the files that we passed through the binary. We embedded all of the articles embedded. We will list all available articles on the homepage. We will also pass the mark down to HTML and actually display properly formatted content on our block. We'll need two directories to get started, one to half hour code and one to hold all of our articles. So create one called articles and then another one called content. If you jump into content, we are gonna add one dummy article that will literally just for now hold some little ip, some text with a little code example here. Then let's jump out and create An article. Go file. And for now, we are only gonna add the logic to embed articles and pull them out of the binary. So again, we need to reach for embed. So let's create a wearable vehicle embedded articles go. And then we need to say go embed. Please add everything on the content that ends with an MD extension. Next, we'll need to create a constant here called Content Dear called Content. And then finally we can actually start to add some logic to pull out articles. And we're gonna create a function here called Get All. And what it will turn is little, just a string slice or an error. And we are just gonna pull out the file names for now. So we have something to show on the homepage show, let's say files error equals embedded articles directory, and then content directory. That was the wrong one. Content D, there we go. And we are not really gonna be handling errors at this level. We are gonna handle them higher up in the controllers. So for now we're just gonna return the error, do nothing with it. And then let's actually pull out these file names. So create a variable called file names. There's a string slice and then we look over All of the files in the, that's embedded into the binary. And since we can technically have directories in here, let's just check if the file we are dealing with is a directory or not. And if it is a directory, we're just gonna continue the loop. And if not, then we will say file names or pens and to file names. And we just want the name before to actually, to actually the extension, right? So let's say strings splits and we are gonna split file name and we're gonna split on that and we are gonna grab the first element. There we go. And I have misspelled file names and with that we are just gonna return file names and nil. And let's just quickly update our homepage component to accept an argument called files that is a string slice so that we can now get rid of all of these hardcoded article cards. So we are just gonna loop over whatever many files for now that we get, uh, get sent from the homepage controller. And we are gonna use the file name as the title and we are gonna say sprint F and use the file name as well for the as the sl. There we go. And then we simply upgrade our home controller here. And for now it's only gonna be gonna be file names. So let's just call this file name so it's very clear where we are at the moment. Articles get all handle the error and we will soon start to handle or add in error pages. So we have like four fours and 500, but for now we don't really need it. So let's just say error context. Will we lock the error to the console if we have any code, not get all articles, an error and it's error. There we go. And then we just pass file limbs to the homepage, give that a save and jump into our browser, do a refresh. And you can see we now have the one article that we created, but we also have some weird formatting issue down here. We need this, this gray area to take up the entire, the entire height of the screen. So let's quickly fix that and we just need to go into the, to the base component and jump down here to our main river and say flex and flex color. Go, go back here. And now we take up all of the height of the of the screen. Alright, so we can pull out articles from the binary, but to actually pass them into STML that we can show on our block. We're gonna need a library, a package called Gold Max. So let's just quickly get that and then open up our articles that go file once more. And we need two function here. We need something to pass the, the content or the file content and then we need something that will, that will get that file out for us, right? So let's begin by creating a past content function that takes in content of type string turns as a string or an error. And what will happen here is that we, we take whatever is given to us and then we convert it into HTML and then the, the function we're gonna be used will will simply just use the IO rider interface and put this into something called a bytes buffer. So this create here an HTML output variable of type bytes buffer that will simply just hold the bytes. Next up is say if error equals gold mark do convert and gold mark converts needs a byte slice where we're gonna pass the content and then we are gonna pass that into HTML output. And same as before, we are not gonna be handling errors at this point, gonna handle them in the controller. So we just return and empty string and the error. And if there's no error, we return HT ml output string and nil. And then finally, for the function to actually pull out the articles, let's just call this Git article, just call it get, since we in the article package, right, it's gonna take in the file name type string return as a string or an error. It's bill Pretty much same as before with the get all function. We are gonna see content error embedded articles and then now sorry about that. And then we are gonna see read file instead and we're gonna pass it the, the path to the, we need to pass it the path as well as the file name. So we're gonna be using Sprint again here to say first the content, uh, directory name and then the file name, then the extension. So we say content dear and then file name. There we go. Same as before. We handle error at another place. And then we simply see return pass content and pass it and wrap it in a string of content. And now we can actually pull out and pass our articles into HTML. And when that in place, we can now go ahead and update our article page component. And let's just start by adding a content argument. And then we can get rid of all these place of the paragraph text and use content instead. And now we are gonna be be receiving a bunch of dynamically generated HTML text. So to actually have some styling that matches our color scheme. We will need to specify here on the article element that we want everything to be text base content. And since we're using this pros plugin, we also need to specify the colors of the header. Since pros provide us with some defaults that might not match the color scheme that we have. So let's just say pros H two text base content. So all of our header two tech hiter HITER two text will use this, this text based content color, right? And you'll need to add this for HITER three, HITER four, HITER five if you have them, but for now we only have HITER two. So let's just keep it like this. And then one final thing in this component, we need to escape strings. And that is because Temple has some security features. That means that if we pass a string, it will just render that string. So right now, if we were to just pass the article content to the article page component, we would just see a bunch of HTML text as string that rendered in the browser. So to get around this, we need to create a function called unsafe. And this function will simply just return a temple component and it will take in some, some HTML. Our ML is a string. And in here we return a timber component funk. And this component funk needs, uh, the cont a context and an IO rider, which will get passed in through timber. So here, all we really have to do is to say playing error io right? String and pass the IO rider or IO IO rider, yes. And then the HTML. And then we return the error and now we just go down here and say unsafe pass the content from the we get for the controller. And now we can actually render the LY generated A CML content. So one final thing before we can actually see this on our block, we need to update our article controller and simply just grab the article out of the binary. So gets and pass the slot as the file name. Same as before. We are not really gonna be handling errors right now, so there's just return could not get article. And for good measure, it's also at the SL in here. And then we update the titles of the SL and we pass the article. Now if we go into the block and refresh and then click here, you can see we actually have our article rendered as HTML. So we have all of these HTML elements that comes from our passa function. So that is looking good. Our code example here is looking a little bit boring, but we will tackle that in just a second. Great. So now we have our markdown articles being successfully embedded in the binary and when not on the on the block. However, as I just mentioned, our code examples are looking a bit boring. They need some syntax, highlightings, some line numbers, right? And we also need to handle what is known as front matter or metadata about the articles because we shouldn't really be showing the file name as the title. We'd also maybe like to have guest offers. So we also like to specify the name of the like the author name of the article, the data was posted, the description, some text to describe it. So all of these things we were handing in the next episode. So we can actually start having a full blown block that you could release tomorrow.