ترفندهای کار با TanStack Start و Durable Objects کلادفلر
خلاصهٔ کاملتر
نویسنده تو این پست چندتا ترفند رو که موقع کار همزمان با TanStack Start و Durable Objects (یعنی آبجکتهای stateful کلادفلر که هر کدوم state و دیتابیس خودشون رو دارن) پیدا کرده، تعریف میکنه. نکتهی پایه اینه که DOها فقط از سمت سرور و از داخل زیرساخت کلادفلر صدا زده میشن، نه از مرورگر. به گفتهی نویسنده، اگه اپت روی Node معمولی یا Vercel هست، DO انتخاب خوبی نیست چون باید یه Worker جدا فقط برای حرف زدن باهاش راه بندازی.
ترفند اول میدلوره. قبلاً هر server function خودش getWorkoutTemplateAIGenerationDurableObject رو صدا میزد و اگه امضای این تابع عوض میشد، باید همهی جاها رو درست میکردی. راه تمیزتر یه Middleware تو TanStack هست که DO رو میسازه و میذارتش تو context. مشکل اینجاست که createMiddleware معمولی context رو undefined میبینه، چون فعلاً تایپهای TanStack چیزهایی رو که global middleware به context اضافه کرده نمیشناسن. راهحل موقت اینه که همون startInstance رو از start.ts ایمپورت کنی و میدلور رو از روی اون بسازی:
export const withWtDo = startInstance.createMiddleware({ type: "function" }).server(async ({ context, next }) => {
const durableObject = await getWorkoutTemplateAIGenerationDurableObject(context);
return next({
context: {
wtDo: durableObject,
},
});
});بعدش کافیه .middleware([withWtDo]) رو به server function اضافه کنی و تو handler مستقیم از context.wtDo استفاده کنی.
ترفند دوم وبسوکتهای تگداره. DOها خودشون از WebSocket پشتیبانی میکنن و با ctx.getWebSockets() میشه به همهی کانکشنها پیام داد. ولی اگه کاربر چندتا سشن تولید تمرین با AI رو همزمان باز کرده باشه، نتیجهی یه سشن به همهشون میرسه. نویسنده sessionId رو تو URL درخواست میذاره، موقع acceptWebSocket یه تگ مثل session: بهش میده و موقع ارسال هم فقط سوکتهای همون تگ رو میگیره (این خلاصهشدهی کد مقالهست):
this.ctx.acceptWebSocket(server, [webSocketTag(sessionId)]);
for (const socket of this.ctx.getWebSockets(webSocketTag(sessionId))) {
socket.send(JSON.stringify(payload));
}نکتهی سوم دربارهی تایپهای برگشتیه. DO هیچوقت مستقیم ساخته نمیشه و همیشه از یه stub (یعنی پروکسیای که درخواستها رو از شبکه به DO میرسونه) صداش میزنی. همین stub جواب هر متد رو تو Promise میپیچه و یه & Disposable هم بهش میچسبونه، حتی اگه return type رو خودت صریح نوشته باشی. برای یه آرایهی ساده مشکلی نیست، چون structural typing تایپاسکریپت اجازه میده اونو به SessionSummary[] نسبت بدی. ولی وقتی متد یه union type مثل SessionPayload برمیگردونه، server function یه خطای خیلی طولانی میده، چون متد [Symbol.dispose] قابل serialize شدن نیست.
نکات کلیدی:
- Durable Objects فقط از سمت سرور و داخل زیرساخت کلادفلر در دسترسن، نه از مرورگر.
- برای دور زدن مشکل تایپ context تو میدلور، اونو با startInstance.createMiddleware بساز.
- با پاس دادن تگ به acceptWebSocket و getWebSockets، هر پیام فقط به سوکتهای همون سشن میرسه.
- stub یه DO به جواب متدها Promise و & Disposable اضافه میکنه، حتی با return type صریح.
- Disposable روی union typeها تو server function خطای «Function may not be serializable» میده.




