How to Build a No‑Exception Safe Request Wrapper for Vue/Axios
Learn to replace repetitive try‑catch blocks with a clean, reusable safe request utility that wraps Axios, returns structured [error, data] tuples, supports automatic error messages, and integrates TypeScript for stronger typing, streamlining error handling across your Vue front‑end code.
Developers often write repetitive try‑catch blocks for every API call, which makes the code verbose, scatters error handling, and leads to bloated files.
💡 Goal: "Safe request wrapper without throwing exceptions"
Desired usage:
const [err, data] = await safeRequest(api.getUser(1))
if (err) return showError(err)
console.log('✅ User info:', data)This eliminates try‑catch while still providing both error and data.
🧩 Implementation Steps
1️⃣ Wrap an Axios instance
import axios from 'axios'
import { ElMessage } from 'element-plus'
const service = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
})
// request interceptor
service.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
},
(error) => Promise.reject(error)
)
// response interceptor
service.interceptors.response.use(
(response) => {
const res = response.data
if (res.code !== 0) {
ElMessage.error(res.message || 'Request failed')
return Promise.reject(new Error(res.message || 'Request failed'))
}
return res.data
},
(error) => {
ElMessage.error(error.message || 'Network error')
return Promise.reject(error)
}
)
export default service2️⃣ Create the safe request function
export async function safeRequest(promise) {
try {
const data = await promise
return [null, data] // ✅ success
} catch (err) {
return [err, null] // ❌ failure
}
}This function turns any Promise into a gentle result tuple instead of throwing.
3️⃣ Build an API module
import request from '@/utils/request'
export const userApi = {
getUser(id) {
return request.get(`/user/${id}`)
},
updateUser(data) {
return request.put('/user', data)
}
}4️⃣ Call it elegantly in the business layer
import { ref, onMounted } from 'vue'
import { userApi } from '@/api/user'
import { safeRequest } from '@/utils/safeRequest'
const user = ref(null)
onMounted(async () => {
const [err, data] = await safeRequest(userApi.getUser(1))
if (err) return showError(err)
console.log('✅ User info:', data)
})The result is concise, data‑centric, and free of try‑catch.
🧱 Further Optimization: Automatic Error Prompt
Add an optional showError flag to safeRequest so errors can be displayed automatically when desired.
import { ElMessage } from 'element-plus'
export async function safeRequest(promise, { showError = true } = {}) {
try {
const data = await promise
return [null, data]
} catch (err) {
if (showError) {
ElMessage.error(err.message || 'Request failed')
}
return [err, null]
}
}Usage with the flag disabled:
const [err, data] = await safeRequest(userApi.getUser(1), { showError: false })🧠 Advanced: TypeScript Support
For TypeScript projects, make the helper generic for better type inference.
export async function safeRequest<T>(promise: Promise<T>): Promise<[Error | null, T | null]> {
try {
const data = await promise
return [null, data]
} catch (err) {
return [err as Error, null]
}
}Calling it:
const [err, user] = await safeRequest<User>(userApi.getUser(1))
if (user) console.log(user.name) // ✅ type‑safeWith this pattern, error handling becomes uniform, UI code stays clean, and developers gain strong typing support.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
