APIXO oferuje dwa sposoby odbierania wyników zadań: polling (tryb async) i webhooks (tryb callback). Ten przewodnik pomaga wybrać właściwe podejście i wdrożyć je efektywnie.
Szybkie porównanie
| Aspekt | Polling (Async) | Webhooks (Callback) |
|---|
| Konfiguracja | Prosta | Wymaga publicznego endpointu |
| Real-time | Bliskie real-time | Natychmiastowe |
| Wywołania API | Wiele na zadanie | Jedno na zadanie |
| Najlepsze dla | Rozwój, aplikacje klienckie | Serwery produkcyjne |
| Infrastruktura | Brak | Endpoint HTTPS |
Kiedy używać pollingu
Wybierz polling gdy:
- Budujesz aplikacje po stronie klienta
- Prototypujesz lub testujesz
- Nie masz publicznego serwera
- Przetwarzasz mały wolumen zadań
Podstawowa implementacja pollingu
async function pollForResult(model, taskId, apiKey) {
const interval = 3000; // 3 seconds
const maxAttempts = 60;
for (let i = 0; i < maxAttempts; i++) {
const response = await fetch(
`https://api.apixo.ai/api/v1/statusTask/${model}?taskId=${taskId}`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
const { data } = await response.json();
if (data.state === 'success') {
return JSON.parse(data.resultJson).resultUrls;
}
if (data.state === 'failed') {
throw new Error(data.failMsg);
}
await new Promise(r => setTimeout(r, interval));
}
throw new Error('Timeout');
}
Exponential backoff
Dla lepszej wydajności użyj exponential backoff:
async function pollWithBackoff(model, taskId, apiKey) {
let interval = 3000; // Start at 3s
const maxInterval = 30000; // Max 30s
const maxTime = 300000; // 5 min timeout
let elapsed = 0;
while (elapsed < maxTime) {
const response = await fetch(
`https://api.apixo.ai/api/v1/statusTask/${model}?taskId=${taskId}`,
{ headers: { 'Authorization': `Bearer ${apiKey}` } }
);
const { data } = await response.json();
if (data.state === 'success') {
return JSON.parse(data.resultJson).resultUrls;
}
if (data.state === 'failed') {
throw new Error(data.failMsg);
}
await new Promise(r => setTimeout(r, interval));
elapsed += interval;
interval = Math.min(interval * 1.5, maxInterval);
}
throw new Error('Timeout');
}
Zalecane odstępy według typu modelu
| Typ modelu | Początkowe oczekiwanie | Odstęp pollingu | Maks. oczekiwanie |
|---|
| Obraz (szybki) | 5s | 3s | 2 min |
| Obraz (jakość) | 10s | 5s | 3 min |
| Wideo | 60s | 15s | 10 min |
| Audio | 30s | 10s | 5 min |
Kiedy używać webhooków
Wybierz webhooki gdy:
- Prowadzisz serwer produkcyjny
- Przetwarzasz duży wolumen zadań
- Potrzebujesz powiadomień w czasie rzeczywistym
- Chcesz zminimalizować wywołania API
Konfiguracja webhooka
1. Utwórz endpoint:
Express.js
FastAPI
Next.js
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook/apixo', (req, res) => {
const { taskId, state, resultJson, failCode, failMsg } = req.body.data;
if (state === 'success') {
const urls = JSON.parse(resultJson).resultUrls;
console.log('Generated:', urls);
// Process your images/videos
} else if (state === 'failed') {
console.error(`Task ${taskId} failed: ${failCode}`);
}
res.status(200).send('OK');
});
app.listen(3000);
from fastapi import FastAPI, Request
import json
app = FastAPI()
@app.post('/webhook/apixo')
async def handle_webhook(request: Request):
body = await request.json()
data = body['data']
if data['state'] == 'success':
urls = json.loads(data['resultJson'])['resultUrls']
print(f"Generated: {urls}")
elif data['state'] == 'failed':
print(f"Task {data['taskId']} failed: {data['failCode']}")
return {'status': 'ok'}
// app/api/webhook/apixo/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const body = await request.json();
const { taskId, state, resultJson, failCode } = body.data;
if (state === 'success') {
const urls = JSON.parse(resultJson).resultUrls;
console.log('Generated:', urls);
} else if (state === 'failed') {
console.error(`Task ${taskId} failed: ${failCode}`);
}
return NextResponse.json({ status: 'ok' });
}
2. Zgłoś zadanie z callbackiem:
const response = await fetch('https://api.apixo.ai/api/v1/generateTask/nano-banana', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
request_type: 'callback',
callback_url: 'https://your-domain.com/webhook/apixo',
input: {
mode: 'text-to-image',
prompt: 'A beautiful landscape',
},
}),
});
Wymagania webhooka
- Musi być publicznie dostępny przez HTTPS
- Musi odpowiadać HTTP 200 w ciągu 30 sekund
- Powinien obsługiwać duplikaty dostaw (użyj
taskId dla idempotencji)
Polityka ponowień
Jeśli webhook się nie powiedzie:
| Próba | Opóźnienie |
|---|
| 1. ponowienie | 30 sekund |
| 2. ponowienie | 2 minuty |
| 3. ponowienie | 10 minut |
Po 3 nieudanych próbach webhook jest porzucany. Nadal możesz sprawdzić status przez endpoint statusu.
Podejście hybrydowe
Dla maksymalnej niezawodności połącz oba podejścia:
class TaskManager {
constructor(apiKey) {
this.apiKey = apiKey;
this.pendingTasks = new Map();
}
async submit(model, input, callbackUrl = null) {
const response = await fetch(
`https://api.apixo.ai/api/v1/generateTask/${model}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
request_type: callbackUrl ? 'callback' : 'async',
callback_url: callbackUrl,
input,
}),
}
);
const { data } = await response.json();
if (!callbackUrl) {
// Fallback to polling if no webhook
return this.poll(model, data.taskId);
}
// Track task for webhook
this.pendingTasks.set(data.taskId, { model, status: 'pending' });
return data.taskId;
}
handleWebhook(body) {
const { taskId, state, resultJson, failMsg } = body.data;
if (state === 'success') {
this.pendingTasks.delete(taskId);
return JSON.parse(resultJson).resultUrls;
}
if (state === 'failed') {
this.pendingTasks.delete(taskId);
throw new Error(failMsg);
}
}
async poll(model, taskId) {
// Fallback polling implementation
return pollWithBackoff(model, taskId, this.apiKey);
}
}
Lokalny rozwój z ngrok
Do testowania webhooków lokalnie:
# Install ngrok
npm install -g ngrok
# Start your local server
node server.js # runs on port 3000
# Tunnel to local server
ngrok http 3000
# Returns: https://abc123.ngrok.io
# Use ngrok URL as callback_url
Podsumowanie
| Scenariusz | Rekomendacja |
|---|
| Rozwój / Testowanie | Polling |
| Aplikacja kliencka | Polling |
| Backend produkcyjny | Webhooks |
| Duży ruch | Webhooks |
| Maksymalna niezawodność | Hybrydowe (webhooks + polling jako zapasowy) |
Zacznij od pollingu dla prostoty, potem przejdź na webhooki wraz ze skalowaniem aplikacji.