Fetching Data
Before we do anything too complicated, lets start with some static content pulled from our GraphQL API.
Create two files inside of your src/routes
directory, +page.gql
and +page.svelte
:
query Info {
species(id: 1) {
id
name
flavor_text
sprites {
front
}
}
}
<script lang="ts">
import { Container, Display, Sprite, Panel } from '~/components'
import type { PageData } from './$houdini'
export let data: PageData
$: ({ Info } = data)
</script>
<Container>
<Panel slot="left">
<Display id="species-name">
{$Info.data.species.name}
<span>no.{$Info.data.species.id}</span>
</Display>
<Sprite
id="species-sprite"
src={$Info.data.species.sprites.front}
speciesName={$Info.data.species.name}
/>
<Display id="species-flavor_text">
{$Info.data.species.flavor_text}
</Display>
</Panel>
</Container>
<script>
import { Container, Display, Sprite, Panel } from '~/components'
/* @type { import('./$houdini').PageData } */
export let data
$: ({ Info } = data)
</script>
<Container>
<Panel slot="left">
<Display id="species-name">
{$Info.data.species.name}
<span>no.{$Info.data.species.id}</span>
</Display>
<Sprite
id="species-sprite"
src={$Info.data.species.sprites.front}
speciesName={$Info.data.species.name}
/>
<Display id="species-flavor_text">
{$Info.data.species.flavor_text}
</Display>
</Panel>
</Container>
You’re already starting to see some of the very exciting things Houdini offers. Just like you might define a route’s load
function in a standard
SvelteKit application, you can use +page.gql
to define the query for your route.
The data for the query is passed as a store that’s available in a key that matches its name (in this case its Info
). We used the store to render some basic information about Bulbasaur using some components that
were provided in the project’s component
directory.
If everything is set up properly, you should see a message printed in your terminal once you save the file. Behind the scenes, Houdini is constantly validating and processing your queries so you can catch errors as quickly as possible.
Anyway, now that you have the necessary files, you should see Bulbasaur’s description. If you are still running into issues, please reach out to us on the svelte discord and we’d be happy to help.
Query Variables
This is a good start but we will need to be able to show information for more species than just Bulbasaur.
Let’s set up our application to take look at the url for the id of the species we are interested in.
To do that, add a directory named [[id]]
and move both +page.gql
and +page.svelte
inside of it:
# This needs to be run at the root of the project
cd src/routes && mkdir "[[id]]" && mv +page.* "./[[id]]"
The double braces might seem strange but that will let us have an optional parameter in the url so we can
render the same view for both /
and /1
.
Now that we have the actual route defined, we will have to change our query so that it can accept
a variable. Doing this is relatively simple, just update the query inside of +page.gql
to look like the following:
query Info($id: Int! = 1) {
species(id: $id) {
name
flavor_text
sprites {
front
}
}
}
And that’s it! Notice that the route parameter matched the name of the query input? Houdini detected that and took care of the all of the wiring for you. Pretty cool, huh? There is also a way to perform custom logic to compute your query inputs when you need it but we’ll keep things simple for now. For more information, check out the query api docs.
You should be able to navigate to /6
and see Charizard’s information. If you then navigate back to /
,
there is no value for the [[id]]
portion of the url and the query uses its default value of 1
.
For completeness, let’s quickly add some buttons to navigate between the different species. Copy and paste this block of code as the last child of the Container
component. Don’t worry if you see an error when you click on them, we’ll fix that next.
<Panel slot="right">
<nav>
<a href={$Info.data.species.id - 1} disabled={$Info.data.species.id <= 1}>
previous
</a>
<a href={$Info.data.species.id + 1} disabled={$Info.data.species.id >= 151}>
next
</a>
</nav>
</Panel>
Loading State
If you’ve already clicked on those links you probably saw a scary message about accessing a value on null
. This is because we haven’t done anything
to handle the loading state for our view. Let’s just do something quick and dirty:
{#if $Info.fetching}
<Container/>
{:else}
<!-- what we had before -->
{/if}
Error Handling
So far so good! There is one slight problem: there are only 151 species in the first generation of Pokémon. The buttons we added in the last section prevent the user from going beyond those bounds, but if we navigate to /152
directly we will get an error since $Info.data.species
is null. Go ahead, give it a try.
In order to prevent this, we need to check if id
is between 1
and 151
and if not, render an error for the user. The best way to do this is to use a load hook to check the value before the load fires. Load hooks belong in +page.js
files so create a
file at src/routes/[[id]]/+page.js
that looks like the following:
import { error } from '@sveltejs/kit'
import type { BeforeLoadEvent } from './$houdini'
export function _houdini_beforeLoad({ params }: BeforeLoadEvent) {
// if we were given an id, convert the string to a number
const id = params.id ? parseInt(params.id) : 1
// check that the id falls between 1 and 151
if (id < 1 || id > 151) {
// return a status code 400 along with the error
throw error(400, 'id must be between 1 and 151')
}
}
import { error } from '@sveltejs/kit'
/**
* @param { import('./$houdini').BeforeLoadEvent }
*/
export function _houdini_beforeLoad({ params }) {
// if we were given an id, convert the string to a number
const id = params.id ? parseInt(params.id) : 1
// check that the id falls between 1 and 151
if (id < 1 || id > 151) {
// return a status code 400 along with the error
throw error(400, 'id must be between 1 and 151')
}
}
Load hooks in houdini all begin with _houdini_
and there are a lot more than just beforeLoad
. For an overview of what hooks you can
define, check out the query api docs.
What’s Next?
Now that you’ve seen the basics of fetching data from the server, we’re going to start to dig a little deeper into how we should be organizing our queries. In the next section we’re going to give our Svelte components the power to define their own data requirements so we don’t have to worry about their concerns when building this route’s query.