Dynamic API Requests with Nuxt 3

Part 6 of 9 in Upgrading Nuxt 2 to Nuxt 3
Dan Pastori avatar
Dan Pastori April 18th, 2022

After your user interacts with your page, it can be necessary to refresh your data. A few examples of when to refresh data is when you need to filter API resources or paginate data.

With Nuxt 3, these dynamic API requests can be structured in a reactive manner. When the query parameters change, the API request is automatically called allowing you to simply re-render the data. In Nuxt 2, this approach was done by wrapping the $axios plugin and calling a method to refresh the data. In Nuxt 3, we can compute a query string and when it changes, the new data will be fetched. Let’s get started!

Gettings Started

Let’s say we are using our /api/v1/cafes endpoint in ROAST. This endpoint allows you to search, paginate, and filter by brew methods through an API request. On our search page, we can set these filters and reload the data without reloading the entire page. This is the scenario we are going to step through in Nuxt 2 vs Nuxt 3 because it’s slightly different.

Step 1: Dynamic API Requests in Nuxt 2

Let’s take a look at a quick example of how to make a dynamic API request with Nuxt 2:

data: {
    return {
        search: '',
        brew_methods: [],
        page: 1,
        cafes: []

methods: {
    async searchCafes( page = 1 ) {
        let params = buildSearchCafes( page );

        const cafes = await this.$axios.$get('https://api.roastandbrew.coffee/api/v1/cafes', {
            params: params
        this.cafes = cafes

    buildSearchCafes( page ){
        let searchParams = {};

        // Pagination
        searchParams.page = page;
        searchParams.take = 12;

        if( this.search != '' ){
            searchParams.search = encodeURIComponent( this.search );

        if( this.brew_methods.length > 0 ){
            searchParams.brew_methods = encodeURIComponent( this.brew_methods.join(',') );

        return searchParams;

There are 3 sections to this example.

Defining Data

First, we have the data. The data contains all of the different variables the user can use to load what they need from the API endpoint. In reality, there are a lot more, but for this example, we have a search string, brew_methods array, page for pagination, and cafes array used to display the data.

Building the Query Parameters

Next, we have the buildSearchCafes( page ) method. This method, simply takes what the user has selected, converts the selections to a JSON object, and returns them. We use these query parameters in our request to our API endpoint in the next step.

Making our Dynamic API Request

Finally, we make our API request. In Nuxt 2, you had a few options. I always used the $axios module since it was easy to customize. You could use fetch or the $http plugin as well. Whatever way you chose to do it, you needed to make an API request with the query parameters. Once you got the data back from the API, you could set it to your local cafes data array. Vue would then reactively display the updates.

When the user clicked a pagination button such as previous, a number, or next, we’d re-request the data with what the user selected. We’d also call the searchCafes() method if the user searches or filters the results. This process allows the user to refresh the data without leaving the page. In Nuxt 3, the process is similar, but a little more reactive.

Step 2: Dynamic API Requests in Nuxt 3

When we build these dynamic API requests in Nuxt 3, we will be focusing solely on watching for changes to our filter variables. This will trigger our watched query string, which will in-turn update the data. Let’s take a look:

const search = useState('search', '');
const brewMethods = useState('brewMethods', []);
const page = useState('page', 1);

const queryString = computed(() => {
    let values = '?page='+page.value+'&take=12';

    values += search.value != '' ? '&search='+search.value : '';
    values += brewMethods.value.length > 0 ? '&brew_methods='+brewMethods.value.join(',') : '';

    return values;

const { data:cafes, pending, refresh } = await useLazyAsyncData(
    () => $fetch( `https://api.roastandbrew.coffee/cafes${queryString.value}`)

// When query string changes, refresh
watch(() => queryString.value, () => refresh() );

Before we break this functionality down and the differences, note that you should use this in a <script setup> tag or setup() method with Vue 3. Now this looks entirely different, but the functionality is the same. Instead of calling a method that refreshes our API, we watch for changes on a dynamic query string.

Defining the State

First, we define the state that we need within our component. We have a search string, brewMethods array, and page that keeps track of the pagination. We didn’t include our cafes here, because we set that variable up with asyncData which we will discuss soon.

Building our Query String

Next, we have our computed queryString. This variable reacts and updates when one of the dependent variables changes. If you break down the method, whenever the page, search or brewMethods gets updated, we dynamically build the query string and return it. This is extremely powerful and you will see why shortly.

Making our Dynamic API Request

To define our cafes variable which is used to render the page, we use the useLazyAsyncData() composable. This composable allows us to decouple the data:cafes, pending, and refresh method. All are extremely important. The data:cafes defines the variable we use to load our cafes from our API request. To show whether the request has completed or not, we can use the pending variable. It will be set to true or false depending on whether the request is loading or not. Finally, we have a refresh() method. This will re-send our API request if we need to refresh the data stored in the cafes variable. This is where the magic happens!

Watching for Changes

The last line of our code ties everything together:

watch(() => queryString.value, () => refresh() );

What this little snippet does is watch the value of our query string. When the query string changes after the user adjusts a search parameter, pagination page, or filter, we call the refresh() method. Since we append the queryString to our API request, when we refresh after a change, the new query string will be sent to the API. This is how we update our data!

So there’s actually another way to do this using Watch Sources. Feel free to skip ahead to Advanced Data Fetching with Nuxt 3 to learn more.


Both Nuxt 2 and Nuxt 3 allow you to easily build dynamic api requests into your application. Even though the structure and syntax is different, the functionality is the same. When migrating from Nuxt 2 to Nuxt 3, these changes may be cumbersome but as you finish your migration it all ties together. You get the speed and updated functionality of Nuxt 3 along with maintaining the existing features in your app.

If you want to see a more cohesive approach to how this works, we do have a book! In the book, we go through multiple API requests, some dynamic, some more static and how they play into the app as a whole.

If there are any questions, feel free to reach out on Twitter (@danpastori) or on our community forum!

Keep Reading
View the course View the Course Upgrading Nuxt 2 to Nuxt 3
Up Next → Importing and Using Components in Nuxt 3

Support future content

The Ultimate Guide to Building APIs and Single-Page Applications with Laravel + VueJS + Capacitor book cover.

Psst... any earnings that we make off of our book is being reinvested to bringing you more content. If you like what you read, consider getting our book or get sweet perks by becoming a sponsor.

Written By Dan

Dan Pastori avatar Dan Pastori

Builder, creator, and maker. Dan Pastori is a Laravel certified developer with over 10 years experience in full stack development. When you aren't finding Dan exploring new techniques in programming, catch him at the beach or hiking in the National Parks.

Like this? Subscribe

We're privacy advocates. We will never spam you and we only want to send you emails that you actually want to receive. One-click unsubscribes are instantly honored.