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. Introduction to authentication4. Our second migration: Users Table5. Storing passwords securely6. Authenticating users Part One7. Authenticating users Part Two8. Remember me/Forget me9. Managing posts using a hypermedia API - Part One10. Managing posts using a hypermedia API - Part Two11. Managing posts using a hypermedia API - Part Three12. Managing posts using a hypermedia API - Part Four13. Implementing CRUD For Articles - Part One14. Implementing CRUD For Articles - Part Two15. Implementing CRUD For Articles - Part Three16. Implementing CRUD For Articles - Part Four17. Flashing Ourselves/Providing Visual Feedback - Part One18. Flashing Ourselves/Providing Visual Feedback - Part Two19. Flashing Ourselves/Providing Visual Feedback - Part ThreeModule 7 - Adding Subscribers
1. What are the minimum requirements?2. Expanding the database: Tokens & Subscribers3. Creating the token and subscriber models - Part One4. Creating the token and subscriber models - Part Two5. Creating the subscription form6. Saving and verifying subscribers - Part One7. Saving and verifying subscribers - Part Two8. Saving and verifying subscribers - Part Three9. Emails and Clients - Part One10. Emails and Clients - Part Two11. Emails and Clients - Part Three12. Our fifth migration: Tokens Table13. Email validation view14. Email validation tokens15. Sending validation emails with SES16. Making it all come togetherAdding Subscribers
Creating the token and subscriber models - Part Two
Summary
Coming soon.
Transcript
All right, so we need three more methods here. We need one to create a token, to get a token, and finally one to delete a token. So let's just begin by creating the new token, func new token, that will return us a token or an error. All right, there we go. And our new token will take in the context and it will take in the dbtx from the database and will take data new token payload. And the token payload will look like this. It's a struct with x, let's grab all of this actually, expiration date and meta, so it's just gonna be this. So we're gonna make required and thinking about it, actually we don't, of course, we don't need this on the model, so let me just remove this. There we go. Now we will, in our new token, say if error, validate structs, data, token, and error. So this is maybe one of the things why people complain about Go being a bit verbose. Anyway, let's create our hash. So we're gonna use make to create a byte slice of 32. And then we say if blank error, and then we use rand with read, pass the bytes. Error does not equal nil. Again, just return an empty token and the error. Finally, we can say hash is equal to hex encode, hex, there we go, encode to string, pass the bytes, and then finally we have the hash here. Now, to create the token, we need to first, let's just create the payload here. Should we call it payload? No, we actually only really need to create the metadata because that is gonna be encoded as a JSON representation right in the database. So we'll say JSON, marshal, data.meta. You know the drill, just return. And we actually have the wrong import here, rand. And we cannot get that from crypto. We could, all right. So we marshaled the metadata we received, and then we just say row error equals to dbstatements insert token, and then I'll just fill this out as usual. It's quite straightforward. We create a new ID. Let's create the now, and use this time, time, time.now, and it's also valid. So we can pass it here now. We have the hash and the metadata. Maybe let's just call this meta information to be a little bit more consistent. Meta information. Finally, we need this expires at. So we can say time, data, there we go. And this is valid because we have validated the data. Again, and finally, we need to return the token. So let's return token, nil. Fill token, so row.id, row.createdAt, row.updatedAt. This is updatedAt. Ah, of course, we do not have an updatedAt. So we can actually remove this field. Row.expiresAtTime, hash. Let's just use the one returned. And row.metaInformation, which we cannot use because this is a byte. So we just return the data.meta. And maybe we should just quickly turn this into a helper method as we did with our subscriber. So func convert dbToken, row dbToken, and return here token, return. And we should probably, yeah, so we can actually, because we get, we can just unmarshal directly in here and handle it. So we're gonna say the meta, it's a byte slice, and then we say metaData. And we have the metaInformation, right? So if error, JSON, on marshal, metaData, then we return token empty, and the error, so we actually also need to return an error here. And this one is gonna be nil. All right. So once, of course, the meta, there we go, metaData, and we also need to add some JSON tags to this. There we go. This is starting to look good. So let's say token error, we could actually just say convert, pass in the row, delete this, and not to subscriber, but to token. Convert dbToken, convert, there we go. And again, of course, we don't need, because this is already going to be row dot, yeah, yeah, all right. So this is also a little bit more clean. All right, so this is our new token model. We validate the data, we create the token, we set the expiration date, and then we return it back to the caller so we can use it for validation in emails and whatever we might need it for. So we are almost done with our models. We just need to create one for getting it, like pulling the token out of the database using the hash provider, and a way to delete it. So I'm just gonna grab this again here and say getTokenByHash, and then, of course, we need to accept the hash as a string. And then we can say row error dbstmts query subscriber, no, query token by hash, hash the goal. Again, just same error handling as always. We're gonna handle it higher up in the chain. Let's say return. So this is where we were both, but you can see once we get the basics out of the way, it gets really, really fast to add, and we have type safety, and we have signatures and everything. So a little bit of front work makes this much, much faster down the line. Finally, deleteToken. It's just gonna accept an ID, and here we will say deleteToken ID, and we are just gonna return an error here. So we can actually just return this thing, delete all of this, and now we are basically ready to build the UI elements, and we can start having subscribers sign up for our newsletter. That doesn't exist yet, but at least all of the internal in the database and the models is ready to start implementing this logic.