Tôi đã triển khai hệ thống API cho hơn 50 dự án AI trong 3 năm qua, và điều tôi học được là: quản lý rate limiting không phải là tùy chọn — mà là yếu tố sống còn để giữ hệ thống ổn định khi người dùng tăng đột biến. Bài viết này sẽ hướng dẫn bạn xây dựng một middleware hoàn chỉnh, đồng thời so sánh chi phí thực tế giữa các nhà cung cấp.
Bảng So Sánh Chi Phí và Hiệu Suất
| Nhà cung cấp | GPT-4.1 ($/MTok) | Claude Sonnet 4.5 ($/MTok) | Độ trễ trung bình | Hỗ trợ thanh toán | Rate Limit mặc định |
|---|---|---|---|---|---|
| HolySheep AI | $8.00 | $15.00 | <50ms | WeChat, Alipay, USDT | Theo gói đăng ký |
| API Chính thức (OpenAI/Anthropic) | $15.00 | $27.00 | 200-500ms | Thẻ quốc tế | Rất thấp ở gói Free |
| Relay Service A | $12.00 | $22.00 | 150-300ms | Thẻ quốc tế | Trung bình |
| Relay Service B | $10.00 | $20.00 | 180-400ms | PayPal | Hạn chế |
Với tỷ giá ¥1 = $1, HolySheep AI giúp bạn tiết kiệm 85%+ chi phí so với API chính thức — đặc biệt quan trọng khi xử lý hàng triệu token mỗi ngày.
Tại Sao Cần Rate Limiting Theo Subscription Tier?
Trong thực chiến, tôi đã gặp nhiều trường hợp:
- Khách hàng gói Free chiếm 80% tài nguyên nhưng chỉ đóng góp 5% doanh thu
- AI chatbot bị quá tải khi cả nghìn người dùng gọi API cùng lúc
- Chi phí phát sinh không kiểm soát được vì thiếu giới hạn sử dụng
- Bị provider chặn IP do exceed quota mà không hay biết
Xây Dựng Middleware Rate Limiting
1. Cấu Trúc Dữ Liệu và Constants
// config/subscriptionTiers.js
// Định nghĩa các gói đăng ký và giới hạn tương ứng
const SUBSCRIPTION_TIERS = {
free: {
name: 'Free Tier',
requestsPerMinute: 10,
requestsPerDay: 100,
tokensPerMonth: 100000,
concurrentRequests: 1,
modelRestrictions: ['gpt-3.5-turbo', 'claude-3-haiku'],
pricePerMonth: 0,
},
starter: {
name: 'Starter',
requestsPerMinute: 60,
requestsPerDay: 5000,
tokensPerMonth: 1000000,
concurrentRequests: 5,
modelRestrictions: ['gpt-3.5-turbo', 'gpt-4o-mini', 'claude-3-haiku', 'claude-3-sonnet'],
pricePerMonth: 29,
},
pro: {
name: 'Professional',
requestsPerMinute: 300,
requestsPerDay: 50000,
tokensPerMonth: 10000000,
concurrentRequests: 20,
modelRestrictions: null, // Tất cả model
pricePerMonth: 99,
},
enterprise: {
name: 'Enterprise',
requestsPerMinute: 1000,
requestsPerDay: -1, // Không giới hạn
tokensPerMonth: -1, // Không giới hạn
concurrentRequests: 100,
modelRestrictions: null,
pricePerMonth: 499,
},
};
// Map user ID -> subscription tier
const userSubscriptions = new Map();
module.exports = { SUBSCRIPTION_TIERS, userSubscriptions };
2. Core Rate Limiter Class
// middleware/rateLimiter.js
const { SUBSCRIPTION_TIERS } = require('../config/subscriptionTiers');
class RateLimiter {
constructor(redisClient = null) {
// Sử dụng Redis để share state giữa nhiều server instances
this.redis = redisClient;
this.inMemoryStore = new Map();
this.cleanupInterval = 60000; // 1 phút
this.startCleanup();
}
// Lấy tier config của user
getUserTier(userId) {
// Giả lập: trong thực tế query từ database
const tierName = userSubscriptions.get(userId) || 'free';
return {
tierName,
...SUBSCRIPTION_TIERS[tierName]
};
}
// Kiểm tra và cập nhật rate limit
async checkLimit(userId, requestedTokens = 0) {
const tier = this.getUserTier(userId);
const now = Date.now();
const windowMs = 60000; // 1 phút
const userKey = ratelimit:${userId};
const dailyKey = ratelimit:daily:${userId}:${new Date().toISOString().split('T')[0]};
const tokenKey = ratelimit:tokens:${userId}:${new Date().toISOString().slice(0, 7)}; // Theo tháng
let currentReqCount;
let currentDailyCount;
let currentTokenUsage;
if (this.redis) {
// Redis implementation
const multi = this.redis.multi();
multi.incr(userKey);
multi.expire(userKey, Math.ceil(windowMs / 1000));
multi.get(dailyKey);
multi.get(tokenKey);
const results = await multi.exec();
currentReqCount = results[0][1];
currentDailyCount = parseInt(results[2][1]) || 0;
currentTokenUsage = parseInt(results[3][1]) || 0;
} else {
// In-memory implementation (cho development/testing)
const userData = this.inMemoryStore.get(userId) || {
requestCount: 0,
windowStart: now,
dailyCount: 0,
dailyReset: this.getMidnightTimestamp(),
tokenUsage: 0,
monthReset: this.getMonthEndTimestamp(),
};
// Reset window nếu hết thời gian
if (now - userData.windowStart > windowMs) {
userData.requestCount = 0;
userData.windowStart = now;
}
// Reset daily nếu qua ngày mới
if (now > userData.dailyReset) {
userData.dailyCount = 0;
userData.dailyReset = this.getMidnightTimestamp();
}
// Reset monthly token usage nếu qua tháng mới
if (now > userData.monthReset) {
userData.tokenUsage = 0;
userData.monthReset = this.getMonthEndTimestamp();
}
userData.requestCount++;
userData.dailyCount++;
userData.tokenUsage += requestedTokens;
this.inMemoryStore.set(userId, userData);
currentReqCount = userData.requestCount;
currentDailyCount = userData.dailyCount;
currentTokenUsage = userData.tokenUsage;
}
// Kiểm tra các điều kiện
const errors = [];
if (tier.requestsPerMinute !== -1 && currentReqCount > tier.requestsPerMinute) {
errors.push({
type: 'RATE_LIMIT_EXCEEDED',
message: Đã vượt giới hạn ${tier.requestsPerMinute} requests/phút. Vui lòng đợi và thử lại.,
retryAfter: Math.ceil(windowMs / 1000) - Math.floor((now % windowMs) / 1000)
});
}
if (tier.requestsPerDay !== -1 && currentDailyCount > tier.requestsPerDay) {
errors.push({
type: 'DAILY_LIMIT_EXCEEDED',
message: Đã vượt giới hạn ${tier.requestsPerDay} requests/ngày.,
retryAfter: Math.ceil((tier.requestsPerDay - currentDailyCount) / tier.requestsPerDay) * 86400
});
}
if (tier.tokensPerMonth !== -1 && currentTokenUsage > tier.tokensPerMonth) {
errors.push({
type: 'TOKEN_QUOTA_EXCEEDED',
message: Đã vượt giới hạn ${tier.tokensPerMonth.toLocaleString()} tokens/tháng.,
currentUsage: currentTokenUsage,
maxUsage: tier.tokensPerMonth
});
}
return {
allowed: errors.length === 0,
errors,
remaining: {
requestsPerMinute: Math.max(0, tier.requestsPerMinute - currentReqCount),
requestsPerDay: Math.max(0, tier.requestsPerDay - currentDailyCount),
tokensRemaining: Math.max(0, tier.tokensPerMonth - currentTokenUsage),
},
tier: tier.tierName,
usage: {
requestsThisMinute: currentReqCount,
requestsToday: currentDailyCount,
tokensThisMonth: currentTokenUsage,
}
};
}
getMidnightTimestamp() {
const now = new Date();
now.setHours(24, 0, 0, 0);
return now.getTime();
}
getMonthEndTimestamp() {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() + 1, 1).getTime();
}
startCleanup() {
setInterval(() => {
const now = Date.now();
for (const [userId, data] of this.inMemoryStore) {
if (now - data.windowStart > 120000 &&
now - data.dailyReset > 86400000 &&
now - data.monthReset > 2678400000) {
this.inMemoryStore.delete(userId);
}
}
}, this.cleanupInterval);
}
}
module.exports = RateLimiter;
3. Express Middleware Tích Hợp
// middleware/expressRateLimit.js
const RateLimiter = require('./rateLimiter');
const rateLimiter = new RateLimiter();
// Middleware cho Express.js
function rateLimitMiddleware(req, res, next) {
const userId = req.headers['x-user-id'] || req.user?.id;
if (!userId) {
return res.status(401).json({
error: 'UNAUTHORIZED',
message: 'Vui lòng cung cấp user ID để xác thực.'
});
}
// Ước tính token cho request này (có thể refine sau)
const estimatedTokens = estimateRequestTokens(req);
rateLimiter.checkLimit(userId, estimatedTokens)
.then(result => {
// Thêm headers thông tin rate limit
res.set({
'X-RateLimit-Limit': result.tier,
'X-RateLimit-Remaining': JSON.stringify(result.remaining),
'X-RateLimit-Reset': result.errors[0]?.retryAfter || 0,
'X-Usage-Requests-Minute': result.usage.requestsThisMinute,
'X-Usage-Tokens-Month': result.usage.tokensThisMonth,
});
if (!result.allowed) {
const primaryError = result.errors[0];
return res.status(429).json({
error: primaryError.type,
message: primaryError.message,
retryAfter: primaryError.retryAfter,
currentUsage: result.usage,
limits: result.remaining,
upgradeUrl: 'https://www.holysheep.ai/pricing'
});
}
// Attach result vào request object để controller sử dụng
req.rateLimitInfo = result;
next();
})
.catch(err => {
console.error('Rate limiter error:', err);
// Fail open: cho phép request đi nếu rate limiter lỗi
next();
});
}
function estimateRequestTokens(req) {
// Ước tính token dựa trên request body
const body = req.body || {};
const messages = body.messages || [];
let totalChars = 0;
for (const msg of messages) {
if (typeof msg.content === 'string') {
totalChars += msg.content.length;
} else if (Array.isArray(msg.content)) {
for (const part of msg.content) {
if (part.text) totalChars += part.text.length;
}
}
}
// Rough estimate: 1 token ≈ 4 characters cho tiếng Anh, 2-3 cho tiếng Việt
return Math.ceil(totalChars / 3);
}
module.exports = { rateLimitMiddleware, rateLimiter };
4. Kết Nối Với HolySheep AI API
// services/holysheepClient.js
const https = require('https');
class HolySheepAIClient {
constructor(apiKey) {
this.baseUrl = 'https://api.holysheep.ai/v1';
this.apiKey = apiKey;
}
async chatCompletion(messages, options = {}) {
const {
model = 'gpt-4o-mini',
temperature = 0.7,
maxTokens = 4096,
topP = 1,
} = options;
const payload = {
model,
messages,
temperature,
max_tokens: maxTokens,
top_p: topP,
};
const result = await this.makeRequest('/chat/completions', payload);
return result;
}
async makeRequest(endpoint, payload) {
const postData = JSON.stringify(payload);
const options = {
hostname: 'api.holysheep.ai',
port: 443,
path: /v1${endpoint},
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postData),
'Authorization': Bearer ${this.apiKey},
// Thêm custom header để track subscription tier
'X-Subscription-Tier': process.env.USER_SUBSCRIPTION_TIER || 'free',
},
timeout: 30000, // 30s timeout
};
return new Promise((resolve, reject) => {
const req = https.request(options, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
// Rate limit headers từ HolySheep
const rateLimitHeaders = {
limit: res.headers['x-ratelimit-limit'],
remaining: res.headers['x-ratelimit-remaining'],
reset: res.headers['x-ratelimit-reset'],
};
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve({
...JSON.parse(data),
_rateLimit: rateLimitHeaders,
});
} else {
const error = JSON.parse(data);
reject({
statusCode: res.statusCode,
error: error.error || error,
rateLimit: rateLimitHeaders,
});
}
});
});
req.on('error', (err) => {
reject({ error: 'NETWORK_ERROR', message: err.message });
});
req.on('timeout', () => {
req.destroy();
reject({ error: 'TIMEOUT', message: 'Request timeout after 30s' });
});
req.write(postData);
req.end();
});
}
// Helper method để get available models và giá
async getModels() {
const result = await this.makeRequest('/models', {});
return result.data;
}
}
// Factory function với API key validation
function createHolySheepClient(apiKey) {
if (!apiKey || !apiKey.startsWith('hss_')) {
throw new Error('Invalid HolySheep API key format. Key must start with "hss_"');
}
return new HolySheepAIClient(apiKey);
}
module.exports = { HolySheepAIClient, createHolySheepClient };
5. Ví Dụ Sử Dụng Trong Route Handler
// routes/aiChat.js
const express = require('express');
const router = express.Router();
const { rateLimitMiddleware } = require('../middleware/expressRateLimit');
const { createHolySheepClient } = require('../services/holysheepClient');
// Áp dụng rate limit middleware cho tất cả routes trong file
router.use(rateLimitMiddleware);
router.post('/chat', async (req, res) => {
try {
const { messages, model = 'gpt-4o-mini', temperature = 0.7 } = req.body;
// Kiểm tra model restriction theo tier
const tier = req.rateLimitInfo.tier;
const allowedModels = {
free: ['gpt-3.5-turbo', 'claude-3-haiku'],
starter: ['gpt-3.5-turbo', 'gpt-4o-mini', 'claude-3-haiku', 'claude-3-sonnet'],
pro: ['gpt-4o-mini', 'gpt-4o', 'claude-3-sonnet', 'claude-3.5-sonnet', 'gemini-2.5-flash'],
enterprise: '*', // Tất cả models
};
if (allowedModels[tier] !== '*' && !allowedModels[tier].includes(model)) {
return res.status(403).json({
error: 'MODEL_NOT_ALLOWED',
message: Gói ${tier} không được phép sử dụng model ${model}.,
allowedModels: allowedModels[tier],
upgradeUrl: 'https://www.holysheep.ai/pricing',
});
}
// Khởi tạo client với API key
const client = createHolySheepClient(req.headers['x-api-key']);
// Gọi HolySheep AI API
const response = await client.chatCompletion(messages, {
model,
temperature: parseFloat(temperature),
maxTokens: Math.min(req.body.maxTokens || 4096, getMaxTokensForTier(tier)),
});
// Trả về response cùng với usage info
res.json({
success: true,
content: response.choices?.[0]?.message?.content || '',
usage: response.usage,
model: response.model,
rateLimit: response._rateLimit,
});
} catch (error) {
console.error('Chat error:', error);
res.status(error.statusCode || 500).json({
error: error.error?.type || 'INTERNAL_ERROR',
message: error.message || 'Đã xảy ra lỗi khi xử lý request.',
});
}
});
function getMaxTokensForTier(tier) {
const limits = {
free: 2048,
starter: 8192,
pro: 16384,
enterprise: 128000,
};
return limits[tier] || 2048;
}
module.exports = router;
6. Server Entry Point
// server.js
const express = require('express');
const helmet = require('helmet');
const { rateLimitMiddleware } = require('./middleware/expressRateLimit');
const aiChatRoutes = require('./routes/aiChat');
const app = express();
// Security headers
app.use(helmet());
// Parse JSON bodies
app.use(express.json({ limit: '10mb' }));
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Apply rate limiting globally
app.use(rateLimitMiddleware);
// Mount routes
app.use('/api/v1', aiChatRoutes);
// Error handling middleware
app.use((err, req, res, next) => {
console.error('Unhandled error:', err);
res.status(500).json({
error: 'INTERNAL_ERROR',
message: 'Đã xảy ra lỗi không mong muốn.',
});
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(🚀 Server running on port ${PORT});
console.log(📡 Rate limiting: ENABLED);
console.log(🌐 HolySheep API: https://api.holysheep.ai/v1);
});
Bảng Giá HolySheep AI 2026 — Cập Nhật Mới Nhất
| Model | Giá / 1M Tokens | Context Window | Tier tối thiểu |
|---|---|---|---|
| GPT-4.1 | $8.00 | 128K tokens | Pro |
| Claude Sonnet 4.5 | $15.00 | 200K tokens | Pro |
| Gemini 2.5 Flash | $2.50 | 1M tokens | Starter |
| DeepSeek V3.2 | $0.42 | 64K tokens | Free |
| GPT-4o-mini | $3.50 | 128K tokens | Starter |
Lỗi Thường Gặp và Cách Khắc Phục
1. Lỗi "RATE_LIMIT_EXCEEDED" - Vượt Quá Giới Hạn Requests
Mô tả: User nhận được response 429 khi exceed số lượng requests cho phép trong một phút.
// ❌ Code sai - không handle rate limit response
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: messages,
});
// ✅ Code đúng - implement retry logic với exponential backoff
async function chatWithRetry(messages, maxRetries = 3) {
const client = createHolySheepClient(process.env.HOLYSHEEP_API_KEY);
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await client.chatCompletion(messages);
return response;
} catch (error) {
if (error.statusCode === 429) {
// Lấy retry-after từ header hoặc tính toán
const retryAfter = error.rateLimit?.reset || Math.pow(2, attempt) * 1000;
console.log(Rate limited. Retrying after ${retryAfter}ms...);
await sleep(retryAfter);
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
2. Lỗi "MODEL_NOT_ALLOWED" - Model Không Được Phép Theo Tier
Mô tả: User cố gắng sử dụng model cao cấp (GPT-4.1, Claude Sonnet 4.5) nhưng gói subscription không hỗ trợ.
// ❌ Code sai - không kiểm tra model restrictions
app.post('/chat', async (req, res) => {
const { model } = req.body;
const client = createHolySheepClient(req.headers['x-api-key']);
const response = await client.chatCompletion(messages, { model });
res.json(response);
});
// ✅ Code đúng - validate model trước khi gọi API
const MODEL_TIER_MAP = {
'gpt-3.5-turbo': 'free',
'claude-3-haiku': 'free',
'deepseek-v3.2': 'free',
'gpt-4o-mini': 'starter',
'claude-3-sonnet': 'starter',
'gemini-2.5-flash': 'starter',
'gpt-4o': 'pro',
'gpt-4.1': 'pro',
'claude-3.5-sonnet': 'pro',
'claude-sonnet-4.5': 'pro',
};
const TIER_LEVELS = { free: 0, starter: 1, pro: 2, enterprise: 3 };
app.post('/chat', async (req, res) => {
const { model, messages } = req.body;
const userTier = req.rateLimitInfo?.tier || 'free';
const requiredTier = MODEL_TIER_MAP[model];
if (requiredTier && TIER_LEVELS[userTier] < TIER_LEVELS[requiredTier]) {
return res.status(403).json({
error: 'MODEL_NOT_ALLOWED',
message: Model "${model}" yêu cầu gói ${requiredTier} trở lên.,
currentTier: userTier,
requiredTier: requiredTier,
upgradeUrl: 'https://www.holysheep.ai/pricing',
alternatives: getAlternativeModels(userTier),
});
}
const client = createHolySheepClient(req.headers['x-api-key']);
const response = await client.chatCompletion(messages, { model });
res.json(response);
});
3. Lỗi "TOKEN_QUOTA_EXCEEDED" - Vượt Hạn Mức Token Tháng
Mô tả: User đã sử dụng hết quota token được phân bổ trong tháng.
// ❌ Code sai - không theo dõi token usage
app.post('/chat', async (req, res) => {
const response = await client.chatCompletion(messages);
res.json(response);
});
// ✅ Code đúng - track và warning trước khi hit quota
class TokenUsageTracker {
constructor() {
this.users = new Map();
}
async checkAndUpdate(userId, tokensToUse) {
const userData = this.getUserData(userId);
const monthlyLimit = this.getMonthlyLimit(userData.tier);
const projectedUsage = userData.currentMonthUsage + tokensToUse;
const usagePercentage = (projectedUsage / monthlyLimit) * 100;
// Warning nếu sử dụng > 80%
if (usagePercentage > 80 && usagePercentage <= 100) {
console.warn(User ${userId} đã sử dụng ${usagePercentage.toFixed(1)}% quota tháng);
}
if (projectedUsage > monthlyLimit) {
return {
allowed: false,
error: 'TOKEN_QUOTA_EXCEEDED',
message: Bạn đã sử dụng hết quota ${monthlyLimit.toLocaleString()} tokens/tháng.,
currentUsage: userData.currentMonthUsage,
limit: monthlyLimit,
resetDate: this.getMonthEndDate(),
upgradeUrl: 'https://www.holysheep.ai/pricing',
};
}
// Cập nhật usage
userData.currentMonthUsage = projectedUsage;
this.saveUserData(userId, userData);
return { allowed: true };
}
getUserData(userId) {
// Implementation: query từ database/cache
return this.users.get(userId) || {
tier: 'free',
currentMonthUsage: 0
};
}
getMonthlyLimit(tier) {
const limits = { free: 100000, starter: 1000000, pro: 10000000, enterprise: -1 };
return limits[tier];
}
getMonthEndDate() {
const now = new Date();
return new Date(now.getFullYear(), now.getMonth() + 1, 1).toLocaleDateString('vi-VN');
}
}
4. Lỗi "INVALID_API_KEY" - API Key Không Hợp Lệ
Mô tả: HolySheep yêu cầu API key phải bắt đầu bằng prefix hss_.
// ❌ Code sai - không validate API key format
const client = new HolySheepAIClient(req.headers['x-api-key']);
// ✅ Code đúng - validate trước khi sử dụng
function validateAndCreateClient(apiKey) {
if (!apiKey) {
throw {
statusCode: 401,
error: 'MISSING_API_KEY',
message: 'Vui lòng cung cấp HolySheep API key trong header x-api-key.'
};
}
if (!apiKey.startsWith('hss_')) {
throw {
statusCode: 401,
error: 'INVALID_API_KEY',
message: 'HolySheep API key phải bắt đầu bằng "hss_". Lấy key tại: https://www.holysheep.ai/dashboard'
};
}
if (apiKey.length < 32) {
throw {
statusCode: 401,
error: 'INVALID_API_KEY',
message: 'API key không đúng độ dài. Vui lòng kiểm tra lại.'
};
}
return createHolySheepClient(apiKey);
}
// Sử dụng trong route
app.post('/chat', async (req, res) => {
try {
const client = validateAndCreateClient(req.headers['x-api-key']);
const response = await client.chatCompletion(messages);
res.json(response);
} catch (error) {
res.status(error.statusCode || 500).json(error);
}
});
5. Lỗi "NETWORK_ERROR" - Kết Nối Timeout
Mô tả: Request mất quá lâu hoặc không thể kết nối đến API server.
// ❌ Code sai - không có timeout handling
const response = await client.chatCompletion(messages);
// ✅ Code đúng - implement circuit breaker pattern
const CircuitBreaker = require('opossum');
const circuitBreakerOptions = {
timeout: 15000, // Nếu API không respond trong 15s, coi như fail
errorThresholdPercentage: 50, // 50% errors -> open circuit
resetTimeout: 30000, // Thử lại sau 30s
};
const breaker = new CircuitBreaker(async (messages) => {
const client = createHolySheepClient(process.env.HOLYSHEEP_API_KEY);
return await client.chatCompletion(messages);
}, circuitBreakerOptions);
breaker.on('open', () => {
console.log('Circuit breaker OPEN - HolySheep API có vấn đề');
});
breaker.on('halfOpen', () => {
console.log('Circuit breaker HALF-OPEN - Testing connection...');
});
breaker.on('close', () => {
console.log('Circuit breaker CLOSED - API hoạt động bình thường');
});
// Sử dụng với fallback
async function smartChatCompletion(messages) {
try {
return await breaker.fire(messages);
} catch (error) {
// Fallback sang model khác hoặc cache
console.log('HolySheep API unavailable, using fallback...');
return getCachedResponse(messages) || {
error: 'SERVICE_UNAVAILABLE