Building a WeChat Mini Program that talks to an AI backend sounds like a weekend project — until you hit the reality of API design, authentication, rate limiting, and the WeChat platform's own quirks. Let's cut through the noise and build a clean API layer between your Mini Program and OpenClaw.
Your WeChat Mini Program doesn't talk to OpenClaw directly. Instead, it goes through a lightweight API gateway running on your Tencent Cloud Lighthouse instance:
Mini Program → HTTPS → Lighthouse API Gateway → OpenClaw → Model API → Response
This separation gives you control over authentication, rate limiting, and response formatting — things the Mini Program framework expects but OpenClaw doesn't natively provide for WeChat's specific requirements.
On your Tencent Cloud Lighthouse instance, create an API layer that the Mini Program can call:
// /opt/clawdbot/api/miniprogram-gateway.js
const express = require('express');
const crypto = require('crypto');
const app = express();
app.use(express.json());
// WeChat session validation middleware
const validateSession = (req, res, next) => {
const sessionKey = req.headers['x-wx-session'];
if (!sessionKey || !isValidSession(sessionKey)) {
return res.status(401).json({ error: 'Invalid session' });
}
next();
};
// Main chat endpoint
app.post('/api/chat', validateSession, async (req, res) => {
const { message, conversation_id } = req.body;
if (!message || message.length > 2000) {
return res.status(400).json({ error: 'Invalid message' });
}
try {
const response = await forwardToOpenClaw({
message,
conversation_id,
user_id: req.wxUserId
});
res.json({
reply: response.text,
conversation_id: response.conversation_id,
tokens_used: response.tokens
});
} catch (err) {
console.error('OpenClaw error:', err.message);
res.status(500).json({ error: 'Service temporarily unavailable' });
}
});
// Health check
app.get('/api/health', (req, res) => {
res.json({ status: 'ok', timestamp: Date.now() });
});
app.listen(3000, () => console.log('Mini Program API running on :3000'));
On the Mini Program side, call your API:
// pages/chat/chat.js
Page({
data: {
messages: [],
inputText: '',
conversationId: null,
loading: false
},
async sendMessage() {
const message = this.data.inputText.trim();
if (!message) return;
this.setData({
loading: true,
messages: [...this.data.messages, { role: 'user', content: message }],
inputText: ''
});
try {
const res = await wx.request({
url: 'https://YOUR_LIGHTHOUSE_IP/api/chat',
method: 'POST',
header: {
'Content-Type': 'application/json',
'x-wx-session': wx.getStorageSync('sessionKey')
},
data: {
message: message,
conversation_id: this.data.conversationId
}
});
if (res.statusCode === 200) {
this.setData({
messages: [...this.data.messages, { role: 'assistant', content: res.data.reply }],
conversationId: res.data.conversation_id
});
}
} catch (err) {
wx.showToast({ title: 'Network error', icon: 'none' });
} finally {
this.setData({ loading: false });
}
}
});
WeChat Mini Programs use a specific auth flow. Here's how to integrate it:
// Login flow on Mini Program side
wx.login({
success: async (loginRes) => {
// Exchange code for session on your server
const result = await wx.request({
url: 'https://YOUR_LIGHTHOUSE_IP/api/auth/wx-login',
method: 'POST',
data: { code: loginRes.code }
});
wx.setStorageSync('sessionKey', result.data.sessionKey);
}
});
Server-side session exchange:
// /opt/clawdbot/api/auth.js
app.post('/api/auth/wx-login', async (req, res) => {
const { code } = req.body;
const wxRes = await fetch(
`https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`
);
const wxData = await wxRes.json();
// Generate a session key for your API
const sessionKey = crypto.randomBytes(32).toString('hex');
// Store session mapping
sessions.set(sessionKey, {
openid: wxData.openid,
created: Date.now()
});
res.json({ sessionKey });
});
Protect your API from abuse:
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 20, // 20 requests per minute per user
keyGenerator: (req) => req.wxUserId || req.ip,
message: { error: 'Too many requests. Please slow down.' }
});
app.use('/api/chat', limiter);
Get your API gateway running on proper infrastructure:
Keep a simple API doc alongside your code:
POST /api/chat
Headers: x-wx-session (required)
Body: { "message": string, "conversation_id": string|null }
Response: { "reply": string, "conversation_id": string, "tokens_used": number }
POST /api/auth/wx-login
Body: { "code": string }
Response: { "sessionKey": string }
GET /api/health
Response: { "status": "ok", "timestamp": number }
A clean API layer is the foundation for everything else — streaming responses, conversation history, user preferences, and analytics. Get the basics right first, then iterate.
Build the API. Ship the Mini Program. Iterate fast.