Fetching data in a Next.js app is an essential part of building any web application. Axios is a popular library for making HTTP requests in JavaScript. It can be easily integrated into a Next.js app to handle the fetching of data from an API, and in this article I’ll talk you through that process.

Set-up

To get started, install Axios using:

npm install --save axios

Once Axios is installed, we can import it and use it in any component that needs to fetch data.

Example

Let’s use Axios to get some information from the free Pokeapi. You can check that out here.

First, let’s build our actual fetch function:

const fetchPokemon = async (pokemon: string) => {
    const res = await axios.get<SinglePokemonResponse>(
        'https://pokeapi.co/api/v2/pokemon/' + pokemon
    );
    return {
        name: res.data.name,
        types: res.data.types,
        sprite: res.data.sprites.front_default,
    };
};

We’ll integrate this into our app later. The axios object contains functions for every HTTP method, “POST, GET, PUT, PATCH, and DELETE”. In our case the Pokeapi only supports GET, so that’s all we need to use. This is a really straightforward get call, we get back a JSON response containing our pokemon data, which I’m then transforming into a bit of a nicer shape.

Integrating

Now let’s do something with this fetch call:

export default function Axios() {
    const [pokemon, setPokemon] = useState<Pokemon | null>();

    useEffect(() => {
        fetchPokemon('bulbasaur')
            .then((p) => setPokemon(p))
            .catch((e: Error | AxiosError) => console.log(e));
    }, []);
    if (!pokemon) return null;
    return (
        <div className="flex h-screen w-full items-center justify-center bg-stone-900 text-white">
            <PokemonCard {...pokemon} />
        </div>
    );
}

The code is straightforward, we perform our fetch and store the result in state. You might be wondering why we’re using state here instead of just a normal variable. This is because our fetch is asynchronous, we don’t have the result straight away. We can’t make the actual component asynchronous, Next.js won’t let us.

The right way to do it is to perform the fetch inside our useEffect so it runs on the first call, and then when we get the pokemon, we can store it in state, causing our component to be re-rendered and updated with the pokemon data.

The side effect of this is that we won’t have the pokemon on our first render, so we need to account for that by checking if our pokemon state variable is null.

Handling States

The previous method works fine, but we can be a little smarter and handle some different states we might run into. Firstly, our call is asynchronous, so it would improve UX a lot to give the user some indicator that something is happening.

We can really easily do this by tracking this in state:

export default function Axios() {
    const [state, setState] = useState<'loading' | 'done' | null>(null);
    const [pokemon, setPokemon] = useState<Pokemon | null>();

Then in our useEffect, we change the state to loading when we start our call, and change it to done afterwards.

    useEffect(() => {
        setState('loading');

        fetchPokemon('bulbasaur')
            .then((p) => setPokemon(p))
            .then(() => setState('done'))
            .catch((e: Error | AxiosError) => console.log(e));
    }, []);

And then we can add in some conditional rendering:

    if (!pokemon || state === 'loading')
        return (
            <div className="flex h-screen w-full items-center justify-center bg-stone-900 text-white">
                <Loading />
            </div>
        );

And here’s what we get:

The other case that can occur that we haven’t handled is if the call completely fails. This is very easy to add in:

export default function Axios() {
    const [state, setState] = useState<'loading' | 'error' | 'done' | null>(
        null
    );
    const [pokemon, setPokemon] = useState<Pokemon | null>();

    useEffect(() => {
        setState('loading');
        fetchPokemon('bulbasaur')
            .then((p) => setPokemon(p))
            .then(() => setState('done'))
            .catch((e: Error | AxiosError) => setState('error'));
    }, []);
    if (!pokemon || state === 'loading')
        return (
            <div className="flex h-screen w-full items-center justify-center bg-stone-900 text-white">
                <Loading />
            </div>
        );
    if (state === 'error')
        return (
            <div className="flex h-screen w-full items-center justify-center bg-stone-900 text-white">
                <Error />
            </div>
        );
    return (
        <div className="flex h-screen w-full items-center justify-center bg-stone-900 text-white">
            <PokemonCard {...pokemon} />
        </div>
    );
}

I’ve added in an extra state for an error, and if this occurs we just get a simple error message. Now here’s what happens if we get an error:

Conclusion

Thanks for reading this article, I hope I’ve helped you out. Using Axios in a Next.js app is a great way to handle data fetching. It’s easy to set up, and it provides a simple API for making HTTP requests. If you’re looking for more material, check out our article on Server Side Rendering and Static Site Generation to help optimise your Next.js app. Feel free to leave a comment below if you liked this article, or if you need any help.

Avatar photo
👋 Hey, I'm Omari Thompson-Edwards
Hey, I'm Omari! I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, reach out on Twitter at @marile0n

💬 Leave a comment

Your email address will not be published. Required fields are marked *

We will never share your email with anyone else.