How I built a Food-Truck Marketplace on Convex, Cloudflare, and Vercel

How Slyderz runs a food-truck marketplace — web, mobile, payments, and a CMS across one TypeScript monorepo with zero traditional servers, on Convex and Cloudflare Workers.
Slyderz is a marketplace that connects food-truck owners with the people who
want to find them. A customer-facing web app, a management dashboard for truck
operators, an iOS app, and a content site, all sharing one backend.
This post is about the architecture that lets all of that live in a single TypeScript
monorepo and deploy to zero traditional servers.
The short version: everything runs on Convex for the application layer and
Cloudflare Workers for the web layer, glued together by a pnpm + Turborepo
monorepo and a branch-driven GitHub Actions pipeline. When code lands on
staging or main, it's live a few minutes later.
One monorepo, five apps, four packages
The whole product is one repository with pnpm workspaces and Turborepo at the
top level. A shared catalog in pnpm-workspace.yaml pins the versions of the
big dependencies (React, Next.js, TypeScript, Tailwind) so every app upgrades in
lockstep instead of drifting.
What lives under apps/ and packages/:
- consumer — the customer-facing site. Next.js 16 with the App Router,
Tailwind v4, React Query, and Stripe for payments.
- dashboard — the operations hub for truck owners: menu, orders, calendar,
hours and locations, reviews, customer CRM, and Instagram integration. It's
TanStack Start (Vite + TanStack Router) with typed server functions, deployed
to Cloudflare Workers.
- mobile — an Expo SDK 57 app with expo-router, native maps, Location,
and Stripe in-app payments. Same auth, same Convex API as the web apps.
- blog — Astro + EmDash, an
edge-native CMS that runs on Cloudflare Workers with D1, R2, and KV.
- packages/backend — the Convex backend, consumed by every app.
- packages/ui — the shared component library (shadcn-style, built on
base-ui) with a branded theme, bundled with tsdown into per-component
entry points.
- packages/email — transactional email templates (jsx-email) rendered
server-side by the backend for auth flows, invoices, and waitlist signups.
- packages/validators — shared Zod schemas so validation never drifts
between client and server.
The key idea is that the backend is also a workspace package. Apps import the
generated Convex API (@workspace/backend/convex/_generated/api) directly, so
there's no REST layer to maintain and no hand-written client to keep in sync —
the types are generated and shared.
Convex is the system of record
All product data organizations, menus, orders, locations, hours, reviews,
payment providers, and social posts lives in one Convex app. Convex gives us
reactive queries, mutations-as-transactions, schedulers, and HTTP actions out of
the box, plus a component ecosystem that replaces most of what I'd otherwise
hand-roll:
- better-auth for auth (email/password + OAuth) stored as a Convex component.
- Stripe for payments and connected accounts for truck operators.
- Resend for transactional email (with a kitchen sink of edge cases handled
by a cron that purges stale emails once an hour).
- R2 for media uploads, served through a CDN
- Workflow for the multi-step Instagram messaging flows with durable
execution and retries.
- PostHog for product analytics.
Because everything is one Convex app, a query from the dashboard and one from
the mobile app hit the same code. Cross-cutting concerns like "this truck is no
longer accepting orders after closing time" are enforced in one place, not
reimplemented per client.
The blog runs at the edge
The blog was the fun one. Instead of attaching a database to a traditional CMS,
it's an Astro site rendered by @astrojs/cloudflare, with EmDash providing the
admin UI and content model. Content lives in D1 (Cloudflare's SQLite), media
in R2, and rendered pages are cached in KV. Scheduled workers rebuild
stale content in the background, and caching rules revalidate aggressively (SWR
on the homepage). Full-text search, RSS, and JSON-LD all come free. It's the
rare CMS where the compute and storage sit in the same edge network as its
readers.
Deployment: branch = environment
CI is a single GitHub Actions workflow (deploy-dashboard.yml) that treats the
git branch as the environment:
- Pushing to staging builds and deploys the dashboard to a
dashboard-staging Worker
- Pushing to main deploys to dashboard-production behind
- Both jobs also run convex deploy using an environment-scoped deploy key, so
the Convex app reflects the same branch that produced the worker.
The workflow is split into build and deploy jobs: the build job compiles the app
with environment-specific build-time variables (scoped in GitHub environment
secrets) and uploads dist/ as an artifact; the deploy job downloads that
artifact and hands it to cloudflare/wrangler-action. That keeps the build
hermetic and the deploy job a pure "publish this exact output" step.
Environment configuration lives in wrangler.jsonc as an env block per
environment. The staging and production Workers share one config with their own
names, vars (Convex URLs, PostHog keys, site URLs) and custom-domain routes.
OpenTelemetry-style observability is toggled on for production through the same
file.
The Takeaway
None of this requires managing infrastructure. Convex owns the data and the
business logic; Cloudflare owns the edge compute, storage, and CDN; GitHub
Actions turns a merge into a deploy. The platform choice was the architecture
decision. It meant one small team could build a marketplace spanning web,
mobile, payments, email, and content publishing without ever provisioning a
server.
The project is live at:
Consumer - slyderz.co
Dashboard - dashboard.slyderz.co
Blog - news.slyderz.co.
Comments
No comments yet