Purpose of Slugs
Slugs replace numeric IDs in URLs to improve SEO, readability, and shareability. They create stable, human-readable links and improve analytics visibility by clearly identifying content.
Database Migration
A new migration adds a slug column to the articles table, with corresponding rollback logic. Migrations are reset and reapplied to ensure the schema aligns with seeded data.
Model and Query Updates
The article model is updated to include a slug field. A slug-generation library converts article titles into URL-friendly strings during creation, and database queries are regenerated accordingly.
Seeding and Database Reset
Existing seed data is regenerated after resetting migrations to ensure all records include valid slug values. This ensures consistency between schema and data.
Routing and Controller Refactor
Routes are updated to use slug instead of id as the URL parameter. A new model function retrieves articles by slug, and controllers are modified to use this lookup.
Final Result and Next Steps
The application successfully serves articles using human-readable URLs. The next step introduces Markdown processing and converting Markdown content into styled HTML.
Let's tackle this slug and human readable URLs I mentioned in the last episode. First of all, why are we using what is called slugs instead of the ID that we have now? Well, it's better for SEO, search engines understand these URLs better and they rank better. They're more trustable, so you can actually, you can read it as a human and make sense of what it is, right?
It's easy to share. It provides stable links. And you can also have more readable analytics. So if you have something like some analytics tools, you can see what pages, the user visits, and you can quickly see what article they are reading. So that's some of the main points as to why we want to use them. Now, what do we need to do? We need to add a slug column to the articles table.
we need to update our model, and then we need to update our views. And then finally, we also need to update our seed data, because what we're gonna do is to take the, or we're not gonna need to update our seed data, sorry, we need to reset the database and we apply it, so we have the seed data created with this slug in mind, because we're gonna be adding it to the model.
and using a library that is simply just takes the title of the model and created into this slot. So let's begin by dealing with the migration. So we just create another migration using the just command like before, just create migration, alder articles table with slug column. There we go. We now have a new migration and then we go to database.
migrations, the new one here, and we say, all the table, if exists, articles, add, column, slug, var, char. And for the down version, we simply say, all the table, if exists, if exists, articles, drop, column, slug. To apply it, we can just jump out and say, just up migrations.
And we can see that it's applied, cleared out. And then we can actually deal with our, we need to update our query. So we actually insert the slug field or the slug, something in the slug column, whenever we create an article. So we need to go into our database here. And when we query, all stays the same, all stays the same. But now we also add a slug to when we create an article, right?
six yes and now we say just generate queries everything is good go into our models our article and let's add a slug here string jump down to our row to article add the slug row slug string finally in the create article what we're going to do is simply to say slug and then
We're gonna use a library called slug, let me say slug make, and then we're gonna pass the title of the article, and now we have an import that we need to add, so we're gonna say, we're gonna say here, we can use code actions and say one, go get, so we're simply just adding to the go.mod right. Give that a save, refresh my LSP,
And actually, if we go into our go.mod file, we can see we now have this package that we just added. Great. Article. Slug make is a string. And of course, this has to be a PG type text, where we have the text being the slug. And then we say valid. And let's just say, we're going to say data.
title does not equal an empty string, then we know it's a valid one. And it's not text, what is this string? String, there we go. All right, so now whenever we create an article, we will have a slug field filled out based on the title, which is exactly what we want. But we currently also have seed data in our database that does not have this slug field. And a quick way to go about adding this is that we just say just
Reset database. No, just reset migrations. There we go. So this just undo all of the migrations and we can see our down migrations work. And then we can reapply our migrations and we have a fresh database that we can now add the seed data to that will then get the slot column filled out based on that title. Now let's apply the seed data one more time.
We create all of the articles. Let's just run it. Switch over back to the browser. Give the page a refresh. We have all the articles back and we're still using this ID here. So going back to the terminal, kill the server. And now in views here, we can say instead of
Instead of ID, we are going to be using slug. Give that a save. Let's run it one more time. Switch back over to the browser. Give it a refresh. And now, you can see down in the left corner, we have GRPC for modern APIs that has the dashes between where the spaces used to be. But if you click, you get an error.
switching back to the terminal here, because we need to update the router. Sorry, the route is no longer an ID. It's a slug. You could technically call it ID, but let's be specific. So it's a slug. And then in our controller here, we can say slug. And then we don't need to pass it anymore.
So we're gonna say just slug and slug, get rid of this error. And then we need another model function because we cannot use an ID here. So we have to say find article by slug, pass in the slug, go into our models, the right models here. And then we can simply take all of this, create the function find article by.
Slug, accept a slug. That's just a string here. Pass the slug, query article by slug. I hope you follow along here. I mean, nothing new. We are just adding the entire flow rate. So the final thing we need to take this and say find by slug. Slug.
Give that a save, just generate queries. And now, if you say just run, do we have any error? We do, because in models, we need to handle that this one accepts a pgtype. So basically the thing that we had down here,
We're just going to add that to say slug here. And then instead of comparing data.title, we're just going to say it's valid if it's not an empty string. Now can we run? We can. Let me switch over to the browser one more time. Give this a refresh. And there we go. Now we have this nice readable slug in the URL.
We can go back and we can find other articles. We cannot, what mistake did I make? So let's go back to the browser here. Sorry, the terminal. Views. Here, we are not replacing the ID.
We are just replacing the parameter that we added to the route, right? So now, let's run this one more time. Browser, give it a refresh. And there we go. Now everything is working as expected. So we have...
We have human readable slugs. We have the URLs that are friendly to search engines. Now we need to talk about markdown, what is markdown, and how do we actually make this here into HTML so we can style it and make it look nice on the page in the next episode. But next, sorry, next module, we are going to be doing styling. So in the next episode, we are going to be
We are going to be dealing with what is marked down and how to be past marked down to HTML.