Back to projects YSKM523 / lakebbs-ca

lakebbs

City-aware Chinese local community platform for Thunder Bay, Sudbury, and Northern Ontario — recently rewritten onto Next.js 16 + Drizzle, with daily aggregation bots and GEO-optimized landing pages.

Live · Recently Rewritten Founder · Core Developer last updated: May 16, 2026

Context

Chinese communities in smaller Canadian cities — Thunder Bay, Sudbury, the rest of Northern Ontario — coordinate housing, jobs, classifieds, immigration questions, and local knowledge almost entirely inside WeChat groups. WeChat is a fine chat client and a terrible permanent record: you can’t search it, you can’t categorize it, group caps lock out new members from existing context, and Google obviously can’t index it.

lakebbs treats that information as a first-class artifact instead of a stream. It’s not a forum clone — it’s a city-aware Chinese local information platform somewhere between forum, classifieds, housing board, and settlement guide.

Home — city-aware feed combining user posts, daily housing / job / Reddit digests, immigration briefings, and a most-liked sidebar.
Home — city-aware feed combining user posts, daily housing / job / Reddit digests, immigration briefings, and a most-liked sidebar.
Section page — /thunderbay/realEstate. Daily aggregated listings from Kijiji + other sources alongside user transfer / sublet posts.
Section page — /thunderbay/realEstate. Daily aggregated listings from Kijiji + other sources alongside user transfer / sublet posts.
Post detail — long-form local guide with likes, saves, comments, and you-might-also-like recommendations.
Post detail — long-form local guide with likes, saves, comments, and you-might-also-like recommendations.
GEO landing — /landing/thunderbay-rent. Rent medians, district picks, Ontario lease rules, FAQ schema for AI search engines.
GEO landing — /landing/thunderbay-rent. Rent medians, district picks, Ontario lease rules, FAQ schema for AI search engines.
Mobile — dedicated top bar + bottom navigation.
Mobile — dedicated top bar + bottom navigation.

Approach

City-aware namespace, not a tag

Every surface is scoped by city: /[city]/[section]. Thunder Bay rentals and Sudbury rentals are different feeds, different SEO targets, different landing pages. A post in one city doesn’t contaminate the other, and a query like “rent in Thunder Bay” lands on a real page instead of a generic national one that ranks for nothing.

Daily aggregation keeps the feed alive before critical mass

The hard part of any community product is the cold-start problem — empty feeds kill retention. lakebbs avoids it by running a set of daily aggregation bots on the private side:

  • Housing digest — Kijiji + other sources, scraped, deduped, surfaced into /[city]/realEstate.
  • Job digest — LinkedIn / Kijiji / City of Thunder Bay postings.
  • IRCC immigration briefing — daily Canada immigration updates summarized in Chinese.
  • r/ThunderBay digest — hot Reddit threads translated with selected comment highlights.

This turns lakebbs into a “daily-open” surface, not a request-response forum. Even with a small user base, the page has fresh content every morning.

GEO landing pages — built for AI search, not just Google

Dedicated landing pages (/landing/thunderbay-rent, immigration, RNIP, Lakehead, etc.) turn local knowledge into long-lived discovery assets. The product treats Generative Engine Optimization as a first-class concern alongside classical SEO:

  • Dynamic sitemap.xml covering root, cities, sections, landings, and the newest 5K posts (1-hour revalidation).
  • Dynamic robots.txt with an explicit AI-crawler allowlist plus a Bytespider crawl-delay.
  • Structured data + OG + FAQ schema on landing pages.
  • Admin-side GEO readiness checklist and citation tracking — measuring whether the site is actually being picked up by AI search.

This is where the product gets defensible: Google indexing a city forum is normal; being one of the cited sources when someone asks an LLM “where should I rent in Thunder Bay” is not.

Stack rewrite (kept the database)

lakebbs started on Vue 2 + Express + Sequelize. It’s now been migrated to a modern TypeScript stack — without a database migration. The MySQL 5.7 schema stayed; Drizzle ORM was pointed at it as-is.

LayerNow
FrameworkNext.js 16 (App Router)
UIReact 19, Server Components + Server Actions
StylingTailwind 4, Base UI, shadcn
DataDrizzle ORM over existing MySQL 5.7
AuthNextAuth v5 (credentials + session)
Deploysystemd lakebbs-next.service on :3477, nginx in front

Keeping the database fixed during a stack rewrite is the boring-but-correct decision — it means the migration is a code project, not a data project. Live cutover happened with zero schema change and zero user-visible downtime beyond a nginx reload.

Product surface

  • City-aware forum with image galleries and 6 standard categories (新鲜事 / 资讯 / 二手 / 房产 / 汽车 / 工作移民)
  • Long-form local guides alongside short threads (housing-area picks, immigration playbooks, licensing notes)
  • Likes, saves, comments (latest / hottest / oldest), recommended posts at the bottom of every detail page
  • 1:1 private messaging, conversation actions (block, archive), in-app notifications
  • User profiles with follow / unfollow / blocklist
  • Full-text search across the site
  • Admin dashboards: geographic audience distribution, per-bot AI-crawler activity (PV, last-seen, purpose), GEO readiness signals

Results & What I Learned

Live in production at lakebbs.ca, serving Thunder Bay, Sudbury, and other Northern Ontario cities.

The thing I underestimated: how much of a community product is just keeping the page alive on day-zero. A forum with empty categories looks dead even if the design is good. The daily aggregation pipeline was originally framed as “nice-to-have content automation” — it turned out to be the load-bearing component. Without it, the SEO landing pages have nowhere to link to and returning users see the same posts they saw yesterday.

The second thing: city-aware namespacing is the single most defensible architectural decision. Tags lose. A user landing on a Toronto-flavored query and finding a Thunder Bay forum will bounce. A user landing on /thunderbay/realEstate finds exactly the content they came for. SEO and product UX point the same direction here.

The stack rewrite was also a lesson in scope. The temptation was to redesign the schema while I was rewriting the app — “as long as we’re rebuilding, let’s fix everything.” I didn’t. Kept MySQL 5.7, kept the table shapes, pointed Drizzle at them, and only modernized the layers that actually needed it. The migration shipped in weeks instead of months.

Stack: Next.js 16 · React 19 · TypeScript · Drizzle ORM · MySQL 5.7 · NextAuth v5 · Tailwind 4 · Base UI · shadcn

Live: lakebbs.ca

Public overview: github.com/YSKM523/lakebbs-ca (private implementation kept separate)