
How I Built Google OAuth for a Real SaaS (Next.js + Auth.js) — CreatorCopilot Part 2
Learn how to implement production-ready Google OAuth in Next.js using Auth.js (NextAuth v5) with App...

Most developers think their APIs will only be used by their frontend.
That assumption breaks the moment your project becomes public.
The internet is full of:
• scraping bots
• spam scripts
• vulnerability scanners
• traffic floods
If you don’t protect your API, a simple script can hit your endpoint thousands of times per minute and destroy your server or database.
This article shows the practical protection stack I use in most projects.
The goal is simple:
• block abusive traffic
• protect database resources
• keep legitimate users unaffected
A bot attack often looks like this:
while(true){
fetch("/api/posts")
}One user can generate thousands of requests per minute.
If your API performs database queries on every request, this can easily overload your system.
Protection requires multiple layers.
Rate limiting restricts how many requests a client can make in a time window.
Example rule:
• 100 requests per minute per IP
If the client exceeds the limit, the API returns HTTP 429.
429 Too Many RequestsBelow is a simple Node.js implementation using an in‑memory store.
const rateLimitMap = new Map()export function rateLimit(ip) {
const now = Date.now()
const window = 60 * 1000 if (!rateLimitMap.has(ip)) {
rateLimitMap.set(ip, { count: 1, start: now })
return true
} const data = rateLimitMap.get(ip) if (now - data.start > window) {
rateLimitMap.set(ip, { count: 1, start: now })
return true
} if (data.count > 100) {
return false
} data.count++
return true
}Usage in an API route:
export async function GET(req) {
const ip = req.headers.get("x-forwarded-for") || "unknown" const allowed = rateLimit(ip) if (!allowed) {
return new Response("Too many requests", { status: 429 })
} return Response.json({ message: "success" })
}For production systems this logic is usually stored in Redis.
Bots often fire requests extremely fast.
Adding a small delay after repeated requests makes automated abuse inefficient.
Example:
function sleep(ms) {
return new Promise(r => setTimeout(r, ms))
}if(data.count > 50){
await sleep(1000)
}Legitimate users will never notice this delay.
Bots will.
Many bots identify themselves in the User‑Agent header.
Example block list:
const blockedAgents = [
"curl",
"python-requests",
"scrapy",
"bot",
"crawler"
]function isBot(agent){
if(!agent) return false agent = agent.toLowerCase() return blockedAgents.some(b => agent.includes(b))
}Use it in your API:
const agent = req.headers.get("user-agent")if(isBot(agent)){
return new Response("blocked", {status:403})
}This blocks many automated scripts immediately.
If your endpoint only reads data, caching can drastically reduce load.
Example pattern:
API request
↓
Cache check
↓
DatabaseIf the data exists in cache, the database is never touched.
This protects your backend during traffic spikes.
Some endpoints are computationally heavy.
Examples:
• search
• AI generation
• file uploads
These routes should have stricter limits.
Example:
search endpoint → 20 requests per minute
standard endpoint → 100 requests per minuteTracking suspicious traffic helps identify patterns.
Example logging:
console.log({
ip,
path:req.url,
agent:req.headers.get("user-agent"),
time:new Date().toISOString()
})You can later block repeating offenders.
A protected API typically looks like this:
Client
↓
Bot detection
↓
Rate limiter
↓
Cache
↓
API logic
↓
DatabaseEach layer reduces the load before requests reach the database.
These protections are critical for:
• public APIs
• SaaS products
• AI endpoints
• search endpoints
• content platforms
Without protection, a small script can easily overload your infrastructure.
Most API outages are not caused by real users.
They are caused by uncontrolled automated traffic.
A few simple protection layers can prevent your backend from being overwhelmed while keeping the experience smooth for legitimate users.
🔥 Found this blog post helpful? 🔥
If you enjoyed this article and found it valuable, please show your support by clapping 👏 and subscribing to my blog for more in-depth insights on web development and Next.js!
Subscribe here: click me
🚀 Follow me on:
🌐 Website: sagarsangwan.dev
🐦 Twitter/X: @sagar sangwan
🔗 LinkedIn: Sagar Sangwan
📸 Instagram: @codingbysagar
▶️YouTube: @codingbysagar
Your encouragement helps me continue creating high-quality content that can assist you on your development journey. 🚀

Code. Write. Build. Explore. 💻✍️ Software developer by day, mechanical tinkerer by night. When I’m not shipping code or writing blogs, you’ll find me trekking up a mountain, whipping up a feast, or hitting the open road on two wheels. Life is better in high gear.
View more blogs by me CLICK HERE

Learn how to implement production-ready Google OAuth in Next.js using Auth.js (NextAuth v5) with App...

A practical guide to implementing a production-ready SEO stack in a Next.js blog. Learn how to add J...

Learn how to build an email queue system in Next.js with Drizzle, Postgres, and Brevo SMTP including...
Subscribe to get the latest posts delivered to your inbox