Fetching Data

Before we do anything too complicated, lets look at what we are starting with:

<!-- src/routes/+page.svelte -->

<script>
    import { graphql } from '$houdini'
    import { Container, Display, Sprite, Panel } from '~/components'

    const info = graphql`
        query SpeciesInfo {
            species(id: 1) {
                name
                flavor_text
                sprites {
                    front
                }
            }
        }
    `
</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>

In this example, you can see we are taking a graphql query named SpeciesInfo and rendering the result using some components from the project’s components directory. There is a lot of stuff going on under the hood here but let’s not worry about it for now - what matters most is that we defined a named query with graphql and got back a store that we are using to access the result of our query.

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, rename +page.svelte to [...id]/+page.svelte. That will let us have an optional parameter in the url which we will interpret as the ID (ie, / and /1 will both render Bulbasaur’s page).

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 string inside of the graphql tag to look like the following:

query SpeciesInfo($id: Int!) {
	species(id: $id) {
		name
		flavor_text
		sprites {
			front
		}
	}
}

In order to tell the runtime how to compute that value, you have to define a function called SpeciesInfoVariables in your route’s +page.js files that returns the correct value. Create src/routes/[...id]/+page.js and paste the following content:

// src/routes/[...id]/+page.js

export function SpeciesInfoVariables({ params }) {
	return {
		id: params.id ? parseInt(params.id) : 1
	}
}

Once that’s done, you should be able to navigate to /6 to see Charizard’s information.

Note: the name of the function that returns the values for a query’s variables must follow a very specific format. It must be called {QueryName}Variables. If you look at the query we are working with, you’ll see its named SpeciesInfo which is why this function is named SpeciesInfoVariables.

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. The reason for 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.isFetching}
	<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. This is accomplished by updating SpeciesInfoVariables to look like:

export function SpeciesInfoVariables({ 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 this.error(400, 'id must be between 1 and 151')
	}

	return {
		id
	}
}

We could also take the opportunity to make sure that the user didn’t try to navigate to a nested route like /1/foo but we’ll leave that as an exercise for you.

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.