[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"header-categories":3,"post-securing-nodejs-api-production":73,"parsed-post-6320ca43-cf52-48c2-8885-b09786e78cb8":148},{"success":4,"data":5},true,{"items":6,"pagination":70},[7,16,24,32,40,48,56,63],{"id":8,"name":9,"description":10,"parentId":11,"createdAt":12,"updatedAt":12,"parent":11,"children":13,"_count":14},"ai-future","AI & The Future","Artificial intelligence and emerging technology",null,"2026-07-24T06:02:45.990Z",[],{"posts":15},1,{"id":17,"name":18,"description":19,"parentId":11,"createdAt":20,"updatedAt":20,"parent":11,"children":21,"_count":22},"tools-productivity","Tools & Productivity","Developer tools and workflow optimization","2026-07-24T06:02:45.712Z",[],{"posts":23},7,{"id":25,"name":26,"description":27,"parentId":11,"createdAt":28,"updatedAt":28,"parent":11,"children":29,"_count":30},"career","Career & Growth","Professional development for developers","2026-07-24T06:02:45.486Z",[],{"posts":31},3,{"id":33,"name":34,"description":35,"parentId":11,"createdAt":36,"updatedAt":36,"parent":11,"children":37,"_count":38},"opinion","Opinion & Analysis","Industry analysis and technical opinions","2026-07-24T06:02:45.272Z",[],{"posts":39},6,{"id":41,"name":42,"description":43,"parentId":11,"createdAt":44,"updatedAt":44,"parent":11,"children":45,"_count":46},"tutorials","Tutorials","Step-by-step learning guides","2026-07-24T06:02:44.965Z",[],{"posts":47},21,{"id":49,"name":50,"description":51,"parentId":11,"createdAt":52,"updatedAt":52,"parent":11,"children":53,"_count":54},"software-engineering","Software Engineering","Best practices, patterns, and principles","2026-07-24T06:02:44.689Z",[],{"posts":55},24,{"id":57,"name":58,"description":59,"parentId":11,"createdAt":60,"updatedAt":60,"parent":11,"children":61,"_count":62},"devops-cloud","DevOps & Cloud","Infrastructure, deployment, and cloud computing","2026-07-24T06:02:44.453Z",[],{"posts":39},{"id":64,"name":65,"description":66,"parentId":11,"createdAt":67,"updatedAt":67,"parent":11,"children":68,"_count":69},"web-development","Web Development","Frontend and backend web development tutorials and guides","2026-07-24T06:02:44.169Z",[],{"posts":55},{"page":15,"limit":71,"total":71,"totalPages":15,"hasNext":72,"hasPrev":72},8,false,{"success":4,"data":74},{"id":75,"slug":76,"title":77,"excerpt":78,"content":79,"status":80,"visibility":81,"featuredImage":82,"canonicalUrl":11,"readingTime":83,"viewCount":84,"commentEnabled":4,"publishedAt":85,"scheduledAt":11,"createdAt":86,"updatedAt":87,"seoTitle":88,"seoDescription":78,"seoKeywords":11,"authorId":89,"author":90,"coAuthors":95,"tags":96,"categories":112,"comments":115,"_count":116,"relatedPosts":118},"6320ca43-cf52-48c2-8885-b09786e78cb8","securing-nodejs-api-production","Securing Your Node.js API: A Checklist for Production","A comprehensive security checklist covering authentication, input validation, rate limiting, and common vulnerabilities in Node.js APIs.","## Security Is Not Optional\n\nEvery API deployed to production is a target. This isn't about paranoia — it's about building habits that prevent breaches.\n\n## 1. Input Validation\n\nNever trust user input. Validate everything at the boundary:\n\n```typescript\nimport { z } from 'zod'\n\nconst UserInputSchema = z.object({\n  email: z.string().email().max(254),\n  name: z.string().min(1).max(100).trim(),\n  age: z.number().int().min(13).max(150),\n})\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event)\n  const validated = UserInputSchema.parse(body) \u002F\u002F Throws on invalid input\n  \u002F\u002F Use validated data — it's safe\n})\n```\n\n## 2. Authentication & Authorization\n\n```typescript\n\u002F\u002F Middleware: verify JWT on protected routes\nexport default defineEventHandler(async (event) => {\n  const token = getHeader(event, 'Authorization')?.replace('Bearer ', '')\n  if (!token) throw createError({ statusCode: 401, message: 'Unauthorized' })\n\n  try {\n    const payload = verifyJWT(token)\n    event.context.user = payload\n  } catch {\n    throw createError({ statusCode: 401, message: 'Invalid token' })\n  }\n})\n```\n\n::callout{icon=\"i-lucide-alert-triangle\" color=\"warning\"}\nAuthentication answers \"who are you?\" — Authorization answers \"what can you do?\" Both are required. Never skip authorization checks just because a user is authenticated.\n::\n\n## 3. Rate Limiting\n\n```typescript\n\u002F\u002F Simple in-memory rate limiter (use Redis for production clusters)\nconst requests = new Map\u003Cstring, { count: number; resetAt: number }>()\n\nfunction rateLimit(ip: string, limit = 100, windowMs = 60000): boolean {\n  const now = Date.now()\n  const record = requests.get(ip)\n\n  if (!record || now > record.resetAt) {\n    requests.set(ip, { count: 1, resetAt: now + windowMs })\n    return true\n  }\n\n  if (record.count >= limit) return false\n  record.count++\n  return true\n}\n```\n\n## 4. SQL Injection Prevention\n\nAlways use parameterized queries (Prisma does this by default):\n\n```typescript\n\u002F\u002F ❌ NEVER do this\nconst user = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE id = '${userId}'`)\n\n\u002F\u002F ✅ Always use parameterized queries\nconst user = await prisma.user.findUnique({ where: { id: userId } })\n```\n\n## 5. Security Headers\n\n```typescript\n\u002F\u002F server\u002Fmiddleware\u002Fsecurity-headers.ts\nexport default defineEventHandler((event) => {\n  setHeaders(event, {\n    'X-Content-Type-Options': 'nosniff',\n    'X-Frame-Options': 'DENY',\n    'X-XSS-Protection': '1; mode=block',\n    'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',\n    'Content-Security-Policy': \"default-src 'self'\",\n  })\n})\n```\n\n## 6. Environment Variables\n\n- Never commit secrets to git\n- Use different secrets per environment\n- Rotate secrets periodically\n- Audit access to secret stores\n\n## The Checklist\n\n- [ ] All inputs validated with strict schemas\n- [ ] Authentication on every protected route\n- [ ] Authorization checks at the resource level\n- [ ] Rate limiting on public endpoints\n- [ ] Security headers configured\n- [ ] HTTPS enforced\n- [ ] Dependencies audited regularly (`pnpm audit`)\n- [ ] Error messages don't leak internals\n","published","public","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1526374965328-7f61d4dc18c5?w=1200&q=80",11,2148,"2026-07-07T06:03:06.469Z","2026-07-24T06:03:06.472Z","2026-07-28T17:08:15.465Z","Securing Your Node.js API: A Checklist for Production | BitBlog","fddb5d93-7a2c-4d86-a06a-fa32e73a01c6",{"email":91,"bio":92,"id":89,"name":93,"avatarUrl":94},"mbeahessilfieprince@gmail.com","Fullstack Software Developer ","Mbeah Essilfie","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F93322394?v=4",[],[97,102,107],{"id":98,"name":99,"color":100,"description":101},"nodejs","Node.js","#339933","JavaScript runtime built on V8",{"id":103,"name":104,"color":105,"description":106},"security","Security","#d63031","Web security best practices",{"id":108,"name":109,"color":110,"description":111},"api-design","API Design","#009688","RESTful and GraphQL API patterns",[113,114],{"id":64,"name":65,"description":66},{"id":49,"name":50,"description":51},[],{"comments":117},0,[119,128,138],{"id":120,"slug":121,"title":122,"excerpt":123,"featuredImage":124,"viewCount":125,"readingTime":71,"publishedAt":126,"author":127},"2997028f-4d22-4eb7-9d86-894a54cb559a","building-type-safe-apis-nuxt3-prisma","Building Type-Safe APIs with Nuxt 3 and Prisma","Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1498050108023-c5249f4df085?w=1200&q=80",1922,"2026-07-23T06:02:46.910Z",{"id":89,"name":93,"avatarUrl":94},{"id":129,"slug":130,"title":131,"excerpt":132,"featuredImage":133,"viewCount":134,"readingTime":135,"publishedAt":136,"author":137},"da9e4553-8b0d-411e-b4ec-f9684017e163","mastering-tailwind-css-design-systems","Mastering Tailwind CSS: From Utility Classes to Design Systems","Go beyond basic utility classes and learn how to build cohesive, maintainable design systems with Tailwind CSS.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555066931-4365d14bab8c?w=1200&q=80",257,10,"2026-07-21T06:02:49.268Z",{"id":89,"name":93,"avatarUrl":94},{"id":139,"slug":140,"title":141,"excerpt":142,"featuredImage":143,"viewCount":144,"readingTime":145,"publishedAt":146,"author":147},"50add5fc-4026-4267-b917-7f71ea9e35b0","complete-guide-vue3-composables","The Complete Guide to Vue 3 Composables","Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1517694712202-14dd9538aa97?w=1200&q=80",990,12,"2026-07-19T06:02:51.753Z",{"id":89,"name":93,"avatarUrl":94},{"data":149,"body":151,"toc":395},{"title":150,"description":150},"",{"type":152,"children":153},"root",[154,163,169,175,180,193,199,207,218,224,232,238,243,251,257,265,271,296,302,390],{"type":155,"tag":156,"props":157,"children":159},"element","h2",{"id":158},"security-is-not-optional",[160],{"type":161,"value":162},"text","Security Is Not Optional",{"type":155,"tag":164,"props":165,"children":166},"p",{},[167],{"type":161,"value":168},"Every API deployed to production is a target. This isn't about paranoia — it's about building habits that prevent breaches.",{"type":155,"tag":156,"props":170,"children":172},{"id":171},"_1-input-validation",[173],{"type":161,"value":174},"1. Input Validation",{"type":155,"tag":164,"props":176,"children":177},{},[178],{"type":161,"value":179},"Never trust user input. Validate everything at the boundary:",{"type":155,"tag":181,"props":182,"children":187},"pre",{"className":183,"code":184,"language":185,"meta":150,"style":186},"language-typescript","import { z } from 'zod'\n\nconst UserInputSchema = z.object({\n  email: z.string().email().max(254),\n  name: z.string().min(1).max(100).trim(),\n  age: z.number().int().min(13).max(150),\n})\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event)\n  const validated = UserInputSchema.parse(body) \u002F\u002F Throws on invalid input\n  \u002F\u002F Use validated data — it's safe\n})\n","typescript","undefined",[188],{"type":155,"tag":189,"props":190,"children":191},"code",{"__ignoreMap":150},[192],{"type":161,"value":184},{"type":155,"tag":156,"props":194,"children":196},{"id":195},"_2-authentication-authorization",[197],{"type":161,"value":198},"2. Authentication & Authorization",{"type":155,"tag":181,"props":200,"children":202},{"className":183,"code":201,"language":185,"meta":150,"style":186},"\u002F\u002F Middleware: verify JWT on protected routes\nexport default defineEventHandler(async (event) => {\n  const token = getHeader(event, 'Authorization')?.replace('Bearer ', '')\n  if (!token) throw createError({ statusCode: 401, message: 'Unauthorized' })\n\n  try {\n    const payload = verifyJWT(token)\n    event.context.user = payload\n  } catch {\n    throw createError({ statusCode: 401, message: 'Invalid token' })\n  }\n})\n",[203],{"type":155,"tag":189,"props":204,"children":205},{"__ignoreMap":150},[206],{"type":161,"value":201},{"type":155,"tag":208,"props":209,"children":212},"callout",{"color":210,"icon":211},"warning","i-lucide-alert-triangle",[213],{"type":155,"tag":164,"props":214,"children":215},{},[216],{"type":161,"value":217},"Authentication answers \"who are you?\" — Authorization answers \"what can you do?\" Both are required. Never skip authorization checks just because a user is authenticated.",{"type":155,"tag":156,"props":219,"children":221},{"id":220},"_3-rate-limiting",[222],{"type":161,"value":223},"3. Rate Limiting",{"type":155,"tag":181,"props":225,"children":227},{"className":183,"code":226,"language":185,"meta":150,"style":186},"\u002F\u002F Simple in-memory rate limiter (use Redis for production clusters)\nconst requests = new Map\u003Cstring, { count: number; resetAt: number }>()\n\nfunction rateLimit(ip: string, limit = 100, windowMs = 60000): boolean {\n  const now = Date.now()\n  const record = requests.get(ip)\n\n  if (!record || now > record.resetAt) {\n    requests.set(ip, { count: 1, resetAt: now + windowMs })\n    return true\n  }\n\n  if (record.count >= limit) return false\n  record.count++\n  return true\n}\n",[228],{"type":155,"tag":189,"props":229,"children":230},{"__ignoreMap":150},[231],{"type":161,"value":226},{"type":155,"tag":156,"props":233,"children":235},{"id":234},"_4-sql-injection-prevention",[236],{"type":161,"value":237},"4. SQL Injection Prevention",{"type":155,"tag":164,"props":239,"children":240},{},[241],{"type":161,"value":242},"Always use parameterized queries (Prisma does this by default):",{"type":155,"tag":181,"props":244,"children":246},{"className":183,"code":245,"language":185,"meta":150,"style":186},"\u002F\u002F ❌ NEVER do this\nconst user = await prisma.$queryRawUnsafe(`SELECT * FROM users WHERE id = '${userId}'`)\n\n\u002F\u002F ✅ Always use parameterized queries\nconst user = await prisma.user.findUnique({ where: { id: userId } })\n",[247],{"type":155,"tag":189,"props":248,"children":249},{"__ignoreMap":150},[250],{"type":161,"value":245},{"type":155,"tag":156,"props":252,"children":254},{"id":253},"_5-security-headers",[255],{"type":161,"value":256},"5. Security Headers",{"type":155,"tag":181,"props":258,"children":260},{"className":183,"code":259,"language":185,"meta":150,"style":186},"\u002F\u002F server\u002Fmiddleware\u002Fsecurity-headers.ts\nexport default defineEventHandler((event) => {\n  setHeaders(event, {\n    'X-Content-Type-Options': 'nosniff',\n    'X-Frame-Options': 'DENY',\n    'X-XSS-Protection': '1; mode=block',\n    'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',\n    'Content-Security-Policy': \"default-src 'self'\",\n  })\n})\n",[261],{"type":155,"tag":189,"props":262,"children":263},{"__ignoreMap":150},[264],{"type":161,"value":259},{"type":155,"tag":156,"props":266,"children":268},{"id":267},"_6-environment-variables",[269],{"type":161,"value":270},"6. Environment Variables",{"type":155,"tag":272,"props":273,"children":274},"ul",{},[275,281,286,291],{"type":155,"tag":276,"props":277,"children":278},"li",{},[279],{"type":161,"value":280},"Never commit secrets to git",{"type":155,"tag":276,"props":282,"children":283},{},[284],{"type":161,"value":285},"Use different secrets per environment",{"type":155,"tag":276,"props":287,"children":288},{},[289],{"type":161,"value":290},"Rotate secrets periodically",{"type":155,"tag":276,"props":292,"children":293},{},[294],{"type":161,"value":295},"Audit access to secret stores",{"type":155,"tag":156,"props":297,"children":299},{"id":298},"the-checklist",[300],{"type":161,"value":301},"The Checklist",{"type":155,"tag":272,"props":303,"children":306},{"className":304},[305],"contains-task-list",[307,319,328,337,346,355,364,381],{"type":155,"tag":276,"props":308,"children":311},{"className":309},[310],"task-list-item",[312,317],{"type":155,"tag":313,"props":314,"children":316},"input",{"disabled":4,"type":315},"checkbox",[],{"type":161,"value":318}," All inputs validated with strict schemas",{"type":155,"tag":276,"props":320,"children":322},{"className":321},[310],[323,326],{"type":155,"tag":313,"props":324,"children":325},{"disabled":4,"type":315},[],{"type":161,"value":327}," Authentication on every protected route",{"type":155,"tag":276,"props":329,"children":331},{"className":330},[310],[332,335],{"type":155,"tag":313,"props":333,"children":334},{"disabled":4,"type":315},[],{"type":161,"value":336}," Authorization checks at the resource level",{"type":155,"tag":276,"props":338,"children":340},{"className":339},[310],[341,344],{"type":155,"tag":313,"props":342,"children":343},{"disabled":4,"type":315},[],{"type":161,"value":345}," Rate limiting on public endpoints",{"type":155,"tag":276,"props":347,"children":349},{"className":348},[310],[350,353],{"type":155,"tag":313,"props":351,"children":352},{"disabled":4,"type":315},[],{"type":161,"value":354}," Security headers configured",{"type":155,"tag":276,"props":356,"children":358},{"className":357},[310],[359,362],{"type":155,"tag":313,"props":360,"children":361},{"disabled":4,"type":315},[],{"type":161,"value":363}," HTTPS enforced",{"type":155,"tag":276,"props":365,"children":367},{"className":366},[310],[368,371,373,379],{"type":155,"tag":313,"props":369,"children":370},{"disabled":4,"type":315},[],{"type":161,"value":372}," Dependencies audited regularly (",{"type":155,"tag":189,"props":374,"children":376},{"className":375},[],[377],{"type":161,"value":378},"pnpm audit",{"type":161,"value":380},")",{"type":155,"tag":276,"props":382,"children":384},{"className":383},[310],[385,388],{"type":155,"tag":313,"props":386,"children":387},{"disabled":4,"type":315},[],{"type":161,"value":389}," Error messages don't leak internals",{"type":155,"tag":391,"props":392,"children":393},"style",{},[394],{"type":161,"value":150},{"title":150,"searchDepth":396,"depth":396,"links":397},2,[398,399,400,401,402,403,404,405],{"id":158,"depth":396,"text":162},{"id":171,"depth":396,"text":174},{"id":195,"depth":396,"text":198},{"id":220,"depth":396,"text":223},{"id":234,"depth":396,"text":237},{"id":253,"depth":396,"text":256},{"id":267,"depth":396,"text":270},{"id":298,"depth":396,"text":301}]