[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fqHhNKKdDc8JLmK5gdkPZzuLnon5lsyc-v8iUxxv1ZsM":3,"$fqbhg9h0oxN6Ek4prwIHy_h_p4aavAvrS8sUy1TxB2Qs":23},{"id":4,"slug":5,"titleFr":6,"titleEn":7,"titleKm":8,"contentFr":9,"contentEn":10,"contentKm":11,"excerptFr":12,"excerptEn":13,"excerptKm":14,"tags":15,"createdAt":22,"updatedAt":22},101,"nuxt4-neon-drizzle-portfolio","Construire un portfolio dynamique avec Nuxt 4, Neon et Drizzle","Building a dynamic portfolio with Nuxt 4, Neon and Drizzle","បង្កើតផលប័ត្រថាមវន្តជាមួយ Nuxt 4, Neon និង Drizzle","## Introduction\n\nPendant 15 ans, mon CV a été un fichier HTML statique. Un seul fichier, hébergé gratuitement, qui faisait le job. Puis un jour, j'ai voulu ajouter un blog. Puis des projets. Puis des commentaires. Puis du multilingue. Et j'ai réalisé que mon fichier HTML ne suffisait plus.\n\nPlutôt que d'empiler du JavaScript vanilla et des appels fetch bricolés, j'ai décidé de tout reconstruire avec une stack moderne. Le résultat : **chetana.dev** — un portfolio dynamique construit avec Nuxt 4, Neon PostgreSQL et Drizzle ORM, déployé sur Vercel.\n\nCet article est un retour d'expérience complet : les choix techniques, les pièges rencontrés, et ce que j'ai appris en tant que développeur backend Java qui découvre l'écosystème JavaScript moderne.\n\n---\n\n## Chapitre 1 : Pourquoi migrer ?\n\n### Les limites du HTML statique\n\nMon ancien CV fonctionnait bien pour ce qu'il était :\n- Un fichier `index.html` de 300 lignes\n- Du CSS inline\n- Hébergé sur GitHub Pages\n- Aucune dépendance, aucun build, aucun serveur\n\nMais dès que j'ai voulu aller plus loin, les limites sont apparues :\n\n- **Pas de blog** : ajouter des articles signifie créer des fichiers HTML manuellement\n- **Pas de données dynamiques** : chaque modification nécessite un commit + push\n- **Pas de commentaires** : impossible sans backend\n- **Pas de multilingue propre** : dupliquer le HTML pour chaque langue ? Non merci\n- **Pas de SEO avancé** : pas de sitemap dynamique, pas de JSON-LD, pas d'OG tags par page\n\n### Le déclic\n\nLe déclic est venu quand j'ai voulu montrer mes compétences en tant qu'Engineering Manager. Un CV statique montre que je sais coder du HTML. Un portfolio dynamique montre que je sais **concevoir, architecturer et déployer une application complète**.\n\n## Chapitre 2 : Le choix de la stack\n\n### Pourquoi Nuxt 4 (et pas Next.js) ?\n\nEn tant que développeur Java, React et Next.js semblaient le choix évident (plus populaire, plus d'offres d'emploi). Mais j'ai choisi Nuxt/Vue pour plusieurs raisons :\n\n**1. La courbe d'apprentissage**\nVue est plus accessible que React pour un développeur backend. Le template HTML + script + style dans un fichier `.vue` ressemble à ce qu'on connaît. Pas de JSX, pas de hooks complexes, pas de \"mental model\" à repenser.\n\n**2. Le système de fichiers comme routeur**\nNuxt 4 utilise le file-based routing : un fichier `pages/blog/[slug].vue` crée automatiquement la route `/blog/:slug`. Pour un développeur habitué aux routes Spring Boot (`@GetMapping`), c'est intuitif.\n\n**3. Les server routes intégrées**\nNuxt inclut Nitro, un serveur HTTP qui permet de créer des API REST directement dans le projet. Un fichier `server/api/blog/index.get.ts` crée un endpoint `GET /api/blog`. Pas besoin d'un backend séparé.\n\n**4. Le SSR natif**\nLe Server-Side Rendering est crucial pour le SEO. Nuxt le fait nativement, sans configuration. Chaque page est rendue côté serveur au premier chargement, puis hydratée côté client.\n\n### Pourquoi Neon PostgreSQL ?\n\nJ'ai envisagé plusieurs options pour la base de données :\n\n| Option | Avantage | Inconvénient |\n|--------|----------|--------------|\n| SQLite fichier | Simple, gratuit | Pas de cloud, pas de serverless |\n| Supabase | UI admin, auth intégrée | Overhead pour un portfolio |\n| PlanetScale | MySQL serverless | MySQL, pas PostgreSQL |\n| Neon | PostgreSQL serverless, gratuit | Moins connu |\n\nJ'ai choisi Neon pour :\n- **PostgreSQL** : je connais PostgreSQL depuis 10 ans (DJUST, Galeries Lafayette, INFOTEL)\n- **Serverless** : le compute s'allume uniquement quand il y a une requête. Coût : 0€\n- **Free tier généreux** : 512 Mo de stockage, 191h de compute/mois\n- **Compatible Drizzle** : driver natif `@neondatabase/serverless`\n\n### Pourquoi Drizzle ORM ?\n\nVenant de Java/Hibernate, j'avais besoin d'un ORM. Les options en TypeScript :\n\n| ORM | Style | Type-safety |\n|-----|-------|-------------|\n| Prisma | Schema-first, migration auto | Bon, mais génère du code |\n| TypeORM | Decorators (style Hibernate) | Moyen |\n| Drizzle | SQL-like, schema-in-code | Excellent |\n\nDrizzle m'a convaincu parce que :\n- **Le schéma est du code TypeScript** : pas de fichier schema séparé, pas de génération de code\n- **Les requêtes ressemblent à du SQL** : `db.select().from(blogPosts).where(eq(...))` — un développeur SQL lit ça sans problème\n- **Type-safety de bout en bout** : le résultat d'une requête est typé automatiquement\n- **Léger** : pas de runtime heavy comme Prisma\n\n## Chapitre 3 : L'architecture\n\n### Structure du projet\n\n```\nchetana-dev/\n├── app/\n│   ├── pages/           # Routes (file-based routing)\n│   │   ├── index.vue    # Page d'accueil\n│   │   ├── blog/\n│   │   │   ├── index.vue    # Liste des articles\n│   │   │   └── [slug].vue   # Article individuel\n│   │   ├── projects/\n│   │   ├── cv.vue\n│   │   └── contact.vue\n│   ├── components/      # Composants réutilisables\n│   │   ├── BlogCard.vue\n│   │   ├── ProjectCard.vue\n│   │   ├── Timeline.vue\n│   │   └── CommentSection.vue\n│   └── composables/     # Logique partagée\n│       └── useI18n.ts   # Système i18n custom\n├── server/\n│   ├── api/             # API REST (Nitro)\n│   │   ├── blog/\n│   │   ├── experiences.get.ts\n│   │   ├── skills.get.ts\n│   │   └── comments/\n│   ├── db/\n│   │   ├── schema.ts    # Schéma Drizzle\n│   │   └── seed.ts      # Données initiales\n│   └── utils/\n│       └── db.ts        # Connexion Neon\n└── nuxt.config.ts\n```\n\n### Le pattern API\n\nChaque endpoint suit le même pattern :\n\n1. Importer la connexion DB (`server/utils/db.ts`)\n2. Utiliser Drizzle pour la requête\n3. Retourner le résultat (Nitro le sérialise en JSON automatiquement)\n\nC'est minimaliste et efficace. Pas de controllers, pas de services, pas de DTOs — juste des fonctions qui retournent des données.\n\n### Le système i18n\n\nPlutôt que d'utiliser une librairie i18n (qui entre en conflit avec nuxt-seo-utils), j'ai créé un composable custom `useLocale()` :\n\n- Un `ref` réactif pour la locale courante (fr/en/km)\n- Une fonction `t(key)` pour les traductions statiques\n- Une fonction `localeField(obj, field)` pour les données DB (sélectionne `titleFr`, `titleEn` ou `titleKm` selon la locale)\n- Fallback automatique vers le français si une traduction manque\n\n## Chapitre 4 : Les pièges rencontrés\n\n### Piège 1 : Le SSR et les composables\n\nEn Nuxt, les composables (`useLocale()`, `useRoute()`) ne fonctionnent que dans le contexte d'un composant Vue. Appeler `useLocale()` dans un fichier utilitaire classique provoque une erreur côté serveur.\n\n**Solution** : toujours appeler les composables dans `setup()` ou dans un `computed`, jamais dans une fonction importée globalement.\n\n### Piège 2 : Les seeds et l'idempotence\n\nAu début, mes scripts de seed faisaient des `INSERT` sans vérifier si les données existaient. Résultat : après 3 exécutions, j'avais 18 expériences au lieu de 6 et 126 skills au lieu de 42.\n\n**Solution** : chaque seed commence par un `DELETE` de toutes les données existantes, puis fait les `INSERT`. C'est brutal mais fiable. L'ordre des `DELETE` respecte les foreign keys (comments → blogPosts → experiences → skills → projects).\n\n### Piège 3 : Les variables d'environnement en local\n\nVercel injecte automatiquement `DATABASE_URL` en production. En local, j'utilise un fichier `.env.local`. Mais `dotenv` par défaut ne charge que `.env`, pas `.env.local`.\n\n**Solution** : ajouter explicitement `config({ path: '.env.local' })` dans chaque script de seed.\n\n### Piège 4 : Le rendu Markdown\n\nLes articles de blog sont stockés en Markdown dans la base de données. Mais Nuxt ne rend pas le Markdown nativement dans le template.\n\n**Solution** : un `computed` dans la page blog qui transforme le Markdown en HTML avec des regex : headers, listes, bold, italic, tables, sauts de ligne. C'est pas aussi complet qu'une librairie Markdown, mais ça suffit pour un blog technique.\n\n### Piège 5 : Le conflit nuxt-seo-utils / useI18n\n\nJ'avais nommé mon composable `useI18n` — le même nom que le composable de `vue-i18n`. Le module `@nuxtjs/seo` importe internement `vue-i18n` et le conflit faisait crasher le build.\n\n**Solution** : renommer le composable en `useLocale()` et l'exporter depuis un fichier nommé `useI18n.ts` (le nom du fichier ne pose pas problème, c'est le nom de la fonction exportée qui compte).\n\n## Chapitre 5 : Le déploiement sur Vercel\n\n### Pourquoi Vercel ?\n\n- **Zero config** : Vercel détecte Nuxt automatiquement et configure le build\n- **Edge network** : le site est servi depuis le CDN le plus proche du visiteur\n- **Auto-deploy** : chaque push sur `main` déclenche un déploiement\n- **Serverless functions** : les server routes Nuxt sont déployées comme des serverless functions\n- **Gratuit** pour un usage personnel\n\n### Le workflow de déploiement\n\n1. `git push origin main`\n2. Vercel détecte le push (webhook GitHub)\n3. Vercel exécute `npm run build` (Nuxt build)\n4. Les fichiers statiques vont sur le CDN\n5. Les server routes deviennent des serverless functions\n6. Le site est live en ~45 secondes\n\n### La connexion Neon ↔ Vercel\n\nNeon fournit une connection string PostgreSQL. Je la stocke dans Vercel comme variable d'environnement (`DATABASE_URL` et `NUXT_DATABASE_URL`).\n\nQuand une serverless function reçoit une requête :\n1. Le driver `@neondatabase/serverless` établit une connexion HTTP (pas TCP)\n2. La requête SQL est envoyée via HTTP à Neon\n3. Neon réveille le compute (si endormi), exécute la requête, retourne le résultat\n4. Le tout en 50-200ms (premier appel après cold start : ~500ms)\n\n## Chapitre 6 : Les résultats\n\n### Performance\n\n| Métrique | Résultat |\n|----------|----------|\n| Lighthouse Performance | 95+ |\n| First Contentful Paint | \u003C 1s |\n| Time to Interactive | \u003C 1.5s |\n| Taille du bundle JS | ~207 KB (gzippé : 77 KB) |\n| Cold start Neon | ~500ms |\n| Requête DB warm | 50-200ms |\n\n### SEO\n\n- **Sitemap dynamique** : génère automatiquement les URLs des articles et projets\n- **JSON-LD** : schema.org Person + BlogPosting sur chaque article\n- **OG/Twitter meta** : `useSeoMeta()` sur chaque page\n- **Robots** : la page CV est en `noindex` (contenu similaire à LinkedIn)\n\n### Coût mensuel\n\n| Service | Coût |\n|---------|------|\n| Vercel (Hobby) | 0€ |\n| Neon (Free tier) | 0€ |\n| Domaine chetana.dev | ~12€/an |\n| **Total** | **~1€/mois** |\n\n## Chapitre 7 : Ce que j'ai appris\n\n### En tant que développeur Java qui découvre le JavaScript moderne\n\n**Ce qui m'a surpris positivement :**\n- La **vitesse de développement** : de l'idée au déploiement en quelques heures, pas en quelques jours\n- Le **hot reload** : modifier un composant et voir le résultat instantanément, sans redémarrer un serveur Spring Boot\n- La **simplicité du déploiement** : `git push` et c'est en production. Pas de Jenkins, pas de Kubernetes, pas de Docker\n- Le **typage end-to-end** : Drizzle + TypeScript = les erreurs de types sont détectées à la compilation\n\n**Ce qui m'a manqué :**\n- La **rigueur de Java** : le typage de TypeScript est bon mais moins strict que Java. Les `any` sont tentants\n- L'**écosystème de tests** : JUnit + Mockito est plus mature que Vitest/Jest pour les tests complexes\n- La **stabilité** : l'écosystème JavaScript bouge trop vite. Ce qui est best practice aujourd'hui sera obsolète dans 6 mois\n\n### Le meilleur des deux mondes\n\nCe projet m'a convaincu que **Java et JavaScript sont complémentaires**, pas concurrents :\n\n- **Java** pour le backend lourd : transactions, multi-tenancy, intégrations enterprise, batch processing\n- **Nuxt/Vue** pour le frontend et les applications légères : portfolios, blogs, dashboards, outils internes\n\nUn développeur qui maîtrise les deux a un avantage considérable sur le marché.\n\n---\n\n*Chetana YIN — Février 2026*\n*Engineering Manager, développeur Java depuis 2008, converti Nuxt depuis 2025.*","## Introduction\n\nFor 15 years, my CV was a static HTML file. A single file, hosted for free, that did the job. Then one day, I wanted to add a blog. Then projects. Then comments. Then multilingual support. And I realized my HTML file wasn't enough anymore.\n\nRather than piling on vanilla JavaScript and hacky fetch calls, I decided to rebuild everything with a modern stack. The result: **chetana.dev** — a dynamic portfolio built with Nuxt 4, Neon PostgreSQL, and Drizzle ORM, deployed on Vercel.\n\nThis article is a complete experience report: the technical choices, the pitfalls encountered, and what I learned as a backend Java developer discovering the modern JavaScript ecosystem.\n\n---\n\n## Chapter 1: Why Migrate?\n\n### The Limits of Static HTML\n\nMy old CV worked well for what it was:\n- A 300-line `index.html` file\n- Inline CSS\n- Hosted on GitHub Pages\n- No dependencies, no build, no server\n\nBut as soon as I wanted to go further, the limits appeared:\n\n- **No blog**: adding articles means manually creating HTML files\n- **No dynamic data**: every modification requires a commit + push\n- **No comments**: impossible without a backend\n- **No proper multilingual**: duplicate the HTML for each language? No thanks\n- **No advanced SEO**: no dynamic sitemap, no JSON-LD, no per-page OG tags\n\n### The Trigger\n\nThe trigger came when I wanted to showcase my skills as an Engineering Manager. A static CV shows I can code HTML. A dynamic portfolio shows I can **design, architect, and deploy a complete application**.\n\n## Chapter 2: Choosing the Stack\n\n### Why Nuxt 4 (and Not Next.js)?\n\nAs a Java developer, React and Next.js seemed the obvious choice (more popular, more job offers). But I chose Nuxt/Vue for several reasons:\n\n**1. The Learning Curve**\nVue is more accessible than React for a backend developer. The HTML template + script + style in a single `.vue` file resembles what we already know. No JSX, no complex hooks, no \"mental model\" to rethink.\n\n**2. File-Based Routing**\nNuxt 4 uses file-based routing: a `pages/blog/[slug].vue` file automatically creates the `/blog/:slug` route. For a developer used to Spring Boot routes (`@GetMapping`), it's intuitive.\n\n**3. Built-in Server Routes**\nNuxt includes Nitro, an HTTP server that lets you create REST APIs directly in the project. A `server/api/blog/index.get.ts` file creates a `GET /api/blog` endpoint. No separate backend needed.\n\n**4. Native SSR**\nServer-Side Rendering is crucial for SEO. Nuxt does it natively, without configuration.\n\n### Why Neon PostgreSQL?\n\nI considered several database options:\n\n| Option | Advantage | Disadvantage |\n|--------|-----------|--------------|\n| SQLite file | Simple, free | No cloud, no serverless |\n| Supabase | Admin UI, built-in auth | Overhead for a portfolio |\n| PlanetScale | MySQL serverless | MySQL, not PostgreSQL |\n| Neon | PostgreSQL serverless, free | Less known |\n\nI chose Neon because:\n- **PostgreSQL**: I've known PostgreSQL for 10 years (DJUST, Galeries Lafayette, INFOTEL)\n- **Serverless**: compute spins up only when there's a request. Cost: $0\n- **Generous free tier**: 512 MB storage, 191h compute/month\n- **Drizzle compatible**: native `@neondatabase/serverless` driver\n\n### Why Drizzle ORM?\n\nComing from Java/Hibernate, I needed an ORM. TypeScript options:\n\n| ORM | Style | Type-safety |\n|-----|-------|-------------|\n| Prisma | Schema-first, auto migration | Good, but generates code |\n| TypeORM | Decorators (Hibernate-style) | Medium |\n| Drizzle | SQL-like, schema-in-code | Excellent |\n\nDrizzle convinced me because:\n- **Schema is TypeScript code**: no separate schema file, no code generation\n- **Queries look like SQL**: `db.select().from(blogPosts).where(eq(...))` — any SQL developer reads this without issue\n- **End-to-end type-safety**: query results are automatically typed\n- **Lightweight**: no heavy runtime like Prisma\n\n## Chapter 3: The Architecture\n\n### Project Structure\n\nThe site follows Nuxt 4 conventions with a clear separation:\n- `app/pages/` — file-based routing (index, blog, projects, cv, contact)\n- `app/components/` — reusable Vue components (BlogCard, ProjectCard, Timeline, CommentSection)\n- `app/composables/` — shared logic (useLocale for i18n)\n- `server/api/` — REST API endpoints via Nitro\n- `server/db/` — Drizzle schema and seed scripts\n\n### The API Pattern\n\nEach endpoint follows the same pattern: import DB connection, use Drizzle for the query, return the result. Minimalist and efficient. No controllers, no services, no DTOs — just functions that return data.\n\n### The i18n System\n\nRather than using an i18n library (which conflicts with nuxt-seo-utils), I created a custom `useLocale()` composable:\n- A reactive `ref` for the current locale (fr/en/km)\n- A `t(key)` function for static translations\n- A `localeField(obj, field)` function for DB data (selects `titleFr`, `titleEn` or `titleKm` based on locale)\n- Automatic fallback to French if a translation is missing\n\n## Chapter 4: Pitfalls Encountered\n\n### Pitfall 1: SSR and Composables\nNuxt composables only work within Vue component context. Calling `useLocale()` in a regular utility file causes a server-side error. Solution: always call composables in `setup()` or `computed`.\n\n### Pitfall 2: Seed Idempotency\nInitially, seed scripts did `INSERT` without checking for existing data. After 3 runs: 18 experiences instead of 6 and 126 skills instead of 42. Solution: each seed starts with `DELETE`, respecting foreign key order.\n\n### Pitfall 3: Local Environment Variables\nVercel auto-injects `DATABASE_URL` in production. Locally, `dotenv` only loads `.env`, not `.env.local`. Solution: explicitly add `config({ path: '.env.local' })` in each seed script.\n\n### Pitfall 4: Markdown Rendering\nBlog posts are stored as Markdown in the database. Solution: a `computed` that transforms Markdown to HTML with regex (headers, lists, bold, italic, tables, line breaks).\n\n### Pitfall 5: The nuxt-seo-utils / useI18n Conflict\nI had named my composable `useI18n` — same name as vue-i18n's composable. The `@nuxtjs/seo` module imports vue-i18n internally, causing build crashes. Solution: rename to `useLocale()`.\n\n## Chapter 5: Deploying on Vercel\n\n### Why Vercel?\n- **Zero config**: Vercel auto-detects Nuxt and configures the build\n- **Edge network**: site served from the nearest CDN\n- **Auto-deploy**: every push to `main` triggers deployment\n- **Serverless functions**: Nuxt server routes deployed as serverless functions\n- **Free** for personal use\n\n### The Neon ↔ Vercel Connection\n\nWhen a serverless function receives a request:\n1. The `@neondatabase/serverless` driver establishes an HTTP connection (not TCP)\n2. The SQL query is sent via HTTP to Neon\n3. Neon wakes the compute (if sleeping), executes the query, returns the result\n4. All in 50-200ms (first call after cold start: ~500ms)\n\n## Chapter 6: Results\n\n### Performance\n\n| Metric | Result |\n|--------|--------|\n| Lighthouse Performance | 95+ |\n| First Contentful Paint | \u003C 1s |\n| Time to Interactive | \u003C 1.5s |\n| JS bundle size | ~207 KB (gzipped: 77 KB) |\n| Neon cold start | ~500ms |\n| Warm DB query | 50-200ms |\n\n### Monthly Cost\n\n| Service | Cost |\n|---------|------|\n| Vercel (Hobby) | $0 |\n| Neon (Free tier) | $0 |\n| chetana.dev domain | ~$12/year |\n| **Total** | **~$1/month** |\n\n## Chapter 7: What I Learned\n\n### As a Java Developer Discovering Modern JavaScript\n\n**What positively surprised me:**\n- **Development speed**: from idea to deployment in hours, not days\n- **Hot reload**: modify a component and see the result instantly, without restarting a Spring Boot server\n- **Deployment simplicity**: `git push` and it's in production. No Jenkins, no Kubernetes, no Docker\n- **End-to-end typing**: Drizzle + TypeScript = type errors caught at compilation\n\n**What I missed:**\n- **Java's rigor**: TypeScript's typing is good but less strict than Java. `any` is tempting\n- **Test ecosystem**: JUnit + Mockito is more mature than Vitest/Jest for complex tests\n- **Stability**: the JavaScript ecosystem moves too fast. Today's best practice is tomorrow's legacy\n\n### The Best of Both Worlds\n\nThis project convinced me that **Java and JavaScript are complementary**, not competing:\n- **Java** for heavy backend: transactions, multi-tenancy, enterprise integrations, batch processing\n- **Nuxt/Vue** for frontend and lightweight applications: portfolios, blogs, dashboards, internal tools\n\nA developer who masters both has a considerable market advantage.\n\n---\n\n*Chetana YIN — February 2026*\n*Engineering Manager, Java developer since 2008, Nuxt convert since 2025.*","## ហេតុអ្វីផ្លាស់ប្តូរពី HTML ស្ថិតិ?\n\nCV HTML សុទ្ធរបស់ខ្ញុំដំណើរការល្អ ប៉ុន្តែខ្ញុំចង់បន្ថែមប្លុក គម្រោង និងមតិយោបល់។ ជំនួសឱ្យការបន្ថែម JavaScript vanilla ខ្ញុំបានជ្រើសរើស stack ទំនើប។\n\n## Stack ដែលបានជ្រើសរើស\n\n- **Nuxt 4** សម្រាប់ SSR និង DX\n- **Neon PostgreSQL** សម្រាប់ DB serverless\n- **Drizzle ORM** សម្រាប់ type-safety\n- **Vercel** សម្រាប់ការដាក់ពង្រាយ\n\n## ស្ថាបត្យកម្ម\n\nគេហទំព័រប្រើ server routes របស់ Nuxt (Nitro) ដើម្បីផ្តល់ REST API ដែលសួរ Neon តាមរយៈ Drizzle។ Frontend ជា Vue 3 ជាមួយ composable i18n សម្រាប់ការគាំទ្រពហុភាសា។","Retour d'expérience complet sur la migration d'un CV HTML statique vers Nuxt 4 + Neon PostgreSQL + Drizzle ORM : choix techniques, pièges rencontrés, et leçons d'un développeur Java.","Complete experience report on migrating a static HTML CV to Nuxt 4 + Neon PostgreSQL + Drizzle ORM: technical choices, pitfalls, and lessons from a Java developer.","បទពិសោធន៍ពីការផ្លាស់ប្តូរ CV HTML ស្ថិតិទៅ Nuxt 4 + Neon + Drizzle។",[16,17,18,19,20,21],"Nuxt","Neon","Drizzle","Vue","TypeScript","Vercel","2025-12-19T19:00:00",[]]