Vue.js HTTP Requests

Most real applications fetch data from a server — user profiles, product lists, news articles. Vue does not have a built-in HTTP client, but it works seamlessly with the browser's native Fetch API and the popular Axios library.

Where to Make HTTP Requests in Vue

HTTP requests are side effects. The right place to trigger them depends on your scenario:

  • On component load — use the mounted() lifecycle hook (Options API) or onMounted() (Composition API)
  • When data changes — use a watcher
  • When a user acts — call from a method triggered by a click or form submit

Diagram: When to Fetch

Page loads → mounted() → fetch initial data
User types in search box → watcher → fetch search results
User clicks "Save" → method → POST request
User clicks page 2 → method → fetch page 2 data

Using the Fetch API

The Fetch API is built into every modern browser. No installation needed. It returns Promises.

GET Request — Loading Data

<div id="app">
  <p v-if="loading">Loading...</p>
  <p v-if="error">{{ error }}</p>
  <ul v-if="!loading">
    <li v-for="user in users" :key="user.id">
      {{ user.name }} — {{ user.email }}
    </li>
  </ul>
</div>

<script>
  Vue.createApp({
    data() {
      return {
        users: [],
        loading: false,
        error: null
      };
    },
    mounted() {
      this.fetchUsers();
    },
    methods: {
      async fetchUsers() {
        this.loading = true;
        this.error   = null;
        try {
          const response = await fetch("https://jsonplaceholder.typicode.com/users");
          if (!response.ok) throw new Error("Server error: " + response.status);
          this.users = await response.json();
        } catch (err) {
          this.error = err.message;
        } finally {
          this.loading = false;
        }
      }
    }
  }).mount("#app");
</script>

Diagram: GET Request Flow

Component mounts
        │
        ▼
fetchUsers() called
loading = true → "Loading..." shown on page
        │
        ▼
fetch("https://...") sends HTTP GET
        │
  Success │  Failure
        │         └──▶ error = "Server error" → error message shown
        ▼
response.json() parses the data
users = [ { id:1, name:"Alice" }, ... ]
loading = false
        │
        ▼
Vue renders the user list on screen

POST Request — Sending Data

methods: {
  async createPost(title, body) {
    try {
      const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ title, body, userId: 1 })
      });
      const newPost = await response.json();
      console.log("Created:", newPost);
    } catch (err) {
      console.error("Failed to create post:", err);
    }
  }
}

Installing and Using Axios

Axios is a popular HTTP library that simplifies requests with cleaner syntax, automatic JSON parsing, and better error handling.

npm install axios

Axios vs Fetch — Key Differences

┌─────────────────────────┬─────────────────┬─────────────────┐
│ Feature                 │ Fetch           │ Axios           │
├─────────────────────────┼─────────────────┼─────────────────┤
│ Built-in browser        │ Yes             │ No (install)    │
│ Auto JSON parse         │ No (manual)     │ Yes             │
│ HTTP error detection    │ No (manual)     │ Yes (auto)      │
│ Request cancellation    │ Manual          │ Built-in        │
│ Interceptors            │ No              │ Yes             │
│ Shorter syntax          │ Moderate        │ Shorter         │
└─────────────────────────┴─────────────────┴─────────────────┘

Axios GET Request

<script setup>
import { ref, onMounted } from "vue";
import axios from "axios";

const posts   = ref([]);
const loading = ref(false);
const error   = ref(null);

async function fetchPosts() {
  loading.value = true;
  error.value   = null;
  try {
    const response = await axios.get("https://jsonplaceholder.typicode.com/posts?_limit=5");
    posts.value = response.data;   // Axios parses JSON automatically
  } catch (err) {
    error.value = err.message;
  } finally {
    loading.value = false;
  }
}

onMounted(fetchPosts);
</script>

<template>
  <p v-if="loading">Fetching posts...</p>
  <p v-if="error" style="color:red">{{ error }}</p>
  <article v-for="post in posts" :key="post.id">
    <h3>{{ post.title }}</h3>
    <p>{{ post.body }}</p>
  </article>
</template>

Axios POST, PUT, DELETE

import axios from "axios";

const BASE = "https://jsonplaceholder.typicode.com";

// POST — create a new resource
async function createItem(data) {
  const response = await axios.post(BASE + "/posts", data);
  return response.data;
}

// PUT — replace a resource completely
async function updateItem(id, data) {
  const response = await axios.put(BASE + "/posts/" + id, data);
  return response.data;
}

// PATCH — update specific fields
async function patchItem(id, fields) {
  const response = await axios.patch(BASE + "/posts/" + id, fields);
  return response.data;
}

// DELETE — remove a resource
async function deleteItem(id) {
  await axios.delete(BASE + "/posts/" + id);
}

Diagram: HTTP Methods and Their Purpose

GET    /posts       → Read all posts
GET    /posts/1     → Read post with id 1
POST   /posts       → Create a new post
PUT    /posts/1     → Replace post 1 entirely
PATCH  /posts/1     → Update specific fields of post 1
DELETE /posts/1     → Delete post 1

Axios Instance — Reusable Base Configuration

Create an Axios instance with a base URL and default headers. Every request made through the instance automatically includes those settings.

// src/api/http.js
import axios from "axios";

const http = axios.create({
  baseURL: "https://api.yoursite.com/v1",
  timeout: 10000,
  headers: {
    "Content-Type": "application/json"
  }
});

// Add auth token to every request automatically
http.interceptors.request.use((config) => {
  const token = localStorage.getItem("authToken");
  if (token) config.headers.Authorization = "Bearer " + token;
  return config;
});

export default http;
// In any component — no need to repeat base URL or headers
import http from "../api/http";

const users = await http.get("/users");   // calls api.yoursite.com/v1/users
const user  = await http.post("/users", { name: "Alice" });

Loading and Error States Pattern

Every fetch operation should track three states: loading, success data, and error. This gives the user feedback at every stage.

Diagram: Three-State Request Pattern

┌─────────────────────────────────────────────────┐
│  Request has not started yet                    │
│  loading = false, data = [], error = null       │
└─────────────────────────────────────────────────┘
                       │ fetch starts
                       ▼
┌─────────────────────────────────────────────────┐
│  Request in progress                            │
│  loading = true  → show spinner or "Loading..." │
└─────────────────────────────────────────────────┘
                       │
          Success ─────┤───── Failure
             │                   │
             ▼                   ▼
┌─────────────────┐    ┌─────────────────────────┐
│ loading = false │    │ loading = false         │
│ data = [...]    │    │ error = "Network error" │
│ Show the data   │    │ Show error message      │
└─────────────────┘    └─────────────────────────┘

Composable for Data Fetching

Extract fetch logic into a reusable composable so any component can use it without duplicating code.

// src/composables/useFetch.js
import { ref } from "vue";

export function useFetch(url) {
  const data    = ref(null);
  const loading = ref(false);
  const error   = ref(null);

  async function fetchData() {
    loading.value = true;
    error.value   = null;
    try {
      const res = await fetch(url);
      if (!res.ok) throw new Error("HTTP error " + res.status);
      data.value = await res.json();
    } catch (err) {
      error.value = err.message;
    } finally {
      loading.value = false;
    }
  }

  fetchData();

  return { data, loading, error, refetch: fetchData };
}
<!-- Any component -->
<script setup>
import { useFetch } from "../composables/useFetch";

const { data: users, loading, error } = useFetch(
  "https://jsonplaceholder.typicode.com/users"
);
</script>

<template>
  <p v-if="loading">Loading...</p>
  <p v-if="error">{{ error }}</p>
  <ul v-if="users">
    <li v-for="user in users" :key="user.id">{{ user.name }}</li>
  </ul>
</template>

Summary

Make HTTP requests in Vue using the browser's Fetch API or the Axios library. Trigger requests in mounted() for on-load data, in watchers for reactive data changes, or in methods for user-triggered actions. Always track loading, data, and error states to give users clear feedback. Create an Axios instance with base URL and interceptors to avoid repeating configuration across components. Extract fetch logic into a composable to reuse it across the application without duplicating code.

Leave a Comment

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