การเชื่อมต่อระบบภายนอกด้วย APIs และ webhooks เพื่อสร้างระบบอัตโนมัติที่สมบูรณ์
Understanding APIs
What is an API?
API (Application Programming Interface) คือ:
├─ ช่องทางการสื่อสารระหว่างระบบ
├─ กำหนดรูปแบบการ request/response
├─ มี rules และ protocols ที่ชัดเจน
└─ ทำให้ระบบต่างๆ สามารถทำงานร่วมกันได้
Types of APIs
1. REST APIs (Most Common)
├─ HTTP methods: GET, POST, PUT, DELETE
├─ JSON/XML data format
├─ Stateless communication
└─ Easy to use and understand
2. GraphQL APIs
├─ Single endpoint
├─ Query exactly what you need
├─ Strong typing
└─ Good for complex data
3. SOAP APIs
├─ XML-based
├─ Strict standards
├─ Enterprise grade
└─ More complex
4. Webhooks
├─ Reverse API
├─ Event-driven
├─ Real-time notifications
└─ Server-to-server communication
Working with REST APIs
Making API Requests
// Basic GET request
async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer your-api-key',
'Content-Type': 'application/json'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('API request failed:', error);
throw error;
}
}
// POST request with data
async function createOrder(orderData) {
try {
const response = await fetch('https://api.example.com/orders', {
method: 'POST',
headers: {
'Authorization': 'Bearer your-api-key',
'Content-Type': 'application/json'
},
body: JSON.stringify(orderData)
});
const data = await response.json();
return data;
} catch (error) {
console.error('Create order failed:', error);
throw error;
}
}
API Authentication Methods
// 1. API Key (Header)
const headers = {
'X-API-Key': 'your-api-key-here',
'Content-Type': 'application/json'
};
// 2. Bearer Token
const headers = {
'Authorization': 'Bearer your-jwt-token',
'Content-Type': 'application/json'
};
// 3. Basic Auth
const credentials = btoa('username:password');
const headers = {
'Authorization': `Basic ${credentials}`,
'Content-Type': 'application/json'
};
// 4. OAuth 2.0
async function getOAuthToken() {
const response = await fetch('https://oauth.example.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'client_credentials',
client_id: 'your-client-id',
client_secret: 'your-client-secret'
})
});
const { access_token } = await response.json();
return access_token;
}
Error Handling
async function robustAPICall(url, options = {}) {
try {
const response = await fetch(url, {
timeout: 10000, // 10 second timeout
...options
});
// Handle different HTTP status codes
switch (response.status) {
case 200:
case 201:
return await response.json();
case 400:
throw new Error('Bad request - Invalid data');
case 401:
throw new Error('Unauthorized - Check API credentials');
case 403:
throw new Error('Forbidden - Insufficient permissions');
case 404:
throw new Error('Not found - Resource does not exist');
case 429:
throw new Error('Rate limit exceeded - Try again later');
case 500:
throw new Error('Server error - Try again later');
default:
throw new Error(`HTTP error! status: ${response.status}`);
}
} catch (error) {
console.error('API call failed:', error);
// Retry logic for transient errors
if (error.message.includes('timeout') || error.message.includes('Server error')) {
console.log('Retrying in 5 seconds...');
await new Promise(resolve => setTimeout(resolve, 5000));
return robustAPICall(url, options);
}
throw error;
}
}
Webhooks Explained
What is a Webhook?
Webhook คือ:
├─ Reverse API pattern
├─ Server sends data to client
├─ Event-driven notifications
├─ Real-time data updates
└─ Reduces polling overhead
ตัวอย่างการใช้งาน:
├─ LINE Platform ส่ง messages มาที่เรา
├─ Stripe ส่ง payment notifications
├─ GitHub ส่ง commit notifications
└─ Supabase ส่ง database change notifications
Creating Webhook Endpoints
// n8n Function Node - LINE Webhook Handler
function handleLINEWebhook() {
const events = $input.all()[0].json.events || [];
const processedEvents = [];
for (const event of events) {
if (event.type === 'message' && event.message.type === 'text') {
processedEvents.push({
type: 'message',
userId: event.source.userId,
message: event.message.text,
replyToken: event.replyToken,
timestamp: event.timestamp
});
} else if (event.type === 'follow') {
processedEvents.push({
type: 'follow',
userId: event.source.userId,
timestamp: event.timestamp
});
} else if (event.type === 'unfollow') {
processedEvents.push({
type: 'unfollow',
userId: event.source.userId,
timestamp: event.timestamp
});
}
}
return processedEvents.map(event => ({ json: event }));
}
// Express.js Webhook Endpoint
app.post('/webhook/line', (req, res) => {
const events = req.body.events || [];
// Process each event
events.forEach(async (event) => {
if (event.type === 'message') {
await processMessage(event);
}
});
// Always return 200 OK
res.status(200).json({ status: 'ok' });
});
Webhook Security
// Verify webhook signature (LINE example)
function verifyWebhookSignature(body, signature, channelSecret) {
const crypto = require('crypto');
const hash = crypto
.createHmac('SHA256', channelSecret)
.update(body, 'utf8')
.digest('base64');
return hash === signature;
}
// Express middleware for webhook verification
app.use('/webhook/line', (req, res, next) => {
const signature = req.headers['x-line-signature'];
const body = JSON.stringify(req.body);
if (!verifyWebhookSignature(body, signature, process.env.LINE_CHANNEL_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
next();
});
Thai Service APIs
1. Thailand Post API
// Track Thailand Post parcels
async function trackThailandPost(trackingNumber) {
const apiKey = process.env.THAILAND_POST_API_KEY;
try {
const response = await fetch(`https://trackapi.thailandpost.co.th/post/api/v1/track/${trackingNumber}`, {
headers: {
'Authorization': `Token ${apiKey}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data.status === 'success') {
return data.response.items[0];
} else {
throw new Error('Tracking failed');
}
} catch (error) {
console.error('Thailand Post API error:', error);
throw error;
}
}
2. DHL Thailand API
// DHL tracking for international shipments
async function trackDHL(waybillNumber) {
try {
const response = await fetch(`https://api-eu.dhl.com/track/shipments?trackingNumber=${waybillNumber}`, {
headers: {
'DHL-API-Key': process.env.DHL_API_KEY',
'Content-Type': 'application/json'
}
});
const data = await response.json();
return data.shipments[0];
} catch (error) {
console.error('DHL API error:', error);
throw error;
}
}
3. Thai Government APIs
// DBD (Department of Business Development) API
async function searchBusinessRegistration(businessId) {
try {
const response = await fetch(`https://api.dbd.go.th/api/v1/business/${businessId}`, {
headers: {
'Authorization': `Bearer ${process.env.DBD_API_KEY}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
return data;
} catch (error) {
console.error('DBD API error:', error);
throw error;
}
}
// Revenue Department API for tax verification
async function verifyTaxID(taxID) {
try {
const response = await fetch(`https://api.rd.go.th/api/v1/verify/${taxID}`, {
headers: {
'Authorization': `Bearer ${process.env.RD_API_KEY}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
return data;
} catch (error) {
console.error('Revenue Department API error:', error);
throw error;
}
}
n8n API Integration
HTTP Request Node Configuration
{
"method": "POST",
"url": "https://api.example.com/endpoint",
"authentication": "Header Auth",
"headerAuth": {
"name": "Authorization",
"value": "Bearer {{ $credentials.apiKey }}"
},
"jsonParameters": true,
"jsonBody": "{\n \"customer_id\": \"{{ $json.customerId }}\",\n \"items\": {{ $json.items }},\n \"total\": {{ $json.total }}\n}",
"options": {
"timeout": 30000,
"retry": {
"enabled": true,
"maxAttempts": 3
}
}
}
Function Node for API Processing
// Process API response in n8n
function processAPIResponse() {
const response = $input.all()[0].json;
// Extract relevant data
const processedData = {
orderId: response.order_id,
status: response.status,
customer: {
id: response.customer.id,
name: response.customer.name,
email: response.customer.email
},
items: response.items.map(item => ({
productId: item.product_id,
name: item.name,
quantity: item.quantity,
price: item.price
})),
totalAmount: response.total_amount,
createdAt: response.created_at
};
// Add processing timestamp
processedData.processedAt = new Date().toISOString();
return [{ json: processedData }];
}
Real-time Integration Examples
1. Order Processing Pipeline
Customer Order → API (Vercel) → Supabase → Webhook → n8n → LINE Notification
// Vercel API endpoint
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
try {
const { customerId, items, totalAmount } = req.body;
// Save to Supabase
const { data: order, error } = await supabase
.from('orders')
.insert({
customer_id: customerId,
items: items,
total_amount: totalAmount,
status: 'pending'
})
.select()
.single();
if (error) throw error;
// Trigger n8n webhook
await fetch('https://your-n8n-instance.com/webhook/new-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orderId: order.order_id,
customerId: customerId,
totalAmount: totalAmount
})
});
res.status(200).json({ success: true, order });
} catch (error) {
console.error('Order processing error:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
2. Real-time Chat System
LINE Message → n8n → Custom API → Supabase (Real-time) → Dashboard Update
// n8n Function Node
async function processChatMessage() {
const message = $json.message;
const userId = $json.userId;
// Save to database
const { data: savedMessage } = await supabase
.from('messages')
.insert({
user_id: userId,
message: message,
timestamp: new Date().toISOString()
})
.select()
.single();
// Process with AI if needed
const aiResponse = await processWithAI(message);
// Update message with AI response
await supabase
.from('messages')
.update({ ai_response: aiResponse })
.eq('id', savedMessage.id);
return [{
json: {
messageId: savedMessage.id,
aiResponse: aiResponse,
replyToken: $json.replyToken
}
}];
}
Best Practices
1. Rate Limiting
// Implement rate limiting
const rateLimiter = new Map();
async function rateLimitedAPICall(url, options, maxCalls = 100, windowMs = 60000) {
const key = options.headers['Authorization'] || 'anonymous';
const now = Date.now();
const windowStart = now - windowMs;
// Clean old entries
for (const [k, calls] of rateLimiter.entries()) {
rateLimiter.set(k, calls.filter(call => call > windowStart));
if (rateLimiter.get(k).length === 0) {
rateLimiter.delete(k);
}
}
// Check current rate
const currentCalls = rateLimiter.get(key) || [];
if (currentCalls.length >= maxCalls) {
throw new Error('Rate limit exceeded');
}
// Add current call
currentCalls.push(now);
rateLimiter.set(key, currentCalls);
// Make API call
return fetch(url, options);
}
2. Caching
// Simple cache implementation
const cache = new Map();
async function cachedAPICall(url, options, ttl = 300000) { // 5 minutes default
const cacheKey = `${url}:${JSON.stringify(options)}`;
const cached = cache.get(cacheKey);
if (cached && (Date.now() - cached.timestamp) < ttl) {
return cached.data;
}
const data = await fetch(url, options);
cache.set(cacheKey, {
data: await data.json(),
timestamp: Date.now()
});
return cache.get(cacheKey).data;
}
3. Error Recovery
// Exponential backoff retry
async function retryAPICall(url, options, maxRetries = 3) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, options);
if (response.ok) {
return response.json();
}
// Don't retry client errors
if (response.status >= 400 && response.status < 500) {
throw new Error(`Client error: ${response.status}`);
}
} catch (error) {
lastError = error;
if (attempt < maxRetries) {
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
console.log(`Retry attempt ${attempt + 1} in ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
throw lastError;
}
Monitoring & Debugging
API Call Logging
// Log all API calls for debugging
async function loggedAPICall(url, options) {
const startTime = Date.now();
const requestId = Math.random().toString(36).substr(2, 9);
console.log(`[${requestId}] API Call: ${options.method || 'GET'} ${url}`);
try {
const response = await fetch(url, options);
const duration = Date.now() - startTime;
console.log(`[${requestId}] Response: ${response.status} (${duration}ms)`);
if (!response.ok) {
const errorText = await response.text();
console.error(`[${requestId}] Error: ${errorText}`);
}
return response;
} catch (error) {
const duration = Date.now() - startTime;
console.error(`[${requestId}] Failed: ${error.message} (${duration}ms)`);
throw error;
}
}
Webhook Monitoring
// Track webhook delivery
app.post('/webhook/:provider', async (req, res) => {
const provider = req.params.provider;
const webhookId = Math.random().toString(36).substr(2, 9);
console.log(`[${webhookId}] Webhook received from ${provider}`);
try {
// Process webhook
await processWebhook(provider, req.body);
console.log(`[${webhookId}] Webhook processed successfully`);
res.status(200).json({ status: 'ok', webhookId });
} catch (error) {
console.error(`[${webhookId}] Webhook processing failed:`, error);
res.status(500).json({
status: 'error',
webhookId,
error: error.message
});
}
});
Next Steps
- Client Examples - ดูตัวอย่างการใช้งานจริง
- Chat Assistant Automation - ตัวอย่าง chatbot
- Real-time Dashboard - ตัวอย่างระบบ real-time
ต้องการความช่วยเหลือ? ติดต่อเราได้ที่ ShantiLink.com 💬