Przykłady webhooków
Ta strona zawiera kompletne, gotowe do produkcji przykłady odbierania i przetwarzania webhooków Ignite w różnych językach programowania.
Pełne przykłady serwera
Next.js API Routes
// pages/api/webhook.ts
import { createHmac, timingSafeEqual } from "crypto";
import { NextApiRequest, NextApiResponse } from "next";
// Disable body parser to access raw body for signature verification
export const config = {
api: {
bodyParser: false,
},
};
interface ThumbnailFormat {
url: string;
fileSize: number;
}
interface ThumbnailVariant {
name: string;
width: number;
height: number;
formats: {
jpeg: ThumbnailFormat;
webp: ThumbnailFormat;
};
}
interface VideoWebhookData {
id: string;
status: "COMPLETE" | "PROCESSING" | "NO_FILE" | "FAILED";
title: string;
src?: {
thumbnails?: ThumbnailVariant[];
thumbnailUrl?: string; // deprecated — use src.thumbnails instead
mp4?: Array<{ name: string; url: string; width: number; height: number }>;
};
// ... other video properties
}
async function getRawBody(req: NextApiRequest): Promise<string> {
const chunks: Buffer[] = [];
for await (const chunk of req) {
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
}
return Buffer.concat(chunks).toString("utf-8");
}
function isValidSignature(
rawBody: string,
signatureHeader: string | string[] | undefined,
secret: string
): boolean {
try {
if (typeof signatureHeader !== "string") {
return false;
}
const time = signatureHeader.match(/t=(\d+)/)?.[1];
const token = signatureHeader.match(/v1=(\w+)/)?.[1];
if (!time || !token) {
return false;
}
const signedPayload = `${time}.${rawBody}`;
const hmac = createHmac("sha256", secret);
const calculatedSignature = hmac.update(signedPayload).digest("hex");
return timingSafeEqual(
Buffer.from(token),
Buffer.from(calculatedSignature)
);
} catch {
return false;
}
}
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "POST") {
return res.status(405).end();
}
const rawBody = await getRawBody(req);
if (
!isValidSignature(
rawBody,
req.headers["x-webhook-signature"],
process.env.WEBHOOK_SIGNING_SECRET!
)
) {
console.error("Invalid webhook signature");
return res.status(403).end();
}
const eventType = req.headers["x-webhook-event"];
const deliveryId = req.headers["x-webhook-delivery-id"];
const video = JSON.parse(rawBody) as VideoWebhookData;
// Process based on event type
switch (eventType) {
case "video.created":
console.log("New video created:", video.id, video.title);
// await handleVideoCreated(video);
break;
case "video.updated":
console.log("Video updated:", video.id, video.title);
if (video.status === "COMPLETE") {
// Video processing finished - update your database
// await db.updateVideo(video.id, {
// thumbnails: video.src?.thumbnails,
// thumbnail: video.src?.thumbnails?.[0]?.formats.jpeg.url,
// sizes: video.src?.mp4,
// });
}
break;
case "video.deleted":
console.log("Video deleted:", video.id);
// await handleVideoDeleted(video);
break;
}
return res.status(204).end();
}Dostawy webhooków
Każda próba dostarczenia webhooka jest logowana. Jeśli dostawa się nie powiedzie, system automatycznie ponowi próbę.
Status dostawy
| Status | Opis |
|---|---|
success | Zdarzenie zostało dostarczone pomyślnie (odpowiedź HTTP 2xx) |
failed | Dostawa nie powiodła się (timeout, HTTP 4xx/5xx, błąd sieci) |
Informacje o dostawie
Każdy wpis o dostawie zawiera:
| Pole | Opis |
|---|---|
responseCode | Kod statusu HTTP zwrócony przez Twój endpoint |
responseBody | Treść odpowiedzi z Twojego endpointu (obcięta przy dużych odpowiedziach) |
errorMessage | Komunikat błędu, jeśli dostawa się nie powiodła |
deliveredAt | Znacznik czasu próby dostawy |
attemptNumber | Która to próba (rosnie przy ponowieniach) |
nextRetryAt | Kiedy zaplanowano następne ponowienie (jeśli dotyczy) |
Zachowanie przy ponowieniach
- Nieudane dostawy są automatycznie ponawiane
- Jeśli ustawiono
nextRetryAt, ponowienie jest zaplanowane - Jeśli
nextRetryAtnie jest ustawione, kolejnych ponowień nie będzie
Dobre praktyki
Stosowanie tych praktyk zwiększa niezawodność przetwarzania webhooków i ułatwia diagnozowanie problemów.
Czas odpowiedzi
Twój endpoint musi odpowiedzieć w ciągu 30 sekund. Przy długo trwających operacjach od razu potwierdź odbiór webhooka i przetwarzaj asynchronicznie:
app.post('/webhook', async (req, res) => {
// Immediately acknowledge receipt
res.status(200).json({ received: true });
// Process asynchronously (don't await)
processWebhookAsync(req.body).catch(console.error);
});
async function processWebhookAsync(video) {
// Long-running operations here
}Bezpieczeństwo
- Chroń sekrety — nigdy nie umieszczaj sekretu podpisywania w kodzie po stronie klienta ani w repozytorium
- Używaj HTTPS — w produkcji zawsze endpointów HTTPS
- Weryfikuj wszystko — zawsze sprawdzaj podpisy przed przetwarzaniem
- Ograniczanie częstotliwości — rozważ limitowanie żądań na swoim endpoincie
Niezawodność
- Idempotentność — zapisuj i sprawdzaj
X-Webhook-Delivery-Id, żeby obsłużyć duplikaty dostaw - Logowanie — loguj wszystkie żądania webhooków do debugowania
- Monitoring — ustaw alerty na nieudane przetwarzanie webhooków
- Obsługa błędów — zwracaj odpowiednie kody HTTP, żeby ponowienia działały poprawnie
Kody statusu HTTP
| Kod | Znaczenie |
|---|---|
200-299 | Sukces — zdarzenie przetworzone |
4xx | Błąd po stronie klienta — będzie ponowione |
5xx | Błąd serwera — będzie ponowione |
Rozwiązywanie problemów
Brak zdarzeń
- Sprawdź, czy subskrypcja webhooka ma status Active
- Zweryfikuj, czy wybrane są właściwe typy zdarzeń
- Upewnij się, że zdarzenia występują we właściwej przestrzeni roboczej
- Sprawdź logi serwera pod kątem przychodzących żądań
Weryfikacja podpisu się nie udaje
- Upewnij się, że używasz właściwego sekretu podpisywania (a nie tokena API)
- Sprawdź, czy treść żądania nie jest modyfikowana przed weryfikacją
- Sprawdź zgodność serializacji JSON (zwarta, bez zbędnych spacji)
- Sprawdź, czy nagłówek podpisu jest parsowany poprawnie:
t=timestamp,v1=signature
Błędy timeoutu
- Odpowiadaj w ciągu 30 sekund
- Przenieś długie operacje do przetwarzania w tle
- Zwróć od razu
200, przetwarzaj asynchronicznie
Zduplikowane zdarzenia
- Wprowadź idempotentność z użyciem
X-Webhook-Delivery-Id - Sprawdzaj
attemptNumberprzy ponowieniach - Przechowuj przetworzone identyfikatory dostaw w bazie danych