Data loading in SvelteKit
With the recent changes to SvelteKit which has invalidated a lot of content already out there, I want to make some notes on how to load data in SvelteKit.
First up, if you werenβt aware, SvelteKit completely changed how file based routing is done. Thereβs a really comprehensive migration guide from Rich Harris and Simon H over on GitHub on it.
Page routing
If you donβt want the background on the route changes then the TL;DR, is here: Get set up.
Iβll quickly go over the new routing system, as itβs a bit different
to what it was, what Iβll do is detail what the SvelteKit skeleton
(created with the SvelteKit CLI command npm init svelte or pnpm create svelte) file structure looked like before and after.
Iβve removed a some of the other files that come with the skeleton for brevity.
Before
sveltekit-skeleton-example/
ββ src/
β ββ routes/
β β ββ index.svelte
β ββ app.html
ββ package.json And this is how itβs now structured.
After
sveltekit-skeleton-example/
ββ src/
β ββ routes/
β β ββ +page.svelte
β ββ app.html
ββ package.json Not a missive change, right? But letβs take a look at how routes were
done vs how they are now. Letβs say there was several other routes about, blog, contact etc.
Before
sveltekit-skeleton-example/
ββ src/
β ββ routes/
β β ββ about.svelte
β β ββ blog.svelte
β β ββ contact.svelte
β β ββ index.svelte
β ββ app.html
ββ package.json This could also be done with a directory, which was my preferred way of grouping routes.
sveltekit-skeleton-example/
ββ src/
β ββ routes/
β β ββ about/
β β β ββ index.svelte
β β ββ blog/
β β β ββ index.svelte
β β ββ contact/
β β β ββ index.svelte
β β ββ index.svelte
β ββ app.html
ββ package.json After
sveltekit-skeleton-example/
ββ src/
β ββ routes/
β β ββ about/
β β β ββ +page.svelte
β β ββ blog/
β β β ββ +page.svelte
β β ββ contact/
β β β ββ +page.svelte
β β ββ +page.svelte
β ββ app.html
ββ package.json As always, people get mad when something changes. I was fine with the changes when they came along and had load of fun refactoring all my old projects to use the new system when it dropped.
Using +page.js|.ts
Before a +page.svelte file is loaded there may be some data needed
for the page, like a list of products, or a list of blog posts. This
is where data loading comes in.
A +page.svelte file can have a sibling +page.js file that runs a load function.
/** @type {import('./$types').PageLoad} */
export const load = async () => {
return {
greeting: 'Hello world!',
}
} The return of the value (greeting) form the load function is
accessible to the +page.svelte file as a data prop. In Svelte
props are received into components with export let.
<script>
/** @type {import('./$types').PageData} */
export let data
</script>
<p>{data.greeting}</p> Ok, so that data defined locally now Iβll take a look at getting some external data.
In the following example Iβm using the CoinLore API to get the top 100 cryptocurrencies (which is the default request). Itβs a fun API to play around with but for this example all I really need is some data from an external source.
SvelteKit builds on top of the Standard Web APIs and makes them
available in the load function. You can see in the example here Iβm
destructuring out the fetch function from the context object that
is available to the load function.
/** @type {import('./$types').PageLoad} */
export const load = async ({ fetch }) => {
const res = await fetch('https://api.coinlore.com/api/tickers/')
const { data } = await res.json()
return {
currencies: data,
}
} Iβm labelling the return value as currencies this is then shown in
the data being passed to +page.svelte.
<script>
/** @type {import('./$types').PageData} */
export let data
</script>
<pre>{JSON.stringify(data, null, 2)}</pre> Resulting data being displayed on the page is something like this:
{
"currencies": [
{
"id": "90",
"symbol": "BTC",
"name": "Bitcoin",
"nameid": "bitcoin"
},
{
"id": "80",
"symbol": "ETH",
"name": "Ethereum",
"nameid": "ethereum"
}
]
} Two or more endpoints?
I detailed this before in a post on how to Fetch data from two or
more endpoints in SvelteKit with Promise.all.
If you donβt like the way you have to put the calls into an array then adding them to their own variables is also fine.
/** @type {import('./$types').PageLoad} */
export const load = async ({ fetch }) => {
const fetchCoins = async () => {
const res = await fetch('https://api.coinlore.com/api/tickers/')
const { data } = await res.json()
return data
}
const fetchCharacters = async () => {
const res = await fetch('https://rickandmortyapi.com/graphql/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: `
query AllCharacters {
characters {
results {
name
id
image
}
}
}
`,
})
const {
data: {
characters: { results },
},
} = await res.json()
return results
}
return {
currencies: fetchCoins(),
characters: fetchCharacters(),
}
} Iβve added an additional call to the page load function to get the characters from the Rick and Morty GraphQL API then calling the function for each of them.
Using +page.server.js|.ts
Say if you have an API key that you donβt want to expose on the client
(the browser) then you can use a +page.server.js file.
In here I can make a call to an API and pass in a secret key that I
have defined in a .env file.
import { SECRET_TOKEN } from '$env/static/private'
/** @type {import('./$types').PageServerLoad} */
export const load = async ({ fetch }) => {
// this is only logged out on the server
console.log('=====================')
console.log(SECRET_TOKEN)
console.log('=====================')
const fetchCoins = async () => {
const res = await fetch('https://api.coinlore.com/api/tickers/')
const { data } = await res.json()
return data
}
return {
currencies: fetchCoins(),
}
} The CoinLore API doesnβt require a key so Iβm just using a dummy token
defined in my .env file.
You can find out more about that in the SvelteKit Environment Variables with the SvelteKit $env Module post I did a while back.
Conclusion
In summary, use +page.js or +page.ts to load data for a page where
thereβs no need for authentication, or if youβre not concerned about
any credentials being exposed on the client. If you need to load data
from an API that requires authentication where you want to keep
credentials from the client then use +page.server.js or +page.server.ts.
Thatβs it for this post, I hope you found it useful.
There's a reactions leaderboard you can check out too.
Sign up for the newsletter
Want to keep up to date with what I'm working on?
Join other developers and sign up for the newsletter.
I care about the protection of your data. Read the Privacy Policy for more info.