Making API Requests With Axios

Legacy Content Notice: This post from 2017 demonstrates Axios usage with Vue.js and WordPress REST API using older patterns. Modern development includes native Fetch API, better error handling, TypeScript support, and improved async patterns. For comprehensive modern JavaScript development practices, see our Modern JavaScript Development Guide.

To make requests to the WordPress REST API we need to install another library to help us make these requests. We can use something like jQuery which has helpers for the different methods of GET, POST, PATCH, DELETE, but it may seem like overkill to import the entire jQuery library just to use the Ajax helpers. There are other libraries we can use which just provide helpers for HTTP requests such as vue-resource or axios. VueJS recommends Axios so that's the library we're going to go with, to install Axios we can do so by using NPM.

npm install axios

To use Axios inside our components we need to first import in by using the following command.

import axios from "axios";

Then we can use the GET request by simply using the .get() method with the URL we need to call. Inside the .then method is what we'll do on a successful request, inside the catch method will run if the HTTP request failed for whatever reason.

axios
    .get("/user?ID=12345")
    .then(function (response) {
        console.log(response);
    })
    .catch(function (error) {
        console.log(error);
    });

For more information about how to use Axios please view the following link. Axios To make an API call within our WordPress theme we need to setup the API for different environments then we're going to use this environment settings when making an HTTP request call to the WordPress API. Inside the config folder of our project, there are files for the different environments, dev.env.js, prod.env.js and test.env.js. Inside these files, you need to add a new element for the API_URL. My dev.env.js will now look like this

var merge = require("webpack-merge");
var prodEnv = require("./prod.env");

module.exports = merge(prodEnv, {
    NODE_ENV: '"development"',
    API_URL: '"https://paulund.dev"',
});

And the prod.env.js will look like this

module.exports = {
    NODE_ENV: '"production"',
    API_URL: '"http://paulund.co.uk"',
    DEBUG: false,
};

When we run the command

npm run dev

This will load in the dev.env.js file and use these variables for the environment. This means that inside our view components we can't hardcode the domain of the REST API we need to make it dynamic. To make a GET request for all posts on the WordPress REST API we'll now need to use the following code.

axios
    .get(process.env.API_URL + "/wp-json/wp/v2/posts")
    .then((response) => {
        this.posts = response.data;
    })
    .catch((e) => {
        console.log(e);
    });

This will now pick up the API_URL variable from the correct environment and use this base domain when making API requests.

Modern Approaches (2024)

Method 1: Modern Axios with TypeScript

// api/wordpress.ts
import axios, { AxiosInstance, AxiosResponse } from "axios";

interface WordPressPost {
    id: number;
    title: { rendered: string };
    content: { rendered: string };
    excerpt: { rendered: string };
    date: string;
    slug: string;
}

interface ApiResponse<T> {
    data: T;
    status: number;
    message?: string;
}

class WordPressAPI {
    private client: AxiosInstance;

    constructor(baseURL: string) {
        this.client = axios.create({
            baseURL: `${baseURL}/wp-json/wp/v2`,
            timeout: 10000,
            headers: {
                "Content-Type": "application/json",
            },
        });

        // Add interceptors
        this.setupInterceptors();
    }

    private setupInterceptors() {
        // Request interceptor
        this.client.interceptors.request.use(
            (config) => {
                console.log(
                    `Making ${config.method?.toUpperCase()} request to ${
                        config.url
                    }`
                );
                return config;
            },
            (error) => Promise.reject(error)
        );

        // Response interceptor
        this.client.interceptors.response.use(
            (response) => response,
            (error) => {
                console.error(
                    "API Error:",
                    error.response?.data || error.message
                );
                return Promise.reject(error);
            }
        );
    }

    async getPosts(params?: {
        per_page?: number;
        page?: number;
    }): Promise<WordPressPost[]> {
        try {
            const response: AxiosResponse<WordPressPost[]> =
                await this.client.get("/posts", { params });
            return response.data;
        } catch (error) {
            throw new Error(`Failed to fetch posts: ${error}`);
        }
    }

    async getPost(id: number): Promise<WordPressPost> {
        try {
            const response: AxiosResponse<WordPressPost> =
                await this.client.get(`/posts/${id}`);
            return response.data;
        } catch (error) {
            throw new Error(`Failed to fetch post ${id}: ${error}`);
        }
    }

    async createPost(post: Partial<WordPressPost>): Promise<WordPressPost> {
        try {
            const response: AxiosResponse<WordPressPost> =
                await this.client.post("/posts", post);
            return response.data;
        } catch (error) {
            throw new Error(`Failed to create post: ${error}`);
        }
    }
}

// Usage in Vue 3 Composition API
import { ref, onMounted } from "vue";

export default {
    setup() {
        const posts = ref<WordPressPost[]>([]);
        const loading = ref(false);
        const error = ref<string | null>(null);

        const api = new WordPressAPI(import.meta.env.VITE_API_URL);

        const fetchPosts = async () => {
            loading.value = true;
            error.value = null;

            try {
                posts.value = await api.getPosts({ per_page: 10 });
            } catch (err) {
                error.value =
                    err instanceof Error
                        ? err.message
                        : "Failed to fetch posts";
            } finally {
                loading.value = false;
            }
        };

        onMounted(fetchPosts);

        return {
            posts,
            loading,
            error,
            fetchPosts,
        };
    },
};

Method 2: Native Fetch API Alternative

// api/fetch-client.ts
class FetchWordPressAPI {
    private baseURL: string;

    constructor(baseURL: string) {
        this.baseURL = `${baseURL}/wp-json/wp/v2`;
    }

    private async request<T>(
        endpoint: string,
        options?: RequestInit
    ): Promise<T> {
        const url = `${this.baseURL}${endpoint}`;

        const response = await fetch(url, {
            headers: {
                "Content-Type": "application/json",
                ...options?.headers,
            },
            ...options,
        });

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        return response.json();
    }

    async getPosts(params?: {
        per_page?: number;
        page?: number;
    }): Promise<WordPressPost[]> {
        const searchParams = new URLSearchParams();
        if (params) {
            Object.entries(params).forEach(([key, value]) => {
                searchParams.append(key, value.toString());
            });
        }

        const endpoint = `/posts${
            searchParams.toString() ? `?${searchParams}` : ""
        }`;
        return this.request<WordPressPost[]>(endpoint);
    }

    async getPost(id: number): Promise<WordPressPost> {
        return this.request<WordPressPost>(`/posts/${id}`);
    }

    async createPost(post: Partial<WordPressPost>): Promise<WordPressPost> {
        return this.request<WordPressPost>("/posts", {
            method: "POST",
            body: JSON.stringify(post),
        });
    }
}

Method 3: Vue 3 Composable with Error Handling

// composables/useWordPressAPI.ts
import { ref, computed } from 'vue';

export function useWordPressAPI() {
  const posts = ref<WordPressPost[]>([]);
  const loading = ref(false);
  const error = ref<string | null>(null);

  const api = new WordPressAPI(import.meta.env.VITE_API_URL);

  const hasError = computed(() => !!error.value);
  const isEmpty = computed(() => posts.value.length === 0);

  const fetchPosts = async (params?: { per_page?: number; page?: number }) => {
    loading.value = true;
    error.value = null;

    try {
      posts.value = await api.getPosts(params);
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Unknown error occurred';
      posts.value = [];
    } finally {
      loading.value = false;
    }
  };

  const refreshPosts = () => fetchPosts();

  return {
    posts: readonly(posts),
    loading: readonly(loading),
    error: readonly(error),
    hasError,
    isEmpty,
    fetchPosts,
    refreshPosts,
  };
}

// Usage in component
<script setup lang="ts">
import { onMounted } from 'vue';
import { useWordPressAPI } from '@/composables/useWordPressAPI';

const { posts, loading, error, hasError, fetchPosts } = useWordPressAPI();

onMounted(() => {
  fetchPosts({ per_page: 5 });
});
</script>

<template>
  <div>
    <div v-if="loading">Loading posts...</div>
    <div v-else-if="hasError" class="error">{{ error }}</div>
    <div v-else>
      <article v-for="post in posts" :key="post.id">
        <h2 v-html="post.title.rendered"></h2>
        <div v-html="post.excerpt.rendered"></div>
      </article>
    </div>
  </div>
</template>

Environment Configuration (Modern)

Vite (Vue 3):

// .env.development
VITE_API_URL=https://paulund.dev

// .env.production
VITE_API_URL=https://paulund.co.uk

// vite.config.js
export default {
  define: {
    __API_URL__: JSON.stringify(process.env.VITE_API_URL)
  }
}

Webpack 5:

// webpack.config.js
const webpack = require("webpack");

module.exports = {
    plugins: [
        new webpack.DefinePlugin({
            "process.env.API_URL": JSON.stringify(process.env.API_URL),
        }),
    ],
};

Key Modern Improvements

  • TypeScript Support: Better type safety and developer experience
  • Composables: Reusable logic with Vue 3 Composition API
  • Error Boundaries: Better error handling and user experience
  • Native Fetch: Alternative to Axios for smaller bundles
  • Environment Variables: Modern build tool integration