Everyone said it would be 'easy'
Sometimes when I'm trying to do something simple and failing, like get a sitemap.xml file for my site, I get serious impostor syndrome.
When I was looking around, all I could find was how 'easy' this was supposed to be. Nothing will make you feel worse of a developer than trying to implement something simple for hours and failing.
Eventually, I was able to figure this out, and hopefully you don't have the same struggle I do.
TLDR: Code Snippet
If you just want the example, I've linked it here on CoderNotes
Explanation
First, an overview of how this works. Sapper allows for Server Routes, which are javascript files that can have get, post, put, or del operations on them. You can call those manually using a fetch() request, but we can use this behavior to create a sitemap.xml, since browsers make a GET request.
So, we make a sitemap.xml.js file in our routes folder, and export a get method. Check the note above to see the specifics.
Step 1: Get the list of your urls
Now, since these are server-side rendered functions, we are a bit limited in what we have access to in here. Our first job is to get access to the data we need to build the sitemap.
If you're lucky, you have a finite set of static files that you know of ahead of time, because you don't handle user-submitted content. If so, you can probably just get the urls the same way you do for the main portion of your site.
However, if you handle user-submitted content like I do, you'll need to go and fetch() your data.
A word of warning here. I originally tried to implement this sitemap by using svelte-apollo, since my database uses a GraphQL endpoint. svelte-apollo returns a store, and this totally broke. I'm not sure if there's some way to fix that, but your best bet is to make sure your data comes back to you as raw json from wherever you are querying. It'll make your life a lot easier.
Remember to call .json() on the response if you made a fetch() call!
Step 2: The render() function
Now that you have your data, we need to write a render() function. There's nothing special about this name, it's just a function that is going to take our data and turn it into a string that the browser can read as xml.
It's all very un-svelte like, so I'll walk you through the function:
First, the whole result of the function is one big string. We can tell javascript that we want a multi-line string by using these ticks `` instead of regular single quotes ''. The first two lines of our sitemap are boilerplate required by a sitemap.xml.
Next up is the interesting part. The ${} syntax is a template literal, meaning that whatever is inside becomes part of the string. The way it works is like this:
var myVariable = "Kevin";
var myString = My name is ${myVariable}
;
However, in this template literal we aren't just plugging in a variable, but calling a function.
The map function does the following:
1) Is called on the array of your data
2) Takes each item in the array and runs the function you pass in on it
3) Combines up everything you return back into one result
In our use case, we are going to map our array or data into a list of strings.
2b) map()
For each url we have, we want to return a block with a tag inside of it. We'll use another template literal to plug in the url.
You'll notice that I have a helper function for my example, called getPath(). I have to do this because when I fetch my data, I don't get back the raw URLs. So I need a function to build them (and since I wrote the logic of how they get built to begin with, that logic is just copied over here). You probably won't want to copy that function, and instead just use your head on plugging in your URLs.
Finally, you will get a bug and you don't call .join('') at the end! If you're noticing a bunch of commas in your sitemap, it's because you forgot this line. By default in javascript, template literals call the .toString() method, and since we are using the map() method, its toString() places a comma between the results around it. To fix this, we need to call .join(''), telling the map to join everything together without a comma before the template literal makes it call the .toString() method.
That's it for the confusing parts! Lastly, we'll head back to sapper world and return the result as xml.
3) Returning the result
The rest is pretty easy. We just have to make sure to set the header to 'Content-Type', 'application/xml'. You're probably used to returning 'application/json', but here that would be an easy mistake to make. Last but not least we simply call the res.end() function, passing in the result of our render() function, and we're all set!
Hopefully this saved you some time if you're coming across this issue. Figuring this out was a frustrating process as someone new to server-side rendering, and I'm honestly surprised that there's not an easier way to do this (or sapper doesn't take care of it for you). Please post in the comments if you have any questions, I'll be around!
If you want the full code snippet, again it can be found here:
https://www.app.codernotes.io/notes/creating-site-map-in-sapper-142
Top comments (0)