Vue APIs
Fetch remote data and represent loading, success, and error states.
Overview
APIs allow a Vue application to communicate with a server. The app can request products, users, weather, or other live information and store the response in reactive state so the interface updates automatically.
Key concepts
- Fetch data from lifecycle hooks or user actions
- Check the HTTP response before reading data
- Represent loading, success, empty, and error states
- Keep reusable request logic organized
What is an API?
An API (Application Programming Interface) is a defined way for software applications to communicate. A Vue application sends a request, a server processes it, and the server returns a response—often as JSON—which Vue can display.
Think of an API as a restaurant waiter: the customer places an order, the waiter carries it to the kitchen, and then returns with the result. The customer does not need to know how the kitchen works internally.
Why APIs are important
An online shop should not hard-code every product inside its Vue components. Loading products from an API allows prices, availability, and product details to remain current without rebuilding the frontend.
- Load current information from a database
- Send user-created data to a server
- Connect to services such as payments, maps, or weather
- Keep frontend presentation separate from backend data
Fetching JSON with fetch
The browser's Fetch API returns a Promise. Always check response.ok because fetch does not reject automatically for HTTP errors such as 404 or 500.
async function getProducts() {
const response = await fetch("/api/products");
if (!response.ok) {
throw new Error("Request failed: " + response.status);
}
return response.json();
}Loading data in a Vue component
Use onMounted when data should load as the component enters the page. Reactive refs hold the results and the request status.
<script setup>
import { onMounted, ref } from "vue";
const products = ref([]);
const error = ref("");
const loading = ref(true);
async function loadProducts() {
loading.value = true;
error.value = "";
try {
const response = await fetch("/api/products");
if (!response.ok) {
throw new Error("Request failed: " + response.status);
}
products.value = await response.json();
} catch (reason) {
error.value = reason instanceof Error
? reason.message
: "Something went wrong";
} finally {
loading.value = false;
}
}
onMounted(loadProducts);
</script>
<template>
<p v-if="loading">Loading products...</p>
<div v-else-if="error">
<p role="alert">{{ error }}</p>
<button @click="loadProducts">Try again</button>
</div>
<p v-else-if="products.length === 0">No products found.</p>
<ul v-else>
<li v-for="product in products" :key="product.id">
{{ product.name }} — {{ product.price }}
</li>
</ul>
</template>Understanding the request states
- Loading: the request is still running
- Success: data was received and can be displayed
- Empty: the request succeeded but returned no records
- Error: the request failed and the user can retry
Representing each state prevents blank screens and gives users clear feedback about what is happening.
Sending data to an API
Use HTTP methods to describe the operation: GET reads, POST creates, PUT or PATCH updates, and DELETE removes data. When sending JSON, set the content type and serialize the body.
async function createProduct(product) {
const response = await fetch("/api/products", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(product)
});
if (!response.ok) {
throw new Error("Unable to create product");
}
return response.json();
}Fetching after user actions
Not every request belongs in onMounted. A weather search, form submission, pagination control, or refresh button should call the API in response to the relevant user action.
Organizing API code
As an application grows, move repeated request logic into a service module or composable. Components can then focus on displaying data and responding to users instead of repeating URLs and error handling.
Best practices
- Fetch data only when it is needed
- Check response.ok before parsing the response
- Show loading, empty, and error feedback
- Use try, catch, and finally for predictable cleanup
- Disable duplicate submissions while a request is running
- Keep reusable API logic outside presentation components
- Cancel or ignore obsolete requests when searches change quickly
- Keep secret API keys on the server, not in frontend code
Common beginner mistakes
- Forgetting to call response.json()
- Assuming every request succeeds
- Treating a 404 or 500 response as success
- Misspelling an endpoint URL
- Fetching repeatedly without a reason
- Leaving stale errors visible during a retry
- Using an array index instead of a stable ID for v-for keys
- Exposing private credentials in the browser
Key takeaways
- APIs connect Vue applications to server data and services
- fetch sends HTTP requests and returns a Promise
- API responses commonly contain JSON
- Vue stores received data in reactive state
- onMounted is useful for initial page data
- User actions can trigger later requests
- Reliable interfaces handle loading, empty, error, and success states
- Reusable request code belongs in services or composables