Skip to main content
Ashish KumarSDET II
All projects

Food Recommendations

A no-login food and dish recommendation board, built and load-hardened like a real product.

Next.js · TypeScript · Prisma · PostgreSQL · Redis · Sentry

Results

30
Playwright cases — happy path, admin, validation, a11y, SEO/errors
Redis
rate limiting that actually holds across serverless instances
Daily
scheduled CI run, independent of whether anything changed that day

Problem

A recommendation board only works if anyone can post without friction, but "no accounts" creates real problems the moment it's live: who can edit a post, what stops one person from spamming submissions or likes, and what happens when something breaks with no user account to trace it back to.

Approach

  • Ownership without accounts: each submission gets a secret edit token at creation time, so the original poster's browser can edit or delete it later with no login — moderation (reports, an admin panel to review and dismiss them) covers the rest.
  • Rate limiting on submissions, likes, comments, and reports via Upstash Redis. It replaced an in-memory limiter that only worked per warm serverless instance — on Vercel that was close to no protection at all, since the next request can land on a different instance with no shared memory.
  • Sentry wired into both the server and edge runtimes, so a failure in either surfaces with a real stack trace instead of a silent 500 a user just bounces off.
  • Postgres via Prisma, with a trigram-search migration for the browse/search page, and a 30-case Playwright suite (happy path, submission validation, admin auth and delete, filtering/pagination, accessibility, SEO/error pages) run in CI on every push and on a daily schedule.

From the code

lib/rateLimit.ts
Upstash Redis
// Redis-backed so limits actually hold across Vercel's ephemeral serverless
// instances — the previous in-memory Map only limited requests hitting the
// same warm instance, which on serverless is close to no protection at all.
export async function isRateLimited(key: string, limit: number, windowMs: number): Promise<boolean> {
  const { success } = await getLimiter(limit, windowMs).limit(key);
  return !success;
}

Outcome

A live, publicly usable app that handles the unglamorous parts of "no login required" honestly — abuse prevention and ownership — instead of skipping them because there's no user table to hang them off of.

Business Impact

The same instinct I bring to test strategy — what actually needs to hold up under real, unauthenticated traffic — applied to building the thing, not just testing it.