{"slug": "how-i-built-a-free-ai-prompt-library-with-node-js-and-postgresql-from-zero-to-in", "title": "How I Built a Free AI Prompt Library With Node.js and PostgreSQL — From Zero to Live in 30 Days", "summary": "A developer built PromptZyo, a free AI prompt library for professionals, using Node.js, Express, EJS, and PostgreSQL, deploying it live on Railway in 30 days. The site serves structured prompts organized by profession to help users get better results from AI tools like ChatGPT and Claude.", "body_md": "If you have been following my Dev.to articles, you know I built Chatzyo — a real-time video chat platform using WebRTC. That project taught me a lot about real-time infrastructure, WebSocket connections, and scaling live traffic.\n\nPromptZyo is completely different. No WebSockets. No real-time anything. Just a well-structured content site built for SEO and passive income.\n\nHere is the honest technical breakdown of how I built it, the decisions I made, and what I would do differently.\n\nMost professionals — teachers, lawyers, HR managers, nurses — know that ChatGPT and Claude can help them. But they keep getting generic, useless results.\n\nThe problem is not the AI tool.The problem is the prompt.\n\nA vague prompt: \"write a lesson plan \"Result: generic, unusable output\n\nA specific prompt: \"write a 45-minute lesson plan for Grade 5 students on\n\nmultiplying fractions for the first time, for a class that excels at whole number operations but has never seen fraction multiplication\"\n\nResult: genuinely useful first draft\n\nNobody had built a library of professionally structured prompts organized by profession. So I built [PromptZyo](https://promptzyo.com) — a free AI prompt library for professionals.\n\nI know Node well from Chatzyo. For a content-heavy site with PostgreSQL queries and server-side rendering, Express is fast, lightweight, and does exactly what I need.\n\nNo need for a heavy framework. Express + EJS handles the routing and\n\ntemplating cleanly.\n\nThis was a deliberate decision.For Chatzyo I used React for the interactive UI. For PromptZyo — a content library where SEO matters more than interactivity — server-side rendering with EJS is the right choice.\n\nFor a prompt library where I need 150+ pages indexed quickly — EJS wins.\n\nMy initial instinct was MongoDB — I know it well and prompts seem like\n\ndocument data.\n\nBut the relational structure changed my mind:\n\nThis is relational data. PostgreSQL handles it cleanly with foreign keys and JOIN queries. MongoDB would have required manual relationship management.\n\nThe query that convinced me:\n\n```\nSELECT p.*, \n       pr.name as profession_name,\n       pr.slug as profession_slug,\n       c.name as category_name\nFROM prompts p\nLEFT JOIN professions pr ON p.profession_id = pr.id\nLEFT JOIN categories c ON p.category_id = c.id\nWHERE pr.slug = $1 AND p.is_active = true\nORDER BY p.created_at DESC\n```\n\nClean, fast, readable. PostgreSQL was the right call.\n\nFor Chatzyo I use a different hosting setup. For PromptZyo I chose Railway for a few reasons:\n\nThe one gotcha: Railway's internal networking only works within the same project. My PostgreSQL and Node app are in the same Railway project — so the internal connection string works. If they were in different projects, I would need the public proxy URL which has higher latency.\n\nCloudflare sits in front of Railway and handles:\n\nSetup was straightforward — point Cloudflare nameservers at Namecheap, add CNAME record pointing to Railway's deployment URL, done.\n\nThe schema has 6 main tables:\n\n```\n-- Core content tables\nprofessions (id, name, slug, description, icon, is_active)\ncategories (id, name, slug, profession_id)\nprompts (id, title, slug, content, profession_id, \n         category_id, ai_tool, difficulty, \n         copy_count, is_active, created_at)\n\n-- User features (built but not launched yet)\nusers (id, google_id, email, name, created_at)\nsaved_prompts (user_id, prompt_id, saved_at)\n\n-- Blog\nblog_posts (id, title, slug, excerpt, content, \n            author, category, is_published, \n            published_at, reading_time)\n\n-- Sessions\nsession (sid, sess, expire)\n```\n\nThe `copy_count`\n\ncolumn tracks how many times each prompt has been copied. Every time a user clicks Copy Prompt, it fires a POST to\n\n`/api/track-copy/:id`\n\nwhich increments the count.\n\n``` js\nrouter.post('/track-copy/:id', async (req, res) => {\n  try {\n    await pool.query(\n      'UPDATE prompts SET copy_count = copy_count + 1 \n       WHERE id = $1',\n      [req.params.id]\n    );\n    res.json({ success: true });\n  } catch (err) {\n    console.error(err);\n    res.json({ error: 'Failed to track' });\n  }\n});\n```\n\nSimple but effective engagement tracking.\n\nThis is where PromptZyo differs most from Chatzyo. Chatzyo's traffic came from localized pages. PromptZyo's traffic will come from a different content structure.\n\nEach URL is a crawlable page with unique\n\ntitle, meta description, and canonical tag.\n\nEvery page type has appropriate schema:\n\n```\n// Profession pages\n{\n  \"@type\": \"CollectionPage\",\n  \"numberOfItems\": prompts.length,\n  \"breadcrumb\": { ... }\n}\n\n// Individual prompt pages  \n{\n  \"@type\": \"HowTo\",  // for the prompt itself\n  \"step\": [ ... ]\n}\n{\n  \"@type\": \"FAQPage\",  // for the FAQ section\n  \"mainEntity\": [ ... ]\n}\n\n// Blog posts\n{\n  \"@type\": \"Article\",\n  \"author\": { \"@type\": \"Person\", ... },\n  \"datePublished\": \"...\",\n  \"dateModified\": \"...\"\n}\n```\n\nSchema markup helps Google understand what each page is — and can trigger\n\nrich results in search.\n\nThe sitemap generates dynamically from the database:\n\n``` js\nrouter.get('/', async (req, res) => {\n  const professions = await pool.query(\n    'SELECT slug FROM professions WHERE is_active = true'\n  );\n  const prompts = await pool.query(\n    'SELECT slug FROM prompts WHERE is_active = true'\n  );\n  const blogPosts = await pool.query(\n    'SELECT slug FROM blog_posts WHERE is_published = true'\n  );\n\n  // Build XML dynamically\n  let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n  <urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">`;\n\n  // Add all URLs...\n\n  res.setHeader('Content-Type', 'application/xml');\n  res.send(xml);\n});\n```\n\nEvery new prompt or blog post automatically appears in the sitemap. No manual updates needed.\n\nWhen a new blog post is published, it automatically notifies Bing via IndexNow:\n\n``` js\nasync function notifyIndexNow(urls) {\n  const key = process.env.INDEXNOW_KEY;\n\n  const data = JSON.stringify({\n    host: 'promptzyo.com',\n    key: key,\n    keyLocation: `https://promptzyo.com/${key}.txt`,\n    urlList: Array.isArray(urls) ? urls : [urls]\n  });\n\n  // POST to api.indexnow.org\n  // Bing indexes within hours instead of weeks\n}\n```\n\nNew content gets indexed by Bing within hours instead of waiting for the crawler to find it organically.\n\n121 prompts across 12 professions means 121 individual pages that Google can index. Each page has:\n\nThat is a lot of on-page content for what could have been a simple prompt card. But the content depth is what makes each page rankable — not just indexable.\n\nOn top of the prompt pages, I am building topical authority through blog articles.\n\nArticles like:\n\nEach article targets a specific high-volume keyword and links back to relevant profession pages and individual prompts.\n\nI built a custom admin panel rather than using a CMS. It handles:\n\nThe bulk import route accepts a JSON array:\n\n``` js\nrouter.post('/bulk-import', isAdmin, async (req, res) => {\n  const { profession_id, ai_tool, \n          difficulty, prompts_json } = req.body;\n\n  const prompts = JSON.parse(prompts_json);\n\n  for (const prompt of prompts) {\n    const slug = prompt.title\n      .toLowerCase()\n      .replace(/[^a-z0-9]+/g, '-')\n      .replace(/(^-|-$)/g, '');\n\n    await pool.query(`\n      INSERT INTO prompts \n        (title, slug, content, profession_id, \n         category_id, ai_tool, difficulty)\n      VALUES ($1, $2, $3, $4, $5, $6, $7)\n      ON CONFLICT (slug) DO NOTHING\n    `, [prompt.title, slug, prompt.content, \n        profession_id, prompt.category_id, \n        ai_tool, difficulty]);\n  }\n\n  res.render('admin-bulk', { success: `Added ${added} prompts` });\n});\n```\n\nThis let me add 10 prompts per profession in one paste rather than one by one.\n\nBuilding Chatzyo taught me real-time infrastructure. Building PromptZyo\n\ntaught me something different:\n\n**Content architecture at scale.**\n\nWith Chatzyo, the technical challenge was keeping WebSocket connections stable and matching users in real time. With PromptZyo, the challenge is making 150+ pages rankable individually while maintaining site-wide\n\nconsistency.\n\nDifferent problems, different solutions, both valuable.\n\nZero organic traffic at day 30 is completely normal for a new domain. Google takes 3-6 months to trust new sites. I built this expecting\n\nnothing until month 3.\n\nThe SEO foundation is solid. Now it is a waiting game combined with consistent content production.\n\n**1. Start the blog earlier**\n\nI launched the prompt library first and added the blog later. In hindsight, I should have had 5 blog articles ready before launch. Blog content builds topical authority faster than prompt pages alone.\n\n**2. Use a CDN for images from day one**\n\nI added Cloudflare later. Should have set it up before launch — it is free and the performance difference is immediate.\n\n**3. Build the admin panel first**\n\nI added features to the admin panel throughout the build. Building it\n\ncomprehensively at the start would have saved time later.\n\n[PromptZyo](https://promptzyo.com) is completely free. 121 prompts across\n\n12 professions. No account needed.\n\nIf you are building something similar — a content library, a tool directory, anything SEO-focused — happy to answer questions in the comments.\n\nAnd if you read my [Chatzyo articles](https://dev.to/gowrishankar_rangasamy_f9)\n\n— thank you for following along. Building two very different products\n\nback to back has been the best technical education I could have given myself.\n\n*Built with Node.js, Express, EJS, PostgreSQL, Railway and Cloudflare.*\n\n*Previous build: How I Built an Anonymous Chat Platform Without Signup Using WebRTC*", "url": "https://wpnews.pro/news/how-i-built-a-free-ai-prompt-library-with-node-js-and-postgresql-from-zero-to-in", "canonical_source": "https://dev.to/gowrishankar_rangasamy_f9/how-i-built-a-free-ai-prompt-library-with-nodejs-and-postgresql-from-zero-to-live-in-30-days-2n75", "published_at": "2026-07-24 15:01:18+00:00", "updated_at": "2026-07-24 15:37:21.768052+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "large-language-models"], "entities": ["PromptZyo", "Chatzyo", "Node.js", "Express", "PostgreSQL", "Railway", "Cloudflare", "EJS"], "alternates": {"html": "https://wpnews.pro/news/how-i-built-a-free-ai-prompt-library-with-node-js-and-postgresql-from-zero-to-in", "markdown": "https://wpnews.pro/news/how-i-built-a-free-ai-prompt-library-with-node-js-and-postgresql-from-zero-to-in.md", "text": "https://wpnews.pro/news/how-i-built-a-free-ai-prompt-library-with-node-js-and-postgresql-from-zero-to-in.txt", "jsonld": "https://wpnews.pro/news/how-i-built-a-free-ai-prompt-library-with-node-js-and-postgresql-from-zero-to-in.jsonld"}}