メインコンテンツへスキップ

冪等性

冪等な処理のために、イベントの id とリソースの resourceId を利用します。
const processedEvents = new Set();

function processEvent(event) {
  // Deduplicate by event ID
  if (processedEvents.has(event.id)) {
    return;
  }

  // Process the event
  handleEvent(event);
  processedEvents.add(event.id);
}

エラーハンドリング

常に適切な HTTP ステータスコードを返してください。
app.post('/jp/developers/webhooks/tiro', (req, res) => {
  try {
    const event = req.body;
    
    // Process the event
    processEvent(event);
    res.status(200).send('OK');
  } catch (error) {
    console.error('Webhook processing error:', error);
    res.status(500).send('Internal server error');
  }
});

リトライの処理

Tiro の webhook システムは、配信に失敗した場合にリトライを行います。リトライは適切に処理してください。
  • 処理に成功した場合は 2xx ステータスコードを返す
  • 恒久的な失敗の場合は 4xx ステータスコードを返す(リトライは行いません)
  • 一時的な失敗の場合は 5xx ステータスコードを返す(リトライを行います)

セキュリティ

常に webhook の真正性を検証してください。
function verifyWebhookAuth(authHeader, expectedSecret) {
  const token = authHeader.replace('Bearer ', '');
  return token === expectedSecret;
}

app.post('/jp/developers/webhooks/tiro', (req, res) => {
  const authHeader = req.headers.authorization;
  
  if (!verifyWebhookAuth(authHeader, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Unauthorized');
  }
  
  // Process webhook event
  processEvent(req.body);
  res.status(200).send('OK');
});

リソースのルーティング

効率的なルーティングのために、resourceTyperesourceId を利用します。
function routeEvent(event) {
  const { resourceType, resourceId } = event.data;

  switch (resourceType) {
    case 'Note':
      return handleNoteEvent(event.type, resourceId, event.data.resource);
    case 'NoteDocument':
      return handleNoteDocumentEvent(event.type, resourceId, event.data.resource);
    case 'NoteSummary':
      return handleNoteSummaryEvent(event.type, resourceId, event.data.resource);
    case 'FolderNoteRelation':
      return handleFolderNoteEvent(event.type, resourceId, event.data.resource);
    default:
      console.log('Unknown resource type:', resourceType);
  }
}