Create, debug, or modify Vercel WDK workflows for data updates and social media posting in the web application...
This skill helps you work with Vercel WDK (Workflow Development Kit) workflows in apps/web/src/workflows/.
The project uses Vercel WDK workflows with the following structure:
apps/web/src/workflows/
├── cars.ts # Car registration data workflow
├── coe.ts # COE bidding data workflow
├── deregistrations.ts # Deregistration data workflow
├── regenerate-post.ts # Blog post regeneration workflow
└── shared.ts # Shared workflow steps (social media, cache)
Workflows are defined using Vercel WDK directives:
import { fetch } from "workflow";
export async function carsWorkflow(payload: CarsWorkflowPayload) {
"use workflow";
// Enable WDK's durable fetch for AI SDK
globalThis.fetch = fetch;
// Step 1: Process data
const result = await processCarsData();
// Step 2: Revalidate cache
await revalidateCarsCache(month, year);
// Step 3: Generate blog post
const post = await generateCarsPost(data, month);
return { postId: post.postId };
}
async function processCarsData(): Promise<UpdaterResult> {
"use step";
// Processing logic - each step is durable and can retry
return await updateCars();
}
Workflows are triggered via Vercel Cron schedules configured in apps/web/vercel.json:
{
"crons": [
{ "path": "/api/workflows/cars", "schedule": "0 10 * * *" },
{ "path": "/api/workflows/coe", "schedule": "0 10 * * *" },
{ "path": "/api/workflows/deregistrations", "schedule": "0 10 * * *" }
]
}
API routes trigger workflows using WDK's start() function:
import { start } from "workflow/api";
import { carsWorkflow } from "@web/workflows/cars";
export async function POST(request: Request) {
const payload = await request.json();
const run = await start(carsWorkflow, [payload]);
return Response.json({ message: "Workflow started", runId: run.runId });
}
Common operations are extracted to shared.ts:
export async function publishToSocialMedia(title: string, link: string) {
"use step";
await socialMediaManager.publishToAll({ message: `📰 ${title}`, link });
}
export async function revalidatePostsCache() {
"use step";
revalidateTag("posts:list", "max");
}
Steps automatically retry on failure. For custom error handling:
async function processData() {
"use step";
try {
const result = await updateData();
return result;
} catch (error) {
console.error("Step failed:", error);
throw error; // Re-throw for automatic retry
}
}
apps/web/src/workflows/"use workflow" directive"use step" directiveapps/web/src/app/api/workflows/vercel.jsonWorkflows typically need:
DATABASE_URL - PostgreSQL connectionUPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN - RedisRun workflow tests:
pnpm -F /web test -- src/workflows
Test individual workflow locally:
# Start dev server
pnpm dev
# Trigger workflow via HTTP
curl -X POST http://localhost:3000/api/workflows/cars
apps/web/src/app/api/workflows/ - Workflow route handlersapps/web/src/config/platforms.ts - Social media configurationapps/web/vercel.json - Vercel Cron schedulesapps/web/CLAUDE.md - Web application documentation"use step"globalThis.fetch = fetch for AI SDK calls