Rate Limiting Isn't One Layer: What LogicVisor and Titan Actually Do Differently A developer explains that rate limiting is not a single layer but three distinct layers, each defending against different failure modes. They describe how LogicVisor, a public AI tool, uses a stack of checks before spending AI tokens, while Titan, a payments platform, relies on NestJS Throttler with Redis for a flat global limit. The distinction highlights the importance of choosing the right strategy based on the cost of abuse. "Add rate limiting" sounds like one task. It's actually three separate layers, each defending against a different failure mode, with different trade-offs and different amounts of trust you can place in them. I only understood the distinction properly once I had to pick a strategy for two systems solving different problems: LogicVisor a public AI tool anyone can hit anonymously and Titan a payments platform where the cost of a bad actor is different from the cost of someone burning through free AI credits . Debouncing a search input, greying out a submit button after the first click, backing off exponentially after a 429. All of this makes an app feel considerate. None of it stops anyone. A malicious actor skips your JavaScript entirely and hits the endpoint directly with curl. Client-side limiting is worth doing it saves you real traffic and prevents accidental double-submits , but it is not a security control. If it's the only thing standing between your API and abuse, you don't have rate limiting, you have a polite suggestion. This is where LogicVisor and Titan diverge, because they're not defending against the same thing. LogicVisor: several checks before a single AI token gets spent LogicVisor is public. Anyone gets 3 free code reviews with no signup, which means the abuse surface is wide open by design. The submission route runs a stack of checks before it ever calls Gemini or Groq, because every AI call costs real money: js // 1. Check if this exact code has already been reviewed by this model const cachedReview = await getCachedAIReview preferred model.id + "-" + canonicalHash ; if cachedReview { return NextResponse.json { success: true, data: cachedReview }, { status: 201 } ; } // 2. Enforce the actual rate limit IP + session based for anon users const rateLimitResult = await enforceAIRateLimit user.id, request ; // 3. Slow down premium users intelligently instead of hard-blocking them const throttleDelay = await getThrottleDelay user.id ; if throttleDelay 0 { await new Promise resolve = setTimeout resolve, throttleDelay ; } // 4. Under heavy load, degrade gracefully instead of rejecting outright const shouldDegrade = await shouldGracefullyDegrade user.id, "ai request" ; if shouldDegrade { // return a basic, non-AI response instead of a 429 } A few things worth naming separately, because they get lumped together under "rate limiting" but aren't the same mechanism: None of this is a single algorithm out of a textbook. It's several cheap, layered checks, ordered so the most expensive resource the AI call is the last thing hit, not the first. Titan: NestJS Throttler backed by Redis Titan's rate limiting is intentionally boring by comparison, and that's the correct choice for what it is. It's @nestjs/throttler wired up as a global guard, with Redis swapped in as the storage backend instead of the default in-memory store: ThrottlerModule.forRootAsync { imports: ConfigModule , inject: ConfigService , useFactory: configService: ConfigService = { throttlers: { ttl: 60000, limit: 10, }, , storage: new ThrottlerStorageRedisService configService.get