[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"navigation":3,"index":58,"index-blogs":197},[4],{"title":5,"path":6,"stem":7,"children":8,"page":57},"Blog","\u002Fblog","blog",[9,13,17,21,25,29,33,37,41,45,49,53],{"title":10,"path":11,"stem":12},"A Guide to Server-Side Rendering (SSR) with Vite and React.js","\u002Fblog\u002Fa-guide-to-server-side-rendering-ssr-with-vite-and-reactjs","blog\u002Fa-guide-to-server-side-rendering-ssr-with-vite-and-reactjs",{"title":14,"path":15,"stem":16},"🔄 Background DNS Refresh in Node.js: Keeping Cached IPs Updated","\u002Fblog\u002Fbackground-dns-refresh-in-nodejs-keeping-cached-ips-updated","blog\u002Fbackground-dns-refresh-in-nodejs-keeping-cached-ips-updated",{"title":18,"path":19,"stem":20},"Comprehensive Guide: Encrypting Images, Uploading to Cloudinary, and Serving Them Securely","\u002Fblog\u002Fcomprehensive-guide-encrypting-images-uploading-to-cloudinary-and-serving-them-securely","blog\u002Fcomprehensive-guide-encrypting-images-uploading-to-cloudinary-and-serving-them-securely",{"title":22,"path":23,"stem":24},"Comprehensive Guide to Using Uploadthing with Express and Typescript","\u002Fblog\u002Fcomprehensive-guide-to-using-uploadthing-with-express-and-typescript","blog\u002Fcomprehensive-guide-to-using-uploadthing-with-express-and-typescript",{"title":26,"path":27,"stem":28},"🌐 DNS Caching in Node.js: Supercharge Your App's Speed & Reliability 🚀","\u002Fblog\u002Fdns-caching-in-nodejs-supercharge-your-apps-speed-and-reliability","blog\u002Fdns-caching-in-nodejs-supercharge-your-apps-speed-and-reliability",{"title":30,"path":31,"stem":32},"Enhancing Redis for Message Queues: Using External Databases for Payload Storage","\u002Fblog\u002Fenhancing-redis-for-message-queues-using-external-databases-for-payload-storage","blog\u002Fenhancing-redis-for-message-queues-using-external-databases-for-payload-storage",{"title":34,"path":35,"stem":36},"Full Stack Learning Management System (LMS) with MERN Stack: A Complete Guide","\u002Fblog\u002Ffull-stack-learning-management-system-lms-with-mern-stack-a-complete-guide","blog\u002Ffull-stack-learning-management-system-lms-with-mern-stack-a-complete-guide",{"title":38,"path":39,"stem":40},"Getting Started with Redis: Elevating Your Web Applications","\u002Fblog\u002Fgetting-started-with-redis-elevating-your-web-applications","blog\u002Fgetting-started-with-redis-elevating-your-web-applications",{"title":42,"path":43,"stem":44},"How Queues Enhance Your Application Development Process","\u002Fblog\u002Fhow-queues-enhance-your-application-development-process","blog\u002Fhow-queues-enhance-your-application-development-process",{"title":46,"path":47,"stem":48},"Next-Gen E-Learning: Transforming Education for Students and Creators","\u002Fblog\u002Fnext-gen-e-learning-transforming-education-for-students-and-creators","blog\u002Fnext-gen-e-learning-transforming-education-for-students-and-creators",{"title":50,"path":51,"stem":52},"Single Sign-On (SSO) Implementation Using Passport.js in TypeScript","\u002Fblog\u002Fsingle-sign-on-sso-implementation-using-passportjs-in-typescript","blog\u002Fsingle-sign-on-sso-implementation-using-passportjs-in-typescript",{"title":54,"path":55,"stem":56},"Using Multer for Local File Uploads: Benefits, Trade-offs, and Cloud Transfer Integration","\u002Fblog\u002Fusing-multer-for-local-file-uploads-benefits-trade-offs-and-cloud-transfer-integration","blog\u002Fusing-multer-for-local-file-uploads-benefits-trade-offs-and-cloud-transfer-integration",false,{"id":59,"title":60,"about":61,"blog":64,"body":67,"description":68,"experience":69,"extension":95,"faq":96,"hero":135,"meta":164,"navigation":165,"path":166,"seo":167,"stem":170,"testimonials":171,"__hash__":196},"index\u002Findex.yml","Hey, I'm Sachin Thapa",{"title":62,"description":63},"About Me","As a Full-Stack Developer with a Bachelor’s degree in Bsc CSIT, I build scalable and reliable digital products with a strong focus on clean architecture, performance, and thoughtful engineering. My approach blends creative problem-solving with technical expertise, transforming ideas into functional, purposeful digital products that seamlessly integrate technology, usability, and simplicity.\n",{"title":65,"description":66},"Latest Articles","Articles on backend development, Node.js, TypeScript, and web technologies — things I'm building and learning along the way.",null,"I build thoughtful digital experiences where engineering meets simplicity, turning complex ideas into reliable, meaningful products.",{"title":70,"items":71},"Work Experience",[72,80,88],{"position":73,"date":74,"company":75},"Freelancing at","2023 - Present",{"name":76,"logo":77,"url":78,"color":79},"Fiverr","i-simple-icons-fiverr","https:\u002F\u002Fwww.fiverr.com","#1DBF73",{"position":81,"date":82,"company":83},"Backend Developer at","2020 - 2023",{"name":84,"logo":85,"url":86,"color":87},"EMS Solution","i-lucide-server","#","#2563EB",{"position":89,"date":90,"company":91},"Intern at","2019 - 2020",{"name":92,"logo":93,"url":86,"color":94},"Lumbini Technologies","i-lucide-graduation-cap","#F97316","yml",{"title":97,"description":98,"categories":99},"Frequently Asked Questions","Answers to common questions about my process and services.",[100,112,127],{"title":101,"questions":102},"Services & Process",[103,106,109],{"label":104,"content":105},"What services do you offer?","\u003Cp>I build and maintain the backend of web applications — REST APIs, databases, authentication, third-party integrations, and full-stack features with Vue.js\u002FNuxt.js. I focus on clean, reliable, and scalable code.\u003C\u002Fp>",{"label":107,"content":108},"What is your development process like?","\u003Cp>I start by understanding your problem, design the architecture, then build incrementally while keeping you updated at every step. Code is tested, documented, and delivered ready to deploy.\u003C\u002Fp>",{"label":110,"content":111},"Do you work with startups?","\u003Cp>Yes! I enjoy helping startups move fast — from building an MVP backend to scaling an existing system.\u003C\u002Fp>",{"title":113,"questions":114},"Pricing & Timelines",[115,118,121,124],{"label":116,"content":117},"How much does a project typically cost?","\u003Cp>It depends on scope and complexity. Backend\u002Ffull-stack projects typically start around $500 and range up to $5,000. I'll give you a fixed quote after a quick discovery call.\u003C\u002Fp>",{"label":119,"content":120},"What are your payment terms?","\u003Cp>I usually ask for a 50% deposit to start, with the remainder due on delivery. I accept payments via bank transfer, Payoneer, or Wise.\u003C\u002Fp>",{"label":122,"content":123},"How long does a typical project take?","\u003Cp>Small features take a few days; a full backend usually takes 2–4 weeks. You'll get a timeline estimate upfront.\u003C\u002Fp>",{"label":125,"content":126},"Do you offer retainers or ongoing support?","\u003Cp>Yes — I offer monthly maintenance and support plans for clients who need ongoing development after launch.\u003C\u002Fp>",{"title":62,"questions":128},[129,132],{"label":130,"content":131},"What do you enjoy most about your work?","\u003Cp>Turning a vague idea into a reliable product. I love the moment a system I built quietly handles real users without a hitch.\u003C\u002Fp>",{"label":133,"content":134},"What are your hobbies outside of work?","\u003Cp>When I'm not coding, I enjoy exploring Nepal's trails, experimenting with new tech, and contributing to open-source projects.\u003C\u002Fp>",{"images":136},[137,140,143,146,149,152,155,158,161],{"src":138,"alt":139},"\u002Fhero\u002Frandom-1.avif","Random Image 1",{"src":141,"alt":142},"\u002Fhero\u002Frandom-2.avif","Random Image 2",{"src":144,"alt":145},"\u002Fhero\u002Frandom-3.avif","Random Image 3",{"src":147,"alt":148},"\u002Fhero\u002Frandom-4.avif","Random Image 4",{"src":150,"alt":151},"\u002Fhero\u002Frandom-5.avif","Random Image 5",{"src":153,"alt":154},"\u002Fhero\u002Frandom-6.avif","Random Image 6",{"src":156,"alt":157},"\u002Fhero\u002Frandom-7.avif","Random Image 7",{"src":159,"alt":160},"\u002Fhero\u002Frandom-8.avif","Random Image 8",{"src":162,"alt":163},"\u002Fhero\u002Frandom-9.avif","Random Image 9",{},true,"\u002F",{"title":168,"description":169},"Sachin Thapa - FullStack Developer","Welcome to my portfolio! I'm Sachin Thapa, a Full-Stack developer. I specialize in creating robust, scalable solutions that are both efficient and maintainable.","index",[172,180,188],{"quote":173,"author":174},"Sachin rebuilt our backend API and cut response times in half. Clean, reliable code that was easy to hand off and maintain. Communication was excellent throughout.",{"name":175,"description":176,"avatar":177},"Sarah Chen","Product Director at Bloom Finance",{"src":178,"srcset":179},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=40&h=40&q=80","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=80&h=80&q=80 2x",{"quote":181,"author":182},"Working with Sachin was seamless. He took our vague requirements and delivered a clean, scalable backend that just works. Rare to find a developer this dependable.",{"name":183,"description":184,"avatar":185},"Michael Rodriguez","Co-founder of Wavelength Music",{"src":186,"srcset":187},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=40&h=40&q=80","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=80&h=80&q=80 2x",{"quote":189,"author":190},"Sachin's engineering is precise and thoughtful. He documented everything, wrote proper tests, and delivered ahead of schedule. Exactly the developer you want on your team.",{"name":191,"description":192,"avatar":193},"Dr. Aisha Johnson","CTO at GreenTech Solutions",{"src":194,"srcset":195},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1573497019940-1c28c88b4f3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=40&h=40&q=80","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1573497019940-1c28c88b4f3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=80&h=80&q=80 2x","FwQet2bUkSS53qw8UJDMPI9098uYCAWyHmB2IylOW7g",[198,1196,2687],{"id":199,"title":22,"author":200,"body":206,"date":1189,"description":1190,"extension":1191,"image":1192,"meta":1193,"minRead":355,"navigation":165,"path":23,"seo":1194,"stem":24,"__hash__":1195},"blog\u002Fblog\u002Fcomprehensive-guide-to-using-uploadthing-with-express-and-typescript.md",{"name":201,"to":202,"username":203,"avatar":204},"Sachin Thapa","https:\u002F\u002Fsachinthapa.hashnode.dev\u002F","sachinthapa572",{"src":205,"alt":201},"https:\u002F\u002Fcdn.hashnode.com\u002Fres\u002Fhashnode\u002Fimage\u002Fupload\u002Fv1733546636355\u002Fa4ae8ce5-eb4c-4ed8-ab9d-15a4b8f32ea5.png",{"type":207,"value":208,"toc":1186},"minimark",[209,213,218,221,225,228,901,905,908,935,939,948,956,959,967,971,974,1076,1121,1125,1128,1132,1135,1138,1143,1182],[210,211,212],"p",{},"This note provides a detailed exploration of setting up file uploads using Uploadthing in an Express.js application with Typescript, focusing on security best practices and the potential use of signed URLs. The information is derived from analyzing Uploadthing's documentation, ensuring a thorough understanding for developers seeking to implement this service.",[214,215,217],"h4",{"id":216},"overview-of-uploadthing","Overview of Uploadthing",[210,219,220],{},"Uploadthing is a file upload solution designed for modern web developers, particularly those using full-stack TypeScript applications. It supports various frameworks like Next.js, Astro, Solid Start, SvelteKit, and Nuxt, with backend adapters including Express, Fastify, H3, and WinterCG\u002FFetch API, and frontend libraries such as React, Vue, SolidJS, Svelte, and Vanilla JS. This versatility makes it suitable for diverse project needs, especially in Express environments with Typescript.",[214,222,224],{"id":223},"step-by-step-setup-process","Step-by-Step Setup Process",[210,226,227],{},"To integrate Uploadthing with Express and Typescript, follow these detailed steps:",[229,230,231,249,263,654,883],"ol",{},[232,233,234,238],"li",{},[235,236,237],"strong",{},"Package Installation:",[239,240,241],"ul",{},[232,242,243,244,248],{},"Begin by installing the Uploadthing package using the command ",[245,246,247],"code",{},"npm install uploadthing",". This installs the necessary dependencies for both server and client-side operations.",[232,250,251,254],{},[235,252,253],{},"Environment Configuration:",[239,255,256],{},[232,257,258,259,262],{},"Access the Uploadthing dashboard to create a new application and retrieve an API key from the API Keys tab. Set this key as an environment variable in your project, typically as ",[245,260,261],{},"UPLOADTHING_TOKEN=your_token",". This ensures secure interaction with the Uploadthing SDK.",[232,264,265,268],{},[235,266,267],{},"File Router Creation:",[239,269,270,645],{},[232,271,272,273,276,277,280,281],{},"Create a file, such as ",[245,274,275],{},"src\u002Fuploadthing.ts",", to define your file routes using the ",[245,278,279],{},"createUploadthing"," function from \"uploadthing\u002Fexpress\". For example, to set up an image uploader:\n",[282,283,288],"pre",{"className":284,"code":285,"language":286,"meta":287,"style":287},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { createUploadthing } from \"uploadthing\u002Fexpress\";\n\nconst f = createUploadthing();\n\nconst imageUploader = f({ \"image\": { maxFileSize: \"4MB\", maxFileCount: 1, acl: \"private\" } })\n  .middleware(async ({ req }) => {\n    \u002F\u002F Security check: Ensure user is authenticated\n    if (!req.user) {\n      throw new Error(\"Unauthorized\");\n    }\n  })\n  .onUploadComplete(({ file }) => {\n    \u002F\u002F Post-upload actions: Log or store the file URL\n    console.log(file.url);\n  });\n\nexport const uploadRouter = f({\n  imageUploader,\n});\n","ts","",[245,289,290,326,332,353,358,435,465,472,499,525,531,539,561,567,592,601,606,626,635],{"__ignoreMap":287},[291,292,295,299,303,307,310,313,316,320,323],"span",{"class":293,"line":294},"line",1,[291,296,298],{"class":297},"s7zQu","import",[291,300,302],{"class":301},"sMK4o"," {",[291,304,306],{"class":305},"sTEyZ"," createUploadthing",[291,308,309],{"class":301}," }",[291,311,312],{"class":297}," from",[291,314,315],{"class":301}," \"",[291,317,319],{"class":318},"sfazB","uploadthing\u002Fexpress",[291,321,322],{"class":301},"\"",[291,324,325],{"class":301},";\n",[291,327,329],{"class":293,"line":328},2,[291,330,331],{"emptyLinePlaceholder":165},"\n",[291,333,335,339,342,345,348,351],{"class":293,"line":334},3,[291,336,338],{"class":337},"spNyl","const",[291,340,341],{"class":305}," f ",[291,343,344],{"class":301},"=",[291,346,306],{"class":347},"s2Zo4",[291,349,350],{"class":305},"()",[291,352,325],{"class":301},[291,354,356],{"class":293,"line":355},4,[291,357,331],{"emptyLinePlaceholder":165},[291,359,361,363,366,368,371,374,377,379,383,385,388,390,393,395,397,400,402,405,408,410,414,416,419,421,423,426,428,430,432],{"class":293,"line":360},5,[291,362,338],{"class":337},[291,364,365],{"class":305}," imageUploader ",[291,367,344],{"class":301},[291,369,370],{"class":347}," f",[291,372,373],{"class":305},"(",[291,375,376],{"class":301},"{",[291,378,315],{"class":301},[291,380,382],{"class":381},"swJcz","image",[291,384,322],{"class":301},[291,386,387],{"class":301},":",[291,389,302],{"class":301},[291,391,392],{"class":381}," maxFileSize",[291,394,387],{"class":301},[291,396,315],{"class":301},[291,398,399],{"class":318},"4MB",[291,401,322],{"class":301},[291,403,404],{"class":301},",",[291,406,407],{"class":381}," maxFileCount",[291,409,387],{"class":301},[291,411,413],{"class":412},"sbssI"," 1",[291,415,404],{"class":301},[291,417,418],{"class":381}," acl",[291,420,387],{"class":301},[291,422,315],{"class":301},[291,424,425],{"class":318},"private",[291,427,322],{"class":301},[291,429,309],{"class":301},[291,431,309],{"class":301},[291,433,434],{"class":305},")\n",[291,436,438,441,444,446,449,452,456,459,462],{"class":293,"line":437},6,[291,439,440],{"class":301},"  .",[291,442,443],{"class":347},"middleware",[291,445,373],{"class":305},[291,447,448],{"class":337},"async",[291,450,451],{"class":301}," ({",[291,453,455],{"class":454},"sHdIc"," req",[291,457,458],{"class":301}," })",[291,460,461],{"class":337}," =>",[291,463,464],{"class":301}," {\n",[291,466,468],{"class":293,"line":467},7,[291,469,471],{"class":470},"sHwdD","    \u002F\u002F Security check: Ensure user is authenticated\n",[291,473,475,478,481,484,487,490,493,496],{"class":293,"line":474},8,[291,476,477],{"class":297},"    if",[291,479,480],{"class":381}," (",[291,482,483],{"class":301},"!",[291,485,486],{"class":305},"req",[291,488,489],{"class":301},".",[291,491,492],{"class":305},"user",[291,494,495],{"class":381},") ",[291,497,498],{"class":301},"{\n",[291,500,502,505,508,511,513,515,518,520,523],{"class":293,"line":501},9,[291,503,504],{"class":297},"      throw",[291,506,507],{"class":301}," new",[291,509,510],{"class":347}," Error",[291,512,373],{"class":381},[291,514,322],{"class":301},[291,516,517],{"class":318},"Unauthorized",[291,519,322],{"class":301},[291,521,522],{"class":381},")",[291,524,325],{"class":301},[291,526,528],{"class":293,"line":527},10,[291,529,530],{"class":301},"    }\n",[291,532,534,537],{"class":293,"line":533},11,[291,535,536],{"class":301},"  }",[291,538,434],{"class":305},[291,540,542,544,547,549,552,555,557,559],{"class":293,"line":541},12,[291,543,440],{"class":301},[291,545,546],{"class":347},"onUploadComplete",[291,548,373],{"class":305},[291,550,551],{"class":301},"({",[291,553,554],{"class":454}," file",[291,556,458],{"class":301},[291,558,461],{"class":337},[291,560,464],{"class":301},[291,562,564],{"class":293,"line":563},13,[291,565,566],{"class":470},"    \u002F\u002F Post-upload actions: Log or store the file URL\n",[291,568,570,573,575,578,580,583,585,588,590],{"class":293,"line":569},14,[291,571,572],{"class":305},"    console",[291,574,489],{"class":301},[291,576,577],{"class":347},"log",[291,579,373],{"class":381},[291,581,582],{"class":305},"file",[291,584,489],{"class":301},[291,586,587],{"class":305},"url",[291,589,522],{"class":381},[291,591,325],{"class":301},[291,593,595,597,599],{"class":293,"line":594},15,[291,596,536],{"class":301},[291,598,522],{"class":305},[291,600,325],{"class":301},[291,602,604],{"class":293,"line":603},16,[291,605,331],{"emptyLinePlaceholder":165},[291,607,609,612,615,618,620,622,624],{"class":293,"line":608},17,[291,610,611],{"class":297},"export",[291,613,614],{"class":337}," const",[291,616,617],{"class":305}," uploadRouter ",[291,619,344],{"class":301},[291,621,370],{"class":347},[291,623,373],{"class":305},[291,625,498],{"class":301},[291,627,629,632],{"class":293,"line":628},18,[291,630,631],{"class":305},"  imageUploader",[291,633,634],{"class":301},",\n",[291,636,638,641,643],{"class":293,"line":637},19,[291,639,640],{"class":301},"}",[291,642,522],{"class":305},[291,644,325],{"class":301},[232,646,647,648,650,651,653],{},"The ",[245,649,443],{}," function allows for authorization checks, such as verifying user login status, while ",[245,652,546],{}," handles post-upload tasks, providing access to file properties like the URL.",[232,655,656,659],{},[235,657,658],{},"Server Integration:",[239,660,661,880],{},[232,662,663,664,667,668,671,672,675,676],{},"In your main server file, typically ",[245,665,666],{},"src\u002Findex.ts",", integrate the file router with Express using the ",[245,669,670],{},"createRouteHandler"," from \"uploadthing\u002Fexpress\". Mount it at a specific endpoint, such as ",[245,673,674],{},"\u002Fapi\u002Fuploadthing",":\n",[282,677,679],{"className":284,"code":678,"language":286,"meta":287,"style":287},"import express from \"express\";\nimport { createRouteHandler } from \"uploadthing\u002Fexpress\";\nimport { uploadRouter } from \".\u002Fuploadthing\";\n\nconst app = express();\n\n\u002F\u002F Mount the Uploadthing route\napp.use(\"\u002Fapi\u002Fuploadthing\", createRouteHandler({\n  router: uploadRouter,\n}));\n\n\u002F\u002F Start the server\napp.listen(3000, () => {\n  console.log(\"Server started on port 3000\");\n});\n",[245,680,681,700,721,743,747,763,767,772,798,809,818,822,827,850,872],{"__ignoreMap":287},[291,682,683,685,688,691,693,696,698],{"class":293,"line":294},[291,684,298],{"class":297},[291,686,687],{"class":305}," express ",[291,689,690],{"class":297},"from",[291,692,315],{"class":301},[291,694,695],{"class":318},"express",[291,697,322],{"class":301},[291,699,325],{"class":301},[291,701,702,704,706,709,711,713,715,717,719],{"class":293,"line":328},[291,703,298],{"class":297},[291,705,302],{"class":301},[291,707,708],{"class":305}," createRouteHandler",[291,710,309],{"class":301},[291,712,312],{"class":297},[291,714,315],{"class":301},[291,716,319],{"class":318},[291,718,322],{"class":301},[291,720,325],{"class":301},[291,722,723,725,727,730,732,734,736,739,741],{"class":293,"line":334},[291,724,298],{"class":297},[291,726,302],{"class":301},[291,728,729],{"class":305}," uploadRouter",[291,731,309],{"class":301},[291,733,312],{"class":297},[291,735,315],{"class":301},[291,737,738],{"class":318},".\u002Fuploadthing",[291,740,322],{"class":301},[291,742,325],{"class":301},[291,744,745],{"class":293,"line":355},[291,746,331],{"emptyLinePlaceholder":165},[291,748,749,751,754,756,759,761],{"class":293,"line":360},[291,750,338],{"class":337},[291,752,753],{"class":305}," app ",[291,755,344],{"class":301},[291,757,758],{"class":347}," express",[291,760,350],{"class":305},[291,762,325],{"class":301},[291,764,765],{"class":293,"line":437},[291,766,331],{"emptyLinePlaceholder":165},[291,768,769],{"class":293,"line":467},[291,770,771],{"class":470},"\u002F\u002F Mount the Uploadthing route\n",[291,773,774,777,779,782,784,786,788,790,792,794,796],{"class":293,"line":474},[291,775,776],{"class":305},"app",[291,778,489],{"class":301},[291,780,781],{"class":347},"use",[291,783,373],{"class":305},[291,785,322],{"class":301},[291,787,674],{"class":318},[291,789,322],{"class":301},[291,791,404],{"class":301},[291,793,708],{"class":347},[291,795,373],{"class":305},[291,797,498],{"class":301},[291,799,800,803,805,807],{"class":293,"line":501},[291,801,802],{"class":381},"  router",[291,804,387],{"class":301},[291,806,729],{"class":305},[291,808,634],{"class":301},[291,810,811,813,816],{"class":293,"line":527},[291,812,640],{"class":301},[291,814,815],{"class":305},"))",[291,817,325],{"class":301},[291,819,820],{"class":293,"line":533},[291,821,331],{"emptyLinePlaceholder":165},[291,823,824],{"class":293,"line":541},[291,825,826],{"class":470},"\u002F\u002F Start the server\n",[291,828,829,831,833,836,838,841,843,846,848],{"class":293,"line":563},[291,830,776],{"class":305},[291,832,489],{"class":301},[291,834,835],{"class":347},"listen",[291,837,373],{"class":305},[291,839,840],{"class":412},"3000",[291,842,404],{"class":301},[291,844,845],{"class":301}," ()",[291,847,461],{"class":337},[291,849,464],{"class":301},[291,851,852,855,857,859,861,863,866,868,870],{"class":293,"line":569},[291,853,854],{"class":305},"  console",[291,856,489],{"class":301},[291,858,577],{"class":347},[291,860,373],{"class":381},[291,862,322],{"class":301},[291,864,865],{"class":318},"Server started on port 3000",[291,867,322],{"class":301},[291,869,522],{"class":381},[291,871,325],{"class":301},[291,873,874,876,878],{"class":293,"line":594},[291,875,640],{"class":301},[291,877,522],{"class":305},[291,879,325],{"class":301},[232,881,882],{},"This setup ensures that file upload requests are handled through the defined endpoint.",[232,884,885,888],{},[235,886,887],{},"Client-Side Implementation:",[239,889,890],{},[232,891,892,893,896,897,900],{},"On the client side, utilize the ",[245,894,895],{},"@uploadthing\u002Freact"," package to generate upload components, such as an upload button. Configure it with the server's endpoint, for example, ",[245,898,899],{},"https:\u002F\u002Fyour-server.com\u002Fapi\u002Fuploadthing",". This allows users to initiate uploads seamlessly within your application.",[214,902,904],{"id":903},"security-best-practices","Security Best Practices",[210,906,907],{},"Security is paramount when handling file uploads. Uploadthing provides several mechanisms to ensure secure operations:",[239,909,910,925],{},[232,911,912,915,916],{},[235,913,914],{},"Middleware for Authentication:"," Implement middleware within the file router to authorize users before uploads. For instance, check if a user is logged in using a service like Clerk, throwing an error if unauthorized:\n",[239,917,918],{},[232,919,920,921,924],{},"Example: ",[245,922,923],{},"throw new Error(\"You must be logged in to upload a profile picture\")"," if no user is signed in.",[232,926,927,930,931,934],{},[235,928,929],{},"Access Control Lists (ACL):"," Since version 6.0, Uploadthing allows setting the ",[245,932,933],{},"acl"," property in route configurations to \"public-read\" or \"private\". Setting it to \"private\" restricts access, ensuring only authorized users can view or download files. This is crucial for protecting sensitive data.",[214,936,938],{"id":937},"handling-file-access-and-signed-urls","Handling File Access and Signed URLs",[210,940,941,942,944,945,947],{},"Accessing uploaded files is facilitated through the ",[245,943,546],{}," callback, which provides a ",[245,946,582],{}," object containing properties like the URL. For example:",[239,949,950],{},[232,951,647,952,955],{},[245,953,954],{},"file.url"," can be used to access the uploaded file, which is particularly useful for storing in a database or sharing within the application.",[210,957,958],{},"However, for files with \"private\" ACL, direct access via URL may require additional authorization. While Uploadthing's documentation does not explicitly detail signed URLs, the concept aligns with cloud storage practices like Amazon S3, where signed URLs provide temporary access to private files. Given Uploadthing's mention of being a \"better S3\" alternative, it seems likely that similar mechanisms exist, but specific implementation details are not provided. Developers may need to:",[239,960,961,964],{},[232,962,963],{},"Refer to the storage provider's documentation (if Uploadthing uses an underlying service like S3) or",[232,965,966],{},"Contact Uploadthing support for guidance on generating signed URLs for sharing private files.",[214,968,970],{"id":969},"tables-for-clarity","Tables for Clarity",[210,972,973],{},"To organize the information, consider the following tables summarizing key aspects:",[975,976,977,999],"table",{},[978,979,980],"thead",{},[981,982,983,989,994],"tr",{},[984,985,986],"th",{},[235,987,988],{},"Step",[984,990,991],{},[235,992,993],{},"Action",[984,995,996],{},[235,997,998],{},"Details",[1000,1001,1002,1016,1029,1045,1062],"tbody",{},[981,1003,1004,1008,1013],{},[1005,1006,1007],"td",{},"1. Install Package",[1005,1009,1010,1011],{},"Run ",[245,1012,247],{},[1005,1014,1015],{},"Installs server and client dependencies",[981,1017,1018,1021,1026],{},[1005,1019,1020],{},"2. Environment Setup",[1005,1022,1023,1024],{},"Set ",[245,1025,261],{},[1005,1027,1028],{},"Secure SDK interaction with API key",[981,1030,1031,1034,1042],{},[1005,1032,1033],{},"3. File Router Creation",[1005,1035,1036,1037,1039,1040],{},"Define routes in ",[245,1038,275],{}," using ",[245,1041,279],{},[1005,1043,1044],{},"Example: Image uploader with 4MB limit, private ACL",[981,1046,1047,1050,1056],{},[1005,1048,1049],{},"4. Server Integration",[1005,1051,1052,1053,1055],{},"Mount at ",[245,1054,674],{}," in Express server",[1005,1057,1058,1059,1061],{},"Use ",[245,1060,670],{}," for routing",[981,1063,1064,1067,1072],{},[1005,1065,1066],{},"5. Client Usage",[1005,1068,1058,1069,1071],{},[245,1070,895],{}," for upload button, set endpoint URL",[1005,1073,920,1074],{},[245,1075,899],{},[975,1077,1078,1097],{},[978,1079,1080],{},[981,1081,1082,1087,1092],{},[984,1083,1084],{},[235,1085,1086],{},"Security Aspect",[984,1088,1089],{},[235,1090,1091],{},"Description",[984,1093,1094],{},[235,1095,1096],{},"Implementation",[1000,1098,1099,1110],{},[981,1100,1101,1104,1107],{},[1005,1102,1103],{},"Middleware Authentication",[1005,1105,1106],{},"Ensure only authorized users can upload files",[1005,1108,1109],{},"Check user login status, throw error if unauthorized",[981,1111,1112,1115,1118],{},[1005,1113,1114],{},"Access Control (ACL)",[1005,1116,1117],{},"Set file access to \"public-read\" or \"private\"",[1005,1119,1120],{},"Configure in route, affects file visibility",[214,1122,1124],{"id":1123},"unexpected-findings","Unexpected Findings",[210,1126,1127],{},"An interesting observation is Uploadthing's positioning as an alternative to S3, suggesting it uses similar cloud storage technologies. This implies potential familiarity with concepts like signed URLs, but the lack of explicit documentation on this topic adds complexity, requiring developers to seek additional resources or support.",[214,1129,1131],{"id":1130},"conclusion","Conclusion",[210,1133,1134],{},"This guide provides a comprehensive approach to setting up Uploadthing with Express and Typescript, emphasizing security through authentication and access controls. While accessing uploaded files is straightforward via the provided URL, handling private files with signed URLs may require further exploration, potentially through Uploadthing's support channels or underlying storage provider documentation.",[1136,1137],"hr",{},[1139,1140,1142],"h3",{"id":1141},"key-citations","Key Citations",[239,1144,1145,1154,1161,1168,1175],{},[232,1146,1147],{},[1148,1149,1153],"a",{"href":1150,"rel":1151},"https:\u002F\u002Fdocs.uploadthing.com\u002F",[1152],"nofollow","Uploadthing Getting Started Guide",[232,1155,1156],{},[1148,1157,1160],{"href":1158,"rel":1159},"https:\u002F\u002Fdocs.uploadthing.com\u002Fbackend-adapters\u002Fexpress",[1152],"Uploadthing Express Adapter Documentation",[232,1162,1163],{},[1148,1164,1167],{"href":1165,"rel":1166},"https:\u002F\u002Fdocs.uploadthing.com\u002Ffile-routes",[1152],"Uploadthing File Routes Documentation",[232,1169,1170],{},[1148,1171,1174],{"href":1172,"rel":1173},"https:\u002F\u002Fdocs.uploadthing.com\u002Fapi-reference\u002Fserver#create-routehandler",[1152],"Uploadthing Server API Reference",[232,1176,1177],{},[1148,1178,1181],{"href":1179,"rel":1180},"https:\u002F\u002Fdocs.uploadthing.com\u002Fapi-reference\u002Fserver#ut-api",[1152],"Uploadthing UTApi Reference",[1183,1184,1185],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":287,"searchDepth":328,"depth":328,"links":1187},[1188],{"id":1141,"depth":334,"text":1142},"2026-07-18","This note provides a detailed exploration of setting up file uploads using Uploadthing in an Express.js application with TypeScript, focusing on security best practices and the potential use of signed URLs.","md","\u002Fimages\u002Fblog\u002Fcomprehensive-guide-to-using-uploadthing-with-express-and-typescript.png",{},{"title":22,"description":1190},"HEzjWFhxyCCWG8xQQ9kaMNpQQfoRZAJZCufyrr9Oudo",{"id":1197,"title":34,"author":1198,"body":1200,"date":2681,"description":2682,"extension":1191,"image":2683,"meta":2684,"minRead":501,"navigation":165,"path":35,"seo":2685,"stem":36,"__hash__":2686},"blog\u002Fblog\u002Ffull-stack-learning-management-system-lms-with-mern-stack-a-complete-guide.md",{"name":201,"to":202,"username":203,"avatar":1199},{"src":205,"alt":201},{"type":207,"value":1201,"toc":2655},[1202,1209,1211,1217,1255,1257,1263,1269,1328,1330,1336,1485,1487,1493,1596,1598,1604,1610,1617,1625,1632,1638,1645,1651,1657,1671,1677,1736,1742,1786,1792,1800,1806,1824,1826,1832,1876,1878,1882,1885,1888,1891,1897,1958,1964,2036,2042,2084,2090,2149,2155,2187,2193,2225,2231,2273,2279,2311,2317,2359,2365,2397,2403,2425,2431,2453,2459,2480,2486,2508,2514,2536,2542,2574,2576,2582,2644,2646,2652],[210,1203,1204,1205,1208],{},"This document provides a structured approach to developing a Learning Management System (LMS) using the ",[235,1206,1207],{},"MERN stack"," (MongoDB, Express.js, React.js, and Node.js). An LMS typically includes features like course management, student enrollment, assessments, and content delivery.",[1136,1210],{},[1139,1212,1214],{"id":1213},"project-overview",[235,1215,1216],{},"Project Overview",[239,1218,1219,1225,1231,1237,1243,1249],{},[232,1220,1221,1224],{},[235,1222,1223],{},"Frontend",": React.js for building the user interface (UI).",[232,1226,1227,1230],{},[235,1228,1229],{},"Backend",": Node.js and Express.js for the server and API layer.",[232,1232,1233,1236],{},[235,1234,1235],{},"Database",": MongoDB for storing user data, courses, assignments, etc.",[232,1238,1239,1242],{},[235,1240,1241],{},"Authentication",": JWT (JSON Web Tokens) for secure user authentication.",[232,1244,1245,1248],{},[235,1246,1247],{},"File Storage",": Use a cloud storage service like AWS S3 or an alternative for storing course material (videos, PDFs).",[232,1250,1251,1254],{},[235,1252,1253],{},"Hosting",": Use platforms like Heroku, Vercel (frontend), and MongoDB Atlas for database hosting.",[1136,1256],{},[1139,1258,1260],{"id":1259},"project-structure",[235,1261,1262],{},"Project Structure",[214,1264,1266],{"id":1265},"_1-frontend-reactjs",[235,1267,1268],{},"1. Frontend (React.js)",[239,1270,1271,1308,1314],{},[232,1272,1273,387,1276],{},[235,1274,1275],{},"Components",[239,1277,1278,1284,1290,1296,1302],{},[232,1279,1280,1283],{},[235,1281,1282],{},"Navbar",": Links to the dashboard, courses, login, and sign-up.",[232,1285,1286,1289],{},[235,1287,1288],{},"Dashboard",": Display an overview of enrolled courses, progress, etc.",[232,1291,1292,1295],{},[235,1293,1294],{},"Course List",": Displays all available courses.",[232,1297,1298,1301],{},[235,1299,1300],{},"Course Details",": Detailed view of course content, assignments, and quizzes.",[232,1303,1304,1307],{},[235,1305,1306],{},"Profile",": User profile and progress.",[232,1309,1310,1313],{},[235,1311,1312],{},"State Management",": Use Redux or React Context API to manage the global state (such as user login, course data, etc.).",[232,1315,1316,1319,1320,1323,1324,1327],{},[235,1317,1318],{},"API Integration",": Use ",[245,1321,1322],{},"axios"," or ",[245,1325,1326],{},"fetch"," to make HTTP requests to your backend APIs.",[1136,1329],{},[214,1331,1333],{"id":1332},"_2-backend-nodejs-expressjs",[235,1334,1335],{},"2. Backend (Node.js & Express.js)",[239,1337,1338,1381],{},[232,1339,1340,1343,1344],{},[235,1341,1342],{},"API Structure",": Organize your backend using the MVC (Model-View-Controller) pattern.",[239,1345,1346,1352,1369],{},[232,1347,1348,1351],{},[235,1349,1350],{},"Controllers",": Handle logic for each feature (e.g., courses, users).",[232,1353,1354,1357,1358,1361,1362,1361,1365,1368],{},[235,1355,1356],{},"Models",": Define schemas for MongoDB (e.g., ",[245,1359,1360],{},"User",", ",[245,1363,1364],{},"Course",[245,1366,1367],{},"Assignment",").",[232,1370,1371,1374,1375,1361,1378,1368],{},[235,1372,1373],{},"Routes",": Create routes for different resources (e.g., ",[245,1376,1377],{},"\u002Fapi\u002Fcourses",[245,1379,1380],{},"\u002Fapi\u002Fusers",[232,1382,1383,387,1386],{},[235,1384,1385],{},"API Endpoints",[239,1387,1388,1392,1398,1404,1409,1415,1421,1427,1433,1439,1444,1450,1456,1462,1468,1473,1479],{},[232,1389,1390,387],{},[235,1391,1241],{},[232,1393,1394,1397],{},[245,1395,1396],{},"POST \u002Fapi\u002Fauth\u002Fregister",": Register new users (admin, teacher, student).",[232,1399,1400,1403],{},[245,1401,1402],{},"POST \u002Fapi\u002Fauth\u002Flogin",": Login and return a JWT token.",[232,1405,1406,387],{},[235,1407,1408],{},"Course Management",[232,1410,1411,1414],{},[245,1412,1413],{},"POST \u002Fapi\u002Fcourses",": Admin or teacher can create courses.",[232,1416,1417,1420],{},[245,1418,1419],{},"GET \u002Fapi\u002Fcourses",": Fetch all available courses for students.",[232,1422,1423,1426],{},[245,1424,1425],{},"GET \u002Fapi\u002Fcourses\u002F:id",": Fetch detailed course data.",[232,1428,1429,1432],{},[245,1430,1431],{},"PUT \u002Fapi\u002Fcourses\u002F:id",": Update a course.",[232,1434,1435,1438],{},[245,1436,1437],{},"DELETE \u002Fapi\u002Fcourses\u002F:id",": Delete a course.",[232,1440,1441,387],{},[235,1442,1443],{},"Assignments & Quizzes",[232,1445,1446,1449],{},[245,1447,1448],{},"POST \u002Fapi\u002Fcourses\u002F:id\u002Fassignments",": Add assignments for a course.",[232,1451,1452,1455],{},[245,1453,1454],{},"GET \u002Fapi\u002Fcourses\u002F:id\u002Fassignments",": Fetch assignments.",[232,1457,1458,1461],{},[245,1459,1460],{},"POST \u002Fapi\u002Fcourses\u002F:id\u002Fquizzes",": Add quizzes for a course.",[232,1463,1464,1467],{},[245,1465,1466],{},"GET \u002Fapi\u002Fcourses\u002F:id\u002Fquizzes",": Fetch quizzes.",[232,1469,1470,387],{},[235,1471,1472],{},"Progress Tracking",[232,1474,1475,1478],{},[245,1476,1477],{},"GET \u002Fapi\u002Fusers\u002F:id\u002Fprogress",": Fetch user progress in courses.",[232,1480,1481,1484],{},[245,1482,1483],{},"PUT \u002Fapi\u002Fusers\u002F:id\u002Fprogress",": Update user progress.",[1136,1486],{},[1139,1488,1490],{"id":1489},"key-features",[235,1491,1492],{},"Key Features",[229,1494,1495,1520,1535,1548,1561,1574,1584],{},[232,1496,1497,387,1500],{},[235,1498,1499],{},"User Roles",[239,1501,1502,1508,1514],{},[232,1503,1504,1507],{},[235,1505,1506],{},"Admin",": Manage users, courses, assignments, and quizzes.",[232,1509,1510,1513],{},[235,1511,1512],{},"Teacher",": Create and manage courses, upload assignments and quizzes.",[232,1515,1516,1519],{},[235,1517,1518],{},"Student",": Enroll in courses, complete assignments, take quizzes.",[232,1521,1522,387,1524],{},[235,1523,1408],{},[239,1525,1526,1529,1532],{},[232,1527,1528],{},"CRUD operations for courses (Create, Read, Update, Delete).",[232,1530,1531],{},"Upload course material (PDF, video, etc.).",[232,1533,1534],{},"Quizzes and assignments as part of each course.",[232,1536,1537,387,1540],{},[235,1538,1539],{},"Student Enrollment",[239,1541,1542,1545],{},[232,1543,1544],{},"Students can browse courses and enroll in them.",[232,1546,1547],{},"Each student can track their progress and grades for enrolled courses.",[232,1549,1550,387,1553],{},[235,1551,1552],{},"Authentication & Authorization",[239,1554,1555,1558],{},[232,1556,1557],{},"JWT-based authentication for login and registration.",[232,1559,1560],{},"Role-based access control (RBAC) to ensure only authorized users (admin\u002Fteacher\u002Fstudent) can perform specific actions.",[232,1562,1563,387,1566],{},[235,1564,1565],{},"Assignment Submission",[239,1567,1568,1571],{},[232,1569,1570],{},"Students can upload their assignment submissions.",[232,1572,1573],{},"Teachers can grade assignments and provide feedback.",[232,1575,1576,387,1579],{},[235,1577,1578],{},"Quizzes",[239,1580,1581],{},[232,1582,1583],{},"Quizzes as part of the course content with auto-grading or manual grading by the teacher.",[232,1585,1586,387,1588],{},[235,1587,1472],{},[239,1589,1590,1593],{},[232,1591,1592],{},"Track student progress for each course.",[232,1594,1595],{},"Display progress on the dashboard (e.g., completion percentage).",[1136,1597],{},[1139,1599,1601],{"id":1600},"steps-to-develop-the-lms",[235,1602,1603],{},"Steps to Develop the LMS",[214,1605,1607],{"id":1606},"step-1-set-up-the-project",[235,1608,1609],{},"Step 1: Set Up the Project",[229,1611,1612],{},[232,1613,1614,387],{},[235,1615,1616],{},"Initialize Git Repository",[282,1618,1623],{"className":1619,"code":1621,"language":1622},[1620],"language-text","   git init\n","text",[245,1624,1621],{"__ignoreMap":287},[229,1626,1627],{},[232,1628,1629,387],{},[235,1630,1631],{},"Frontend Setup (React)",[282,1633,1636],{"className":1634,"code":1635,"language":1622},[1620],"   npx create-react-app lms-frontend\n   cd lms-frontend\n",[245,1637,1635],{"__ignoreMap":287},[229,1639,1640],{},[232,1641,1642,387],{},[235,1643,1644],{},"Backend Setup (Node.js & Express)",[282,1646,1649],{"className":1647,"code":1648,"language":1622},[1620],"   mkdir lms-backend\n   cd lms-backend\n   npm init -y\n   npm install express mongoose bcryptjs jsonwebtoken cors\n",[245,1650,1648],{"__ignoreMap":287},[214,1652,1654],{"id":1653},"step-2-database-setup-mongodb",[235,1655,1656],{},"Step 2: Database Setup (MongoDB)",[239,1658,1659,1665],{},[232,1660,1661,1664],{},[235,1662,1663],{},"MongoDB Atlas",": Create a MongoDB Atlas account and set up a cloud database.",[232,1666,1667,1670],{},[235,1668,1669],{},"Local MongoDB",": If preferred, install MongoDB locally.",[214,1672,1674],{"id":1673},"step-3-backend-development",[235,1675,1676],{},"Step 3: Backend Development",[229,1678,1679,1700,1716,1726],{},[232,1680,1681,387,1684],{},[235,1682,1683],{},"Define Models (MongoDB)",[239,1685,1686],{},[232,1687,1688,1689,1361,1691,1361,1693,1695,1696,1699],{},"Create models for ",[235,1690,1360],{},[235,1692,1364],{},[235,1694,1367],{},", and ",[235,1697,1698],{},"Quiz"," using Mongoose.",[232,1701,1702,387,1705],{},[235,1703,1704],{},"User Authentication",[239,1706,1707,1713],{},[232,1708,1058,1709,1712],{},[245,1710,1711],{},"bcryptjs"," to hash passwords.",[232,1714,1715],{},"Implement JWT-based authentication for login and registration.",[232,1717,1718,387,1721],{},[235,1719,1720],{},"API Routes",[239,1722,1723],{},[232,1724,1725],{},"Set up API routes for user management, course CRUD operations, assignments, and quizzes.",[232,1727,1728,387,1731],{},[235,1729,1730],{},"Middleware",[239,1732,1733],{},[232,1734,1735],{},"Create authentication middleware to protect routes (e.g., only authenticated users can access certain routes).",[214,1737,1739],{"id":1738},"step-4-frontend-development",[235,1740,1741],{},"Step 4: Frontend Development",[229,1743,1744,1754,1763,1776],{},[232,1745,1746,387,1749],{},[235,1747,1748],{},"React Components",[239,1750,1751],{},[232,1752,1753],{},"Build UI components for course management, enrollment, dashboard, and profile.",[232,1755,1756,387,1758],{},[235,1757,1312],{},[239,1759,1760],{},[232,1761,1762],{},"Use Redux or React Context API to handle global state (e.g., user data, enrolled courses, etc.).",[232,1764,1765,387,1767],{},[235,1766,1318],{},[239,1768,1769],{},[232,1770,1058,1771,1323,1773,1775],{},[245,1772,1322],{},[245,1774,1326],{}," to connect frontend to backend APIs.",[232,1777,1778,387,1781],{},[235,1779,1780],{},"Protected Routes",[239,1782,1783],{},[232,1784,1785],{},"Use React Router for navigation and protect routes using authentication checks (e.g., only logged-in users can access certain pages).",[214,1787,1789],{"id":1788},"step-5-file-storage-aws-s3-or-alternatives",[235,1790,1791],{},"Step 5: File Storage (AWS S3 or Alternatives)",[239,1793,1794,1797],{},[232,1795,1796],{},"Use AWS S3 or a similar service to store and retrieve files (course material, assignments).",[232,1798,1799],{},"Integrate the file upload functionality in the backend and create upload endpoints.",[214,1801,1803],{"id":1802},"step-6-testing",[235,1804,1805],{},"Step 6: Testing",[239,1807,1808,1813,1818],{},[232,1809,1810,1812],{},[235,1811,1223],{},": Test components using React Testing Library.",[232,1814,1815,1817],{},[235,1816,1229],{},": Use tools like Postman or Insomnia to test API endpoints.",[232,1819,1820,1823],{},[235,1821,1822],{},"End-to-End Testing",": Use Cypress for testing the entire application flow.",[1136,1825],{},[1139,1827,1829],{"id":1828},"deployment",[235,1830,1831],{},"Deployment",[229,1833,1834,1849,1865],{},[232,1835,1836,387,1838],{},[235,1837,1223],{},[239,1839,1840],{},[232,1841,1842,1843,1323,1846,489],{},"Deploy the React app on ",[235,1844,1845],{},"Vercel",[235,1847,1848],{},"Netlify",[232,1850,1851,387,1853],{},[235,1852,1229],{},[239,1854,1855],{},[232,1856,1857,1858,1361,1861,1864],{},"Deploy the backend on ",[235,1859,1860],{},"Heroku",[235,1862,1863],{},"DigitalOcean",", or any Node.js hosting service.",[232,1866,1867,387,1869],{},[235,1868,1235],{},[239,1870,1871],{},[232,1872,1058,1873,1875],{},[235,1874,1663],{}," for cloud-hosted MongoDB.",[1136,1877],{},[1139,1879,1880],{"id":1130},[235,1881,1131],{},[210,1883,1884],{},"By following this guide, you can develop a full-fledged Learning Management System using the MERN stack. The system can be further expanded by adding features like real-time chat, discussion forums, notifications, and more.",[210,1886,1887],{},"If you need help with specific sections like setting up authentication, deploying the app, or adding advanced features, feel free to ask!",[210,1889,1890],{},"In a full-stack Learning Management System (LMS) developed with the MERN stack, a wide range of features and functionalities can be implemented. Here’s a breakdown of key features and their corresponding functionalities that you can consider for your project:",[1139,1892,1894],{"id":1893},"_1-user-management",[235,1895,1896],{},"1. User Management",[239,1898,1899,1908,1918,1928,1938,1948],{},[232,1900,1901,387,1903],{},[235,1902,1499],{},[239,1904,1905],{},[232,1906,1907],{},"Admin, Teacher, Student.",[232,1909,1910,387,1913],{},[235,1911,1912],{},"Registration",[239,1914,1915],{},[232,1916,1917],{},"Role-based user registration (Student, Teacher, Admin).",[232,1919,1920,387,1923],{},[235,1921,1922],{},"Login\u002FLogout",[239,1924,1925],{},[232,1926,1927],{},"JWT-based authentication for session management.",[232,1929,1930,387,1933],{},[235,1931,1932],{},"Profile Management",[239,1934,1935],{},[232,1936,1937],{},"Allow users to update their profile information (name, email, password).",[232,1939,1940,387,1943],{},[235,1941,1942],{},"Password Reset",[239,1944,1945],{},[232,1946,1947],{},"Password reset functionality using email or SMS.",[232,1949,1950,387,1953],{},[235,1951,1952],{},"Role-based Access Control (RBAC)",[239,1954,1955],{},[232,1956,1957],{},"Implement permissions based on user roles.",[1139,1959,1961],{"id":1960},"_2-course-management",[235,1962,1963],{},"2. Course Management",[239,1965,1966,1976,1986,1996,2006,2016,2026],{},[232,1967,1968,387,1971],{},[235,1969,1970],{},"Course Creation",[239,1972,1973],{},[232,1974,1975],{},"Teachers and Admins can create courses, including uploading videos, PDFs, or other course materials.",[232,1977,1978,387,1981],{},[235,1979,1980],{},"Course Editing",[239,1982,1983],{},[232,1984,1985],{},"Update or modify course content, titles, descriptions, and associated media.",[232,1987,1988,387,1991],{},[235,1989,1990],{},"Course Deletion",[239,1992,1993],{},[232,1994,1995],{},"Admins or course creators can delete courses.",[232,1997,1998,387,2001],{},[235,1999,2000],{},"Course Categories",[239,2002,2003],{},[232,2004,2005],{},"Add categories or tags to organize courses by subject.",[232,2007,2008,387,2011],{},[235,2009,2010],{},"Course Enrollment",[239,2012,2013],{},[232,2014,2015],{},"Students can browse and enroll in available courses.",[232,2017,2018,387,2021],{},[235,2019,2020],{},"Course Prerequisites",[239,2022,2023],{},[232,2024,2025],{},"Set prerequisites for enrolling in advanced courses.",[232,2027,2028,387,2031],{},[235,2029,2030],{},"Course Recommendations",[239,2032,2033],{},[232,2034,2035],{},"Suggest courses based on the student's interest or previously taken courses.",[1139,2037,2039],{"id":2038},"_3-content-delivery",[235,2040,2041],{},"3. Content Delivery",[239,2043,2044,2054,2064,2074],{},[232,2045,2046,387,2049],{},[235,2047,2048],{},"Video Lectures",[239,2050,2051],{},[232,2052,2053],{},"Upload and stream course videos from cloud storage (e.g., AWS S3).",[232,2055,2056,387,2059],{},[235,2057,2058],{},"PDF\u002FDocument Uploads",[239,2060,2061],{},[232,2062,2063],{},"Allow teachers to upload reading materials, slides, and other documents.",[232,2065,2066,387,2069],{},[235,2067,2068],{},"Content Sequencing",[239,2070,2071],{},[232,2072,2073],{},"Organize content into modules or units, and require students to complete them sequentially.",[232,2075,2076,387,2079],{},[235,2077,2078],{},"Course Progression",[239,2080,2081],{},[232,2082,2083],{},"Track and display course progression for students (e.g., 50% complete).",[1139,2085,2087],{"id":2086},"_4-assignments-quizzes",[235,2088,2089],{},"4. Assignments & Quizzes",[239,2091,2092,2102,2111,2120,2129,2139],{},[232,2093,2094,387,2097],{},[235,2095,2096],{},"Assignment Creation",[239,2098,2099],{},[232,2100,2101],{},"Teachers can create assignments with due dates and attach documents.",[232,2103,2104,387,2106],{},[235,2105,1565],{},[239,2107,2108],{},[232,2109,2110],{},"Students can upload assignment files or submit their work online.",[232,2112,2113,387,2116],{},[235,2114,2115],{},"Assignment Grading",[239,2117,2118],{},[232,2119,1573],{},[232,2121,2122,387,2124],{},[235,2123,1578],{},[239,2125,2126],{},[232,2127,2128],{},"Add multiple-choice or short-answer quizzes.",[232,2130,2131,387,2134],{},[235,2132,2133],{},"Auto-Grading for Quizzes",[239,2135,2136],{},[232,2137,2138],{},"Automatically grade multiple-choice quizzes.",[232,2140,2141,387,2144],{},[235,2142,2143],{},"Quiz Results",[239,2145,2146],{},[232,2147,2148],{},"Display quiz results and grades for students.",[1139,2150,2152],{"id":2151},"_5-student-progress-tracking",[235,2153,2154],{},"5. Student Progress Tracking",[239,2156,2157,2167,2177],{},[232,2158,2159,387,2162],{},[235,2160,2161],{},"Progress Dashboard",[239,2163,2164],{},[232,2165,2166],{},"Show students their progress in all enrolled courses.",[232,2168,2169,387,2172],{},[235,2170,2171],{},"Completion Certificates",[239,2173,2174],{},[232,2175,2176],{},"Award certificates for course completion.",[232,2178,2179,387,2182],{},[235,2180,2181],{},"Gradebook",[239,2183,2184],{},[232,2185,2186],{},"Teachers can maintain a gradebook that displays student performance across assignments and quizzes.",[1139,2188,2190],{"id":2189},"_6-discussion-forums",[235,2191,2192],{},"6. Discussion Forums",[239,2194,2195,2205,2215],{},[232,2196,2197,387,2200],{},[235,2198,2199],{},"Course-Specific Forums",[239,2201,2202],{},[232,2203,2204],{},"Allow students to discuss topics related to the course.",[232,2206,2207,387,2210],{},[235,2208,2209],{},"Threaded Discussions",[239,2211,2212],{},[232,2213,2214],{},"Support for threaded discussions with replies and comments.",[232,2216,2217,387,2220],{},[235,2218,2219],{},"Moderation",[239,2221,2222],{},[232,2223,2224],{},"Teachers and Admins can moderate discussions, remove inappropriate posts, and ban users if necessary.",[1139,2226,2228],{"id":2227},"_7-messaging-and-notifications",[235,2229,2230],{},"7. Messaging and Notifications",[239,2232,2233,2243,2253,2263],{},[232,2234,2235,387,2238],{},[235,2236,2237],{},"Messaging System",[239,2239,2240],{},[232,2241,2242],{},"Implement a direct messaging system between students, teachers, and admins.",[232,2244,2245,387,2248],{},[235,2246,2247],{},"Email Notifications",[239,2249,2250],{},[232,2251,2252],{},"Send email alerts for assignment deadlines, new course enrollment, and quiz results.",[232,2254,2255,387,2258],{},[235,2256,2257],{},"Push Notifications",[239,2259,2260],{},[232,2261,2262],{},"(Optional) Push notifications for real-time updates on mobile devices or browsers.",[232,2264,2265,387,2268],{},[235,2266,2267],{},"In-app Notifications",[239,2269,2270],{},[232,2271,2272],{},"Display in-app alerts for important updates.",[1139,2274,2276],{"id":2275},"_8-grading-and-evaluation",[235,2277,2278],{},"8. Grading and Evaluation",[239,2280,2281,2291,2301],{},[232,2282,2283,387,2286],{},[235,2284,2285],{},"Grading System",[239,2287,2288],{},[232,2289,2290],{},"Implement a grading system for assignments and quizzes.",[232,2292,2293,387,2296],{},[235,2294,2295],{},"Feedback Mechanism",[239,2297,2298],{},[232,2299,2300],{},"Allow teachers to provide detailed feedback on assignments and quizzes.",[232,2302,2303,387,2306],{},[235,2304,2305],{},"Final Grades",[239,2307,2308],{},[232,2309,2310],{},"Calculate final course grades based on assignments, quizzes, and other evaluation methods.",[1139,2312,2314],{"id":2313},"_9-reports-and-analytics",[235,2315,2316],{},"9. Reports and Analytics",[239,2318,2319,2329,2339,2349],{},[232,2320,2321,387,2324],{},[235,2322,2323],{},"Student Reports",[239,2325,2326],{},[232,2327,2328],{},"Generate reports on student performance and activity.",[232,2330,2331,387,2334],{},[235,2332,2333],{},"Teacher Reports",[239,2335,2336],{},[232,2337,2338],{},"Track how students are progressing in courses created by a teacher.",[232,2340,2341,387,2344],{},[235,2342,2343],{},"Admin Dashboard",[239,2345,2346],{},[232,2347,2348],{},"Display platform-wide statistics such as the number of users, active courses, enrollment statistics, etc.",[232,2350,2351,387,2354],{},[235,2352,2353],{},"Course Engagement Analytics",[239,2355,2356],{},[232,2357,2358],{},"Show how many students have completed or engaged with specific course content.",[1139,2360,2362],{"id":2361},"_10-payment-integration",[235,2363,2364],{},"10. Payment Integration",[239,2366,2367,2377,2387],{},[232,2368,2369,387,2372],{},[235,2370,2371],{},"Course Purchases",[239,2373,2374],{},[232,2375,2376],{},"Implement paid courses using a payment gateway like Stripe or PayPal.",[232,2378,2379,387,2382],{},[235,2380,2381],{},"Subscription Plans",[239,2383,2384],{},[232,2385,2386],{},"Offer subscription models where users pay for monthly or yearly access to all courses.",[232,2388,2389,387,2392],{},[235,2390,2391],{},"Refund System",[239,2393,2394],{},[232,2395,2396],{},"Allow users to request refunds for paid courses.",[1139,2398,2400],{"id":2399},"_11-real-time-features",[235,2401,2402],{},"11. Real-time Features",[239,2404,2405,2415],{},[232,2406,2407,387,2410],{},[235,2408,2409],{},"Real-time Classroom\u002FLive Sessions",[239,2411,2412],{},[232,2413,2414],{},"Integrate with video conferencing APIs (e.g., Zoom, Google Meet) to allow live classes.",[232,2416,2417,387,2420],{},[235,2418,2419],{},"Real-time Chat",[239,2421,2422],{},[232,2423,2424],{},"Add real-time chat for student-to-student or student-to-teacher interaction during courses or lectures.",[1139,2426,2428],{"id":2427},"_12-content-search-and-filtering",[235,2429,2430],{},"12. Content Search and Filtering",[239,2432,2433,2443],{},[232,2434,2435,387,2438],{},[235,2436,2437],{},"Search Functionality",[239,2439,2440],{},[232,2441,2442],{},"Implement a search bar to search for courses, teachers, or specific content.",[232,2444,2445,387,2448],{},[235,2446,2447],{},"Filters",[239,2449,2450],{},[232,2451,2452],{},"Allow users to filter courses by categories, difficulty level, price, and instructor.",[1139,2454,2456],{"id":2455},"_13-certificates-and-badges",[235,2457,2458],{},"13. Certificates and Badges",[239,2460,2461,2470],{},[232,2462,2463,387,2465],{},[235,2464,2171],{},[239,2466,2467],{},[232,2468,2469],{},"Automatically generate a certificate for students upon course completion.",[232,2471,2472,387,2475],{},[235,2473,2474],{},"Badging System",[239,2476,2477],{},[232,2478,2479],{},"Award badges for achievements like course completion, top quiz scorer, etc.",[1139,2481,2483],{"id":2482},"_14-mobile-responsiveness-and-compatibility",[235,2484,2485],{},"14. Mobile Responsiveness and Compatibility",[239,2487,2488,2498],{},[232,2489,2490,387,2493],{},[235,2491,2492],{},"Responsive Design",[239,2494,2495],{},[232,2496,2497],{},"Ensure the UI is fully responsive and works well on mobile devices and tablets.",[232,2499,2500,387,2503],{},[235,2501,2502],{},"Progressive Web App (PWA)",[239,2504,2505],{},[232,2506,2507],{},"Optionally, turn the web app into a PWA for better mobile experience.",[1139,2509,2511],{"id":2510},"_15-content-management-system-cms-for-admins",[235,2512,2513],{},"15. Content Management System (CMS) for Admins",[239,2515,2516,2526],{},[232,2517,2518,387,2521],{},[235,2519,2520],{},"Static Page Management",[239,2522,2523],{},[232,2524,2525],{},"Allow admins to manage static pages like Terms and Conditions, About Us, etc.",[232,2527,2528,387,2531],{},[235,2529,2530],{},"Announcements",[239,2532,2533],{},[232,2534,2535],{},"Admins can make platform-wide announcements (e.g., new features, updates).",[1139,2537,2539],{"id":2538},"_16-third-party-integrations",[235,2540,2541],{},"16. Third-Party Integrations",[239,2543,2544,2554,2564],{},[232,2545,2546,387,2549],{},[235,2547,2548],{},"Social Media Integration",[239,2550,2551],{},[232,2552,2553],{},"Allow users to sign up and log in using their Google, Facebook, or LinkedIn accounts.",[232,2555,2556,387,2559],{},[235,2557,2558],{},"Analytics Tools",[239,2560,2561],{},[232,2562,2563],{},"Integrate Google Analytics or other analytics tools to track user behavior and engagement.",[232,2565,2566,387,2569],{},[235,2567,2568],{},"Email Marketing",[239,2570,2571],{},[232,2572,2573],{},"Integrate tools like Mailchimp for sending newsletters and updates.",[1136,2575],{},[1139,2577,2579],{"id":2578},"optional-advanced-features",[235,2580,2581],{},"Optional Advanced Features",[229,2583,2584,2594,2604,2614,2624,2634],{},[232,2585,2586,387,2589],{},[235,2587,2588],{},"Gamification",[239,2590,2591],{},[232,2592,2593],{},"Introduce a points-based system for students to earn points and rewards as they complete courses or perform well in quizzes.",[232,2595,2596,387,2599],{},[235,2597,2598],{},"AI-Powered Course Recommendations",[239,2600,2601],{},[232,2602,2603],{},"Use machine learning algorithms to suggest courses based on a student’s previous activity or interests.",[232,2605,2606,387,2609],{},[235,2607,2608],{},"Multilingual Support",[239,2610,2611],{},[232,2612,2613],{},"Implement multi-language support to cater to students from different regions.",[232,2615,2616,387,2619],{},[235,2617,2618],{},"Plagiarism Detection",[239,2620,2621],{},[232,2622,2623],{},"Use third-party plagiarism detection tools to check student assignments for originality.",[232,2625,2626,387,2629],{},[235,2627,2628],{},"Offline Mode",[239,2630,2631],{},[232,2632,2633],{},"Allow students to download course content for offline access.",[232,2635,2636,387,2639],{},[235,2637,2638],{},"Video Playback with Annotations",[239,2640,2641],{},[232,2642,2643],{},"Allow students to annotate videos while watching lectures and save their notes for future reference.",[1136,2645],{},[1139,2647,2649],{"id":2648},"summary",[235,2650,2651],{},"Summary",[210,2653,2654],{},"A full-featured LMS using the MERN stack can include a wide array of features ranging from basic user management and course delivery to advanced features like real-time chat, video conferencing, and gamification. The extent of features will depend on your project scope, but the features listed above will give you a comprehensive, scalable, and user-friendly LMS.",{"title":287,"searchDepth":328,"depth":328,"links":2656},[2657,2658,2659,2660,2661,2662,2663,2664,2665,2666,2667,2668,2669,2670,2671,2672,2673,2674,2675,2676,2677,2678,2679,2680],{"id":1213,"depth":334,"text":1216},{"id":1259,"depth":334,"text":1262},{"id":1489,"depth":334,"text":1492},{"id":1600,"depth":334,"text":1603},{"id":1828,"depth":334,"text":1831},{"id":1130,"depth":334,"text":1131},{"id":1893,"depth":334,"text":1896},{"id":1960,"depth":334,"text":1963},{"id":2038,"depth":334,"text":2041},{"id":2086,"depth":334,"text":2089},{"id":2151,"depth":334,"text":2154},{"id":2189,"depth":334,"text":2192},{"id":2227,"depth":334,"text":2230},{"id":2275,"depth":334,"text":2278},{"id":2313,"depth":334,"text":2316},{"id":2361,"depth":334,"text":2364},{"id":2399,"depth":334,"text":2402},{"id":2427,"depth":334,"text":2430},{"id":2455,"depth":334,"text":2458},{"id":2482,"depth":334,"text":2485},{"id":2510,"depth":334,"text":2513},{"id":2538,"depth":334,"text":2541},{"id":2578,"depth":334,"text":2581},{"id":2648,"depth":334,"text":2651},"2025-08-24","Create a Learning Management System using MERN stack with course management, user roles, authentication, and file storage in this comprehensive guide","https:\u002F\u002Fcdn.hashnode.com\u002Fres\u002Fhashnode\u002Fimage\u002Fupload\u002Fv1756738480908\u002Fd3df4aab-3170-4d1c-b4ec-a3eb1858f84a.png",{},{"title":34,"description":2682},"2WHN9nr3VATFADeAnh8tYqq4jAq9oOc7osUbDOeBYhA",{"id":2688,"title":14,"author":2689,"body":2692,"date":3067,"description":3068,"extension":1191,"image":3069,"meta":3070,"minRead":334,"navigation":165,"path":15,"seo":3071,"stem":16,"__hash__":3072},"blog\u002Fblog\u002Fbackground-dns-refresh-in-nodejs-keeping-cached-ips-updated.md",{"name":201,"to":202,"username":203,"avatar":2690},{"src":2691,"alt":201},"https:\u002F\u002Fcdn.hashnode.com\u002Fres\u002Fhashnode\u002Fimage\u002Fupload\u002Fv1733546636355\u002Fa4ae8ce5-eb4c-4ed8-ab9d-15a4b8f32ea5.png?w=500&h=500&fit=crop&crop=entropy&auto=compress,format&format=webp",{"type":207,"value":2693,"toc":3045},[2694,2699,2708,2711,2714,2731,2734,2738,2742,2745,2759,2763,2770,2777,2784,2791,2795,2798,2802,2805,2822,2826,2833,2839,2845,2851,2855,2859,2865,2869,2875,2879,2885,2889,2970,2974,3006,3010,3013,3017,3031,3035,3038],[2695,2696,2698],"h2",{"id":2697},"introduction","📌 Introduction",[210,2700,2701,2702,2707],{},"In our previous blog on ",[1148,2703,2706],{"href":2704,"rel":2705},"https:\u002F\u002Fsachinthapa.hashnode.dev\u002Fdns-caching-in-nodejs-why-and-how-to-optimize-api-performance",[1152],"DNS Caching in Node.js",", we learned how caching resolved IP addresses improves performance and prevents frequent DNS lookups. However, one challenge remains: what happens when the TTL expires?",[210,2709,2710],{},"With basic DNS caching, when an IP address expires, our system performs a fresh DNS lookup before serving the next request. This creates latency spikes and blocking operations for users. To fix this problem, we need to implement background DNS refresh.",[210,2712,2713],{},"In this comprehensive guide, we'll cover:",[239,2715,2716,2719,2722,2725,2728],{},[232,2717,2718],{},"Why background DNS refresh is essential for consistent performance",[232,2720,2721],{},"How it eliminates latency spikes in high-traffic applications",[232,2723,2724],{},"A detailed implementation in Node.js with scheduled updates",[232,2726,2727],{},"Performance benchmarks comparing on-demand and background refresh",[232,2729,2730],{},"Best practices for production environments",[210,2732,2733],{},"By the end, you'll know how to keep cached DNS records fresh without ever blocking requests! 🚀",[2695,2735,2737],{"id":2736},"the-problem-delayed-dns-refresh-causes-latency-spikes","❌ The Problem: Delayed DNS Refresh Causes Latency Spikes",[1139,2739,2741],{"id":2740},"how-basic-dns-caching-works","🔹 How Basic DNS Caching Works",[210,2743,2744],{},"With the basic caching approach:",[229,2746,2747,2750,2753,2756],{},[232,2748,2749],{},"We resolve a domain name to an IP and cache it until the TTL expires",[232,2751,2752],{},"After TTL expiration, the next request triggers a fresh DNS lookup",[232,2754,2755],{},"That specific request experiences a delay while waiting for DNS resolution",[232,2757,2758],{},"Subsequent requests are fast again until the next expiration",[1139,2760,2762],{"id":2761},"issues-with-this-approach","🔹 Issues with This Approach",[210,2764,2765,2766,2769],{},"1️⃣ ",[235,2767,2768],{},"Latency Spikes"," – Users experience periodic delays when an expired cache is refreshed",[210,2771,2772,2773,2776],{},"2️⃣ ",[235,2774,2775],{},"Blocking Requests"," – The first request after expiration waits for a DNS resolution",[210,2778,2779,2780,2783],{},"3️⃣ ",[235,2781,2782],{},"Inconsistent Performance"," – Some requests are fast (cached IPs), others are slow (DNS lookups)",[210,2785,2786,2787,2790],{},"4️⃣ ",[235,2788,2789],{},"Poor User Experience"," – Unpredictable response times frustrate users",[2695,2792,2794],{"id":2793},"solution-refresh-ips-in-the-background","📌 Solution: Refresh IPs in the Background",[210,2796,2797],{},"Instead of waiting for a request to trigger a DNS resolution, we can refresh cached IPs proactively before they expire. This ensures continuous smooth operation without any request ever having to wait.",[2695,2799,2801],{"id":2800},"the-solution-implementing-background-dns-refresh","✅ The Solution: Implementing Background DNS Refresh",[210,2803,2804],{},"Our improved approach:",[229,2806,2807,2810,2813,2816,2819],{},[232,2808,2809],{},"Resolve and cache DNS records as before",[232,2811,2812],{},"Calculate when to refresh (typically at 80% of TTL)",[232,2814,2815],{},"Schedule background refresh tasks before TTL expiry",[232,2817,2818],{},"Keep cached records updated without impacting incoming requests",[232,2820,2821],{},"Ensure consistent performance for all users",[1139,2823,2825],{"id":2824},"benefits-of-background-dns-refresh","🔥 Benefits of Background DNS Refresh:",[210,2827,2828,2829,2832],{},"✅ ",[235,2830,2831],{},"Eliminates latency spikes"," – Requests never wait for DNS lookups",[210,2834,2828,2835,2838],{},[235,2836,2837],{},"Prevents blocking requests"," – Cached IPs are always ready before expiration",[210,2840,2828,2841,2844],{},[235,2842,2843],{},"Smoothens API performance"," – Provides consistent, predictable response times",[210,2846,2828,2847,2850],{},[235,2848,2849],{},"Handles DNS failures gracefully"," – If a refresh fails, the existing IP remains valid",[2695,2852,2854],{"id":2853},"implementation-background-dns-refresh-in-nodejs","🔧 Implementation: Background DNS Refresh in Node.js",[1139,2856,2858],{"id":2857},"step-1-enhance-the-cache-to-support-background-refresh","Step 1: Enhance the Cache to Support Background Refresh",[282,2860,2863],{"className":2861,"code":2862,"language":1622},[1620],"import express from \"express\";\nimport fetch from \"node-fetch\";\nimport * as dns from \"node:dns\";\n\nconst app = express();\nconst PORT = 3000;\nconst dnsCache = new Map();\nconst REFRESH_THRESHOLD = 0.8; \u002F\u002F Refresh when 80% of TTL has elapsed\n\nasync function resolveAndCache(domain) {\n    try {\n        const result = await dns.promises.resolve4(domain, { ttl: true });\n        if (result.length > 0) {\n            const { address, ttl } = result[0];\n            const now = Date.now();\n            const expiresAt = now + ttl * 1000;\n\n            \u002F\u002F Calculate when to refresh (before expiry)\n            const refreshAt = now + (ttl * REFRESH_THRESHOLD * 1000);\n\n            \u002F\u002F Store both expiry and refresh times\n            dnsCache.set(domain, { \n                address, \n                expiresAt,\n                refreshAt\n            });\n\n            console.log(`Updated IP for ${domain}: ${address} (TTL: ${ttl}s)`);\n\n            \u002F\u002F Schedule background refresh before expiry\n            const timeUntilRefresh = refreshAt - now;\n            setTimeout(() => refreshDomain(domain), timeUntilRefresh);\n\n            return address;\n        }\n    } catch (error) {\n        console.error(`DNS lookup failed for ${domain}:`, error);\n    }\n    return null;\n}\n",[245,2864,2862],{"__ignoreMap":287},[1139,2866,2868],{"id":2867},"step-2-set-up-a-background-refresh-process","Step 2: Set Up a Background Refresh Process",[282,2870,2873],{"className":2871,"code":2872,"language":1622},[1620],"async function refreshDomain(domain) {\n    console.log(`Background refresh for ${domain}`);\n    const cached = dnsCache.get(domain);\n\n    \u002F\u002F Only refresh if this entry still exists and hasn't been refreshed already\n    if (cached && Date.now() \u003C cached.expiresAt && Date.now() >= cached.refreshAt) {\n        await resolveAndCache(domain);\n    }\n}\n\n\u002F\u002F Function to get a cached IP (with fallback to resolution)\nasync function getCachedIP(domain) {\n    const now = Date.now();\n\n    \u002F\u002F Check if domain is cached and still valid\n    if (dnsCache.has(domain)) {\n        const { address, expiresAt } = dnsCache.get(domain);\n        if (now \u003C expiresAt) {\n            return address;\n        }\n    }\n\n    \u002F\u002F Not cached or expired, resolve now\n    return await resolveAndCache(domain);\n}\n\n\u002F\u002F Initialize cache for commonly used domains\nasync function initializeCache(domains) {\n    for (const domain of domains) {\n        await resolveAndCache(domain);\n    }\n}\n\nconst monitoredDomains = [\"api.weather.com\", \"api.payments.com\", \"api.auth.com\"];\ninitializeCache(monitoredDomains);\n",[245,2874,2872],{"__ignoreMap":287},[1139,2876,2878],{"id":2877},"step-3-use-cached-ips-without-delays","Step 3: Use Cached IPs Without Delays",[282,2880,2883],{"className":2881,"code":2882,"language":1622},[1620],"app.get(\"\u002Fweather\", async (req, res) => {\n    const weatherAPI = \"api.weather.com\";\n    try {\n        const ip = await getCachedIP(weatherAPI);\n        if (!ip) return res.status(500).json({ error: \"DNS lookup failed\" });\n\n        const response = await fetch(`http:\u002F\u002F${ip}\u002Fdata`, {\n            headers: { 'Host': 'api.weather.com' }\n        });\n\n        const data = await response.json();\n        res.json({ success: true, data });\n    } catch (error) {\n        res.status(500).json({ error: \"Failed to fetch weather data\" });\n    }\n});\n\napp.listen(PORT, () => console.log(`Server running on port ${PORT}`));\n",[245,2884,2882],{"__ignoreMap":287},[2695,2886,2888],{"id":2887},"performance-comparison-with-vs-without-background-refresh","⚡ Performance Comparison: With vs. Without Background Refresh",[975,2890,2891,2904],{},[978,2892,2893],{},[981,2894,2895,2898,2901],{},[984,2896,2897],{},"Feature",[984,2899,2900],{},"Without Background Refresh",[984,2902,2903],{},"With Background Refresh",[1000,2905,2906,2916,2926,2937,2948,2959],{},[981,2907,2908,2910,2913],{},[1005,2909,2768],{},[1005,2911,2912],{},"Present (after TTL expiry)",[1005,2914,2915],{},"Eliminated completely",[981,2917,2918,2920,2923],{},[1005,2919,2775],{},[1005,2921,2922],{},"First request after expiry is slow",[1005,2924,2925],{},"No blocking requests ever",[981,2927,2928,2931,2934],{},[1005,2929,2930],{},"DNS Lookup Frequency",[1005,2932,2933],{},"On-demand (delayed)",[1005,2935,2936],{},"Periodic (proactive)",[981,2938,2939,2942,2945],{},[1005,2940,2941],{},"Performance",[1005,2943,2944],{},"Inconsistent",[1005,2946,2947],{},"Smooth & consistent",[981,2949,2950,2953,2956],{},[1005,2951,2952],{},"p95 Latency",[1005,2954,2955],{},"150-250ms",[1005,2957,2958],{},"5-10ms",[981,2960,2961,2964,2967],{},[1005,2962,2963],{},"p99 Latency",[1005,2965,2966],{},"300-400ms",[1005,2968,2969],{},"10-15ms",[2695,2971,2973],{"id":2972},"best-practices-for-production","🎯 Best Practices for Production",[229,2975,2976,2982,2988,2994,3000],{},[232,2977,2978,2981],{},[235,2979,2980],{},"Adjust the refresh threshold"," based on your application's needs (80% of TTL is a good starting point)",[232,2983,2984,2987],{},[235,2985,2986],{},"Monitor DNS resolution times"," to identify performance issues",[232,2989,2990,2993],{},[235,2991,2992],{},"Implement error handling"," in refresh operations to avoid crashing the application",[232,2995,2996,2999],{},[235,2997,2998],{},"Consider memory usage"," when caching many domains",[232,3001,3002,3005],{},[235,3003,3004],{},"Use health checks"," to ensure your background refresh process is working properly",[2695,3007,3009],{"id":3008},"final-thoughts","🚀 Final Thoughts",[210,3011,3012],{},"✅ Background DNS refresh ensures that DNS lookups never block a request, eliminating latency spikes and providing consistent performance.",[1139,3014,3016],{"id":3015},"ideal-use-cases","💡 Ideal Use Cases:",[239,3018,3019,3022,3025,3028],{},[232,3020,3021],{},"APIs handling thousands of requests per second",[232,3023,3024],{},"Microservices communicating with third-party services",[232,3026,3027],{},"IoT devices that need uninterrupted connectivity",[232,3029,3030],{},"Payment gateways, streaming services, and real-time applications",[1139,3032,3034],{"id":3033},"next-steps","🔥 Next Steps",[210,3036,3037],{},"In our next blog post, we'll explore how to implement a robust DNS fallback mechanism that can handle DNS resolution failures gracefully, ensuring your application stays operational even when DNS providers experience issues.",[210,3039,3040,3041,3044],{},"Check out our previous blog on ",[1148,3042,2706],{"href":2704,"rel":3043},[1152]," and stay tuned for our upcoming post on DNS Fallback Mechanisms! 🚀",{"title":287,"searchDepth":328,"depth":328,"links":3046},[3047,3048,3052,3053,3056,3061,3062,3063],{"id":2697,"depth":328,"text":2698},{"id":2736,"depth":328,"text":2737,"children":3049},[3050,3051],{"id":2740,"depth":334,"text":2741},{"id":2761,"depth":334,"text":2762},{"id":2793,"depth":328,"text":2794},{"id":2800,"depth":328,"text":2801,"children":3054},[3055],{"id":2824,"depth":334,"text":2825},{"id":2853,"depth":328,"text":2854,"children":3057},[3058,3059,3060],{"id":2857,"depth":334,"text":2858},{"id":2867,"depth":334,"text":2868},{"id":2877,"depth":334,"text":2878},{"id":2887,"depth":328,"text":2888},{"id":2972,"depth":328,"text":2973},{"id":3008,"depth":328,"text":3009,"children":3064},[3065,3066],{"id":3015,"depth":334,"text":3016},{"id":3033,"depth":334,"text":3034},"2025-03-11","Learn how to implement background DNS refresh in Node.js to maintain consistent performance and eliminate latency spikes in high-traffic applications","\u002Fimages\u002Fblog\u002Fbackground-dns-refresh-in-nodejs-keeping-cached-ips-updated.png",{},{"title":14,"description":3068},"o60gjFJTCvrXQkO1HOjDfxkAbQsN2sJmRQS6xjaHJ08"]