API FAQ
Overview
This FAQ covers common questions and issues related to uni-app APIs, including platform-specific behaviors, best practices, and troubleshooting tips.
General API Questions
Q: What's the difference between uni.request and native fetch/XMLHttpRequest?
A: uni.request is uni-app's unified network request API that works across all platforms:
// uni.request - Recommended for uni-app
uni.request({
url: 'https://api.example.com/data',
method: 'GET',
data: {
id: 123
},
success: (res) => {
console.log(res.data)
},
fail: (err) => {
console.error(err)
}
})
// Promise-based approach
const response = await uni.request({
url: 'https://api.example.com/data',
method: 'POST',
data: {
name: 'John',
email: 'john@example.com'
}
})
Benefits of uni.request:
- Cross-platform compatibility
- Automatic request/response interceptors
- Built-in error handling
- Platform-specific optimizations
Q: How do I handle API responses consistently across platforms?
A: Use a unified response handler:
// utils/api.js
export const apiRequest = (options) => {
return new Promise((resolve, reject) => {
uni.request({
...options,
success: (res) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data)
} else {
reject(new Error(`HTTP ${res.statusCode}: ${res.data.message || 'Request failed'}`))
}
},
fail: (err) => {
reject(new Error(`Network error: ${err.errMsg}`))
}
})
})
}
// Usage
try {
const data = await apiRequest({
url: '/api/users',
method: 'GET'
})
console.log(data)
} catch (error) {
console.error('API Error:', error.message)
}
Q: How do I implement request interceptors?
A: Create a request wrapper with interceptor functionality:
// utils/request.js
class RequestInterceptor {
constructor() {
this.requestInterceptors = []
this.responseInterceptors = []
}
// Add request interceptor
addRequestInterceptor(interceptor) {
this.requestInterceptors.push(interceptor)
}
// Add response interceptor
addResponseInterceptor(interceptor) {
this.responseInterceptors.push(interceptor)
}
// Make request with interceptors
async request(config) {
// Apply request interceptors
let finalConfig = config
for (const interceptor of this.requestInterceptors) {
finalConfig = await interceptor(finalConfig)
}
try {
const response = await uni.request(finalConfig)
// Apply response interceptors
let finalResponse = response
for (const interceptor of this.responseInterceptors) {
finalResponse = await interceptor(finalResponse)
}
return finalResponse
} catch (error) {
throw error
}
}
}
// Create instance
const request = new RequestInterceptor()
// Add token to all requests
request.addRequestInterceptor((config) => {
const token = uni.getStorageSync('token')
if (token) {
config.header = {
...config.header,
'Authorization': `Bearer ${token}`
}
}
return config
})
// Handle response errors globally
request.addResponseInterceptor((response) => {
if (response.statusCode === 401) {
// Redirect to login
uni.navigateTo({
url: '/pages/login/login'
})
}
return response
})
Storage APIs
Q: What's the difference between sync and async storage methods?
A: Synchronous methods block execution, while asynchronous methods don't:
// Synchronous - blocks execution
try {
uni.setStorageSync('key', 'value')
const value = uni.getStorageSync('key')
console.log(value) // 'value'
} catch (error) {
console.error('Storage error:', error)
}
// Asynchronous - non-blocking
uni.setStorage({
key: 'key',
data: 'value',
success: () => {
uni.getStorage({
key: 'key',
success: (res) => {
console.log(res.data) // 'value'
}
})
}
})
// Promise-based async approach
try {
await uni.setStorage({
key: 'key',
data: 'value'
})
const res = await uni.getStorage({
key: 'key'
})
console.log(res.data) // 'value'
} catch (error) {
console.error('Storage error:', error)
}
When to use each:
- Use sync methods for simple, quick operations
- Use async methods for large data or when you need non-blocking behavior
Q: How do I handle storage quota limits?
A: Implement storage management with quota checking:
// utils/storage.js
export const StorageManager = {
// Check available storage space
async getStorageInfo() {
try {
const info = await uni.getStorageInfo()
return {
keys: info.keys,
currentSize: info.currentSize,
limitSize: info.limitSize,
usage: (info.currentSize / info.limitSize * 100).toFixed(2) + '%'
}
} catch (error) {
console.error('Failed to get storage info:', error)
return null
}
},
// Clean up old data
async cleanupStorage(maxAge = 7 * 24 * 60 * 60 * 1000) { // 7 days
try {
const info = await uni.getStorageInfo()
const now = Date.now()
for (const key of info.keys) {
if (key.startsWith('cache_')) {
const data = uni.getStorageSync(key)
if (data && data.timestamp && (now - data.timestamp) > maxAge) {
uni.removeStorageSync(key)
}
}
}
} catch (error) {
console.error('Storage cleanup failed:', error)
}
},
// Set data with expiration
setWithExpiration(key, data, expirationMs) {
const item = {
data,
timestamp: Date.now(),
expiration: expirationMs
}
try {
uni.setStorageSync(key, item)
} catch (error) {
if (error.errMsg.includes('exceed max storage size')) {
// Clean up and retry
this.cleanupStorage()
uni.setStorageSync(key, item)
}
}
},
// Get data with expiration check
getWithExpiration(key) {
try {
const item = uni.getStorageSync(key)
if (!item) return null
const now = Date.now()
if (item.expiration && (now - item.timestamp) > item.expiration) {
uni.removeStorageSync(key)
return null
}
return item.data
} catch (error) {
console.error('Failed to get storage item:', error)
return null
}
}
}
Navigation APIs
Q: What's the difference between navigateTo, redirectTo, and reLaunch?
A: Each navigation method serves different purposes:
// navigateTo - Add to navigation stack (can go back)
uni.navigateTo({
url: '/pages/detail/detail?id=123'
})
// redirectTo - Replace current page (cannot go back)
uni.redirectTo({
url: '/pages/result/result'
})
// reLaunch - Clear stack and navigate to new page
uni.reLaunch({
url: '/pages/home/home'
})
// switchTab - Navigate to tab page
uni.switchTab({
url: '/pages/profile/profile'
})
// navigateBack - Go back in navigation stack
uni.navigateBack({
delta: 1 // Number of pages to go back
})
Use cases:
navigateTo
: Normal page navigationredirectTo
: Login success, form submission resultsreLaunch
: App restart, major state changesswitchTab
: Tab bar navigationnavigateBack
: Return to previous page
Q: How do I pass complex data between pages?
A: Several approaches for passing data:
// Method 1: URL parameters (for simple data)
uni.navigateTo({
url: `/pages/detail/detail?id=${id}&name=${encodeURIComponent(name)}`
})
// Method 2: Global storage (for complex data)
// Page A
uni.setStorageSync('pageData', {
user: userObject,
settings: settingsObject
})
uni.navigateTo({
url: '/pages/detail/detail'
})
// Page B
const pageData = uni.getStorageSync('pageData')
// Method 3: Event bus (for real-time updates)
// utils/eventBus.js
export const EventBus = {
events: {},
on(event, callback) {
if (!this.events[event]) {
this.events[event] = []
}
this.events[event].push(callback)
},
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(callback => callback(data))
}
},
off(event, callback) {
if (this.events[event]) {
const index = this.events[event].indexOf(callback)
if (index > -1) {
this.events[event].splice(index, 1)
}
}
}
}
// Usage
import { EventBus } from '@/utils/eventBus'
// Page A
EventBus.emit('dataUpdate', complexData)
// Page B
EventBus.on('dataUpdate', (data) => {
console.log('Received data:', data)
})
File System APIs
Q: How do I handle file uploads with progress tracking?
A: Implement file upload with progress monitoring:
// utils/fileUpload.js
export const FileUploader = {
// Upload single file with progress
uploadFile(filePath, options = {}) {
return new Promise((resolve, reject) => {
const uploadTask = uni.uploadFile({
url: options.url || '/api/upload',
filePath: filePath,
name: options.name || 'file',
formData: options.formData || {},
header: options.header || {},
success: (res) => {
try {
const data = JSON.parse(res.data)
resolve(data)
} catch (error) {
resolve(res.data)
}
},
fail: reject
})
// Track upload progress
uploadTask.onProgressUpdate((progress) => {
const percent = Math.round(progress.progress)
if (options.onProgress) {
options.onProgress(percent, progress)
}
})
// Allow cancellation
if (options.onTaskCreated) {
options.onTaskCreated(uploadTask)
}
})
},
// Upload multiple files
async uploadMultipleFiles(filePaths, options = {}) {
const results = []
const errors = []
for (let i = 0; i < filePaths.length; i++) {
try {
const result = await this.uploadFile(filePaths[i], {
...options,
onProgress: (percent, progress) => {
if (options.onProgress) {
options.onProgress(i, percent, progress)
}
}
})
results.push(result)
} catch (error) {
errors.push({ index: i, error })
}
}
return { results, errors }
},
// Compress image before upload
async compressAndUpload(imagePath, options = {}) {
try {
// Compress image
const compressedPath = await uni.compressImage({
src: imagePath,
quality: options.quality || 80,
width: options.width,
height: options.height
})
// Upload compressed image
return await this.uploadFile(compressedPath.tempFilePath, options)
} catch (error) {
console.error('Compress and upload failed:', error)
throw error
}
}
}
// Usage example
import { FileUploader } from '@/utils/fileUpload'
// In component
methods: {
async handleFileUpload() {
try {
// Choose image
const chooseResult = await uni.chooseImage({
count: 1,
sizeType: ['compressed']
})
const filePath = chooseResult.tempFilePaths[0]
// Upload with progress
const result = await FileUploader.compressAndUpload(filePath, {
url: '/api/upload/image',
quality: 70,
onProgress: (percent) => {
this.uploadProgress = percent
}
})
console.log('Upload successful:', result)
} catch (error) {
console.error('Upload failed:', error)
uni.showToast({
title: 'Upload failed',
icon: 'none'
})
}
}
}
Platform-Specific API Issues
Q: How do I handle platform differences in APIs?
A: Use conditional compilation and platform detection:
// Platform detection
const platform = uni.getSystemInfoSync().platform
// Conditional compilation
// #ifdef MP-WEIXIN
// WeChat Mini Program specific code
wx.getLocation({
type: 'gcj02',
success: (res) => {
console.log('WeChat location:', res)
}
})
// #endif
// #ifdef APP-PLUS
// App specific code
plus.geolocation.getCurrentPosition((position) => {
console.log('App location:', position)
})
// #endif
// #ifdef H5
// Web specific code
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition((position) => {
console.log('Web location:', position)
})
}
// #endif
// Universal approach with fallbacks
export const LocationService = {
async getCurrentLocation() {
return new Promise((resolve, reject) => {
uni.getLocation({
type: 'gcj02',
success: (res) => {
resolve({
latitude: res.latitude,
longitude: res.longitude,
accuracy: res.accuracy
})
},
fail: (error) => {
// Platform-specific fallbacks
// #ifdef APP-PLUS
this.getAppLocation().then(resolve).catch(reject)
// #endif
// #ifdef H5
this.getWebLocation().then(resolve).catch(reject)
// #endif
// #ifndef APP-PLUS || H5
reject(error)
// #endif
}
})
})
},
// #ifdef APP-PLUS
getAppLocation() {
return new Promise((resolve, reject) => {
plus.geolocation.getCurrentPosition(
(position) => {
resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy
})
},
reject
)
})
},
// #endif
// #ifdef H5
getWebLocation() {
return new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error('Geolocation not supported'))
return
}
navigator.geolocation.getCurrentPosition(
(position) => {
resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
accuracy: position.coords.accuracy
})
},
reject
)
})
}
// #endif
}
Q: How do I handle WeChat Mini Program specific APIs?
A: Use WeChat-specific APIs with proper error handling:
// WeChat Mini Program specific features
export const WeChatFeatures = {
// WeChat login
async wechatLogin() {
// #ifdef MP-WEIXIN
try {
const loginResult = await wx.login()
const userInfo = await wx.getUserInfo()
// Send to your server for authentication
const authResult = await uni.request({
url: '/api/auth/wechat',
method: 'POST',
data: {
code: loginResult.code,
userInfo: userInfo
}
})
return authResult.data
} catch (error) {
console.error('WeChat login failed:', error)
throw error
}
// #endif
// #ifndef MP-WEIXIN
throw new Error('WeChat login only available in WeChat Mini Program')
// #endif
},
// WeChat payment
async wechatPay(paymentData) {
// #ifdef MP-WEIXIN
return new Promise((resolve, reject) => {
wx.requestPayment({
...paymentData,
success: resolve,
fail: reject
})
})
// #endif
// #ifndef MP-WEIXIN
throw new Error('WeChat payment only available in WeChat Mini Program')
// #endif
},
// Share to WeChat
shareToWeChat(shareData) {
// #ifdef MP-WEIXIN
wx.shareAppMessage({
title: shareData.title,
desc: shareData.description,
path: shareData.path,
imageUrl: shareData.imageUrl
})
// #endif
}
}
Performance and Optimization
Q: How do I optimize API calls for better performance?
A: Implement caching, batching, and request optimization:
// API optimization utilities
export const APIOptimizer = {
cache: new Map(),
pendingRequests: new Map(),
// Request with caching
async cachedRequest(url, options = {}) {
const cacheKey = `${url}_${JSON.stringify(options)}`
const cacheTime = options.cacheTime || 5 * 60 * 1000 // 5 minutes
// Check cache
const cached = this.cache.get(cacheKey)
if (cached && (Date.now() - cached.timestamp) < cacheTime) {
return cached.data
}
// Check if request is already pending
if (this.pendingRequests.has(cacheKey)) {
return this.pendingRequests.get(cacheKey)
}
// Make request
const requestPromise = uni.request({
url,
...options
}).then(response => {
// Cache response
this.cache.set(cacheKey, {
data: response.data,
timestamp: Date.now()
})
// Remove from pending
this.pendingRequests.delete(cacheKey)
return response.data
}).catch(error => {
// Remove from pending on error
this.pendingRequests.delete(cacheKey)
throw error
})
// Store pending request
this.pendingRequests.set(cacheKey, requestPromise)
return requestPromise
},
// Batch multiple requests
async batchRequests(requests) {
try {
const results = await Promise.allSettled(
requests.map(request => uni.request(request))
)
return results.map((result, index) => ({
success: result.status === 'fulfilled',
data: result.status === 'fulfilled' ? result.value.data : null,
error: result.status === 'rejected' ? result.reason : null,
originalRequest: requests[index]
}))
} catch (error) {
console.error('Batch requests failed:', error)
throw error
}
},
// Debounced request (for search, etc.)
debouncedRequest: (() => {
const timeouts = new Map()
return (key, requestFn, delay = 300) => {
return new Promise((resolve, reject) => {
// Clear existing timeout
if (timeouts.has(key)) {
clearTimeout(timeouts.get(key))
}
// Set new timeout
const timeoutId = setTimeout(async () => {
try {
const result = await requestFn()
resolve(result)
} catch (error) {
reject(error)
} finally {
timeouts.delete(key)
}
}, delay)
timeouts.set(key, timeoutId)
})
}
})(),
// Clear cache
clearCache(pattern) {
if (pattern) {
for (const key of this.cache.keys()) {
if (key.includes(pattern)) {
this.cache.delete(key)
}
}
} else {
this.cache.clear()
}
}
}
Error Handling
Q: How do I implement proper error handling for APIs?
A: Create a comprehensive error handling system:
// Error handling utilities
export const ErrorHandler = {
// Error types
ERROR_TYPES: {
NETWORK: 'NETWORK_ERROR',
TIMEOUT: 'TIMEOUT_ERROR',
SERVER: 'SERVER_ERROR',
CLIENT: 'CLIENT_ERROR',
UNKNOWN: 'UNKNOWN_ERROR'
},
// Parse error from response
parseError(error) {
if (!error) {
return {
type: this.ERROR_TYPES.UNKNOWN,
message: 'Unknown error occurred',
code: 'UNKNOWN'
}
}
// Network errors
if (error.errMsg && error.errMsg.includes('network')) {
return {
type: this.ERROR_TYPES.NETWORK,
message: 'Network connection failed',
code: 'NETWORK_FAILED',
original: error
}
}
// Timeout errors
if (error.errMsg && error.errMsg.includes('timeout')) {
return {
type: this.ERROR_TYPES.TIMEOUT,
message: 'Request timeout',
code: 'REQUEST_TIMEOUT',
original: error
}
}
// HTTP status errors
if (error.statusCode) {
const statusCode = error.statusCode
if (statusCode >= 400 && statusCode < 500) {
return {
type: this.ERROR_TYPES.CLIENT,
message: error.data?.message || `Client error (${statusCode})`,
code: `HTTP_${statusCode}`,
statusCode,
original: error
}
}
if (statusCode >= 500) {
return {
type: this.ERROR_TYPES.SERVER,
message: error.data?.message || `Server error (${statusCode})`,
code: `HTTP_${statusCode}`,
statusCode,
original: error
}
}
}
return {
type: this.ERROR_TYPES.UNKNOWN,
message: error.message || 'Unknown error occurred',
code: 'UNKNOWN',
original: error
}
},
// Handle error with user feedback
handleError(error, options = {}) {
const parsedError = this.parseError(error)
// Log error
console.error('API Error:', parsedError)
// Show user feedback
if (!options.silent) {
let message = parsedError.message
// Customize message based on error type
switch (parsedError.type) {
case this.ERROR_TYPES.NETWORK:
message = 'Network connection failed. Please check your internet connection.'
break
case this.ERROR_TYPES.TIMEOUT:
message = 'Request timeout. Please try again.'
break
case this.ERROR_TYPES.SERVER:
message = 'Server error. Please try again later.'
break
}
uni.showToast({
title: message,
icon: 'none',
duration: 3000
})
}
// Custom error handler
if (options.onError) {
options.onError(parsedError)
}
return parsedError
},
// Retry mechanism
async retryRequest(requestFn, maxRetries = 3, delay = 1000) {
let lastError
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await requestFn()
} catch (error) {
lastError = error
const parsedError = this.parseError(error)
// Don't retry client errors
if (parsedError.type === this.ERROR_TYPES.CLIENT) {
throw error
}
// Wait before retry (exponential backoff)
if (attempt < maxRetries) {
await new Promise(resolve =>
setTimeout(resolve, delay * Math.pow(2, attempt - 1))
)
}
}
}
throw lastError
}
}
// Usage example
try {
const data = await ErrorHandler.retryRequest(
() => uni.request({
url: '/api/data',
method: 'GET'
}),
3, // max retries
1000 // initial delay
)
console.log('Success:', data)
} catch (error) {
ErrorHandler.handleError(error, {
onError: (parsedError) => {
// Custom error handling
if (parsedError.code === 'HTTP_401') {
// Redirect to login
uni.navigateTo({
url: '/pages/login/login'
})
}
}
})
}
Best Practices
- Always handle errors gracefully
- Use appropriate API methods for each platform
- Implement caching for frequently accessed data
- Use request interceptors for common functionality
- Optimize network requests with batching and debouncing
- Provide meaningful user feedback for API operations
- Test APIs across all target platforms
- Implement proper retry mechanisms for network failures
Summary
Understanding uni-app APIs and their platform-specific behaviors is crucial for building robust cross-platform applications. By following these best practices and implementing proper error handling, caching, and optimization strategies, you can create reliable and performant applications that work seamlessly across all supported platforms.