The Service Orders Management System communicates with a backend API using Axios. This page explains how API integration works, including authentication, error handling, and common patterns.
API Setup
The application uses the useApi() composable to create configured Axios instances for API calls.
Axios Configuration
import axios from 'axios'
export const useApi = () => {
const config = useRuntimeConfig ()
const token = import . meta . client ? localStorage . getItem ( 'access_token' ) : null
const { public : { apiBaseUrl } } = useRuntimeConfig ()
const api = axios . create ({
baseURL: apiBaseUrl as string ,
headers: {
'Accept' : 'application/json' ,
'Content-Type' : 'application/json' ,
... ( token ? { Authorization: `Bearer ${ token } ` } : {})
}
})
return api
}
Key Configuration Elements
The API base URL is configured in nuxt.config.ts using runtime config: export default defineNuxtConfig ({
runtimeConfig: {
public: {
apiBaseUrl: 'http://localhost:8000/api/'
}
} ,
})
Benefits :
Environment-specific configuration
Can be overridden with environment variables
Centralized API endpoint management
Usage :const { public : { apiBaseUrl } } = useRuntimeConfig ()
The token is retrieved from localStorage on the client: const token = import . meta . client ? localStorage . getItem ( 'access_token' ) : null
Why the client check?
Prevents errors during server-side rendering (SSR)
localStorage is only available in browsers
Returns null on server, ensuring SSR compatibility
Authentication Flow
Login Request
Here’s how the login process works in pages/index.vue:
< script setup >
import { useRouter } from 'vue-router' ;
import { ref } from 'vue' ;
import { useAuthStore } from '~/stores/auth' ;
const form = ref ({
email: '' ,
password: ''
})
const api = useApi ()
const checkLogin = async () => {
validateForm ()
if ( ! formFlag . value ) {
return
}
await api . post ( '/login' , form . value ). then (
( response ) => {
// Store the access token
localStorage . setItem ( 'access_token' , response . data . access_token )
// Redirect to service orders page
window . location . href = '/serviceorders'
}). catch (
( error ) => {
// Handle login errors
if ( error . response . data . message ) {
alert ( `Validacion Incorrecta: ${ error . response . data . message } ` )
}
else {
alert ( `Validacion Incorrecta` )
}
form . value . email = ""
form . value . password = ""
})
}
</ script >
Flow :
User submits email and password
POST request to /login endpoint
On success: Store token in localStorage and redirect
On error: Display error message and clear form
Authenticated Requests
Once logged in, all subsequent requests automatically include the Bearer token:
const api = useApi ()
// This request includes: Authorization: Bearer <token>
const response = await api . get ( '/service-orders' )
Logout Request
layouts/default.vue (logout)
< script setup >
const api = useApi ()
const logout = async () => {
// Call logout endpoint
await api . post ( '/logout' )
// Clear local token
localStorage . removeItem ( 'access_token' )
// Redirect to login
window . location . href = '/'
}
</ script >
Common API Patterns
Fetching Data
GET Request
GET with Parameters
GET with Query Params
const api = useApi ()
const orders = ref ([])
const getServiceOrders = async () => {
try {
const response = await api . get ( '/service-orders' )
orders . value = response . data
} catch ( error ) {
console . error ( 'Failed to fetch orders:' , error )
}
}
onMounted ( async () => {
await getServiceOrders ()
})
Creating Data
POST Request
Login Example
const api = useApi ()
const createOrder = async ( orderData : ServiceOrder ) => {
try {
const response = await api . post ( '/service-orders' , orderData )
return response . data
} catch ( error ) {
console . error ( 'Failed to create order:' , error )
throw error
}
}
Updating Data
const api = useApi ()
const updateOrder = async ( id : number , orderData : ServiceOrder ) => {
try {
const response = await api . put ( `/service-orders/ ${ id } ` , orderData )
return response . data
} catch ( error ) {
console . error ( 'Failed to update order:' , error )
throw error
}
}
Deleting Data
const api = useApi ()
const deleteOrder = async ( id : number ) => {
try {
const response = await api . delete ( `/service-orders/ ${ id } ` )
return response . data
} catch ( error ) {
console . error ( 'Failed to delete order:' , error )
throw error
}
}
Error Handling Patterns
Basic Error Handling
const api = useApi ()
const fetchData = async () => {
try {
const response = await api . get ( '/service-orders' )
return response . data
} catch ( error ) {
console . error ( 'Error:' , error )
// Handle error appropriately
alert ( 'Failed to fetch data' )
}
}
Detailed Error Handling
const api = useApi ()
const login = async ( credentials ) => {
try {
const response = await api . post ( '/login' , credentials )
return response . data
} catch ( error ) {
// Check if error has response from server
if ( error . response ) {
// Server responded with error
const message = error . response . data . message || 'Login failed'
alert ( `Validation Error: ${ message } ` )
} else if ( error . request ) {
// Request made but no response received
alert ( 'No response from server. Please check your connection.' )
} else {
// Error setting up request
alert ( 'An error occurred. Please try again.' )
}
throw error
}
}
Error Handling with State
< template >
< div >
< div v-if = " loading " > Loading... </ div >
< div v-else-if = " error " > Error: {{ error }} </ div >
< div v-else >
<!-- Display data -->
</ div >
</ div >
</ template >
< script setup >
const api = useApi ()
const orders = ref ([])
const loading = ref ( false )
const error = ref ( null )
const fetchOrders = async () => {
loading . value = true
error . value = null
try {
const response = await api . get ( '/service-orders' )
orders . value = response . data
} catch ( err ) {
error . value = err . message
} finally {
loading . value = false
}
}
onMounted (() => {
fetchOrders ()
})
</ script >
Response Structure
Successful Response
// API Response
{
data : [
{
id: 1 ,
number: 123456 ,
type: "garantía" ,
company: { name: "Tech Solutions" },
// ... other fields
}
]
}
// Accessing data
const response = await api . get ( '/service-orders' )
const orders = response . data // Array of orders
Error Response
// API Error Response
{
response : {
status : 401 ,
data : {
message : "Invalid credentials"
}
}
}
// Handling error
try {
await api . post ( '/login' , credentials )
} catch ( error ) {
console . log ( error . response . status ) // 401
console . log ( error . response . data . message ) // "Invalid credentials"
}
TypeScript Integration
Define interfaces for API responses:
interface ApiResponse < T > {
data : T ;
}
const getServiceOrders = async () : Promise < ApiResponse < ServiceOrder []>> => {
const response = await api . get ( '/service-orders' )
return response
}
Best Practices
Always Handle Errors Use try-catch blocks and provide meaningful error messages to users
Loading States Show loading indicators during API calls for better UX
Reuse API Instance Always use useApi() instead of creating new Axios instances
Type Your Responses Use TypeScript interfaces for API responses to ensure type safety
Common Endpoints
Based on the codebase, here are the API endpoints in use:
Method Endpoint Description Auth Required POST /loginAuthenticate user No POST /logoutEnd user session Yes GET /service-ordersGet all service orders Yes GET /service-orders/:idGet specific order Yes POST /service-ordersCreate new order Yes PUT /service-orders/:idUpdate entire order Yes PATCH /service-orders/:idPartially update order Yes DELETE /service-orders/:idDelete order Yes
All endpoints (except /login) require a valid Bearer token in the Authorization header.
Next Steps
Composables Learn more about the useApi composable
State Management Understand how authentication state is managed