├── .nvmrc ├── server ├── tsconfig.json └── routes │ └── rss.xml.ts ├── public ├── riyad.jpg ├── favicon.ico ├── not-found.jpg ├── favicon-16x16.png ├── favicon-32x32.png ├── apple-touch-icon.png ├── blogs-img │ ├── blog.jpg │ ├── blog1.jpg │ ├── blog2.jpg │ ├── blog3.jpg │ ├── blog4.jpg │ ├── blog5.jpg │ ├── blog6.jpg │ ├── blog7.png │ └── blog8.png ├── android-chrome-192x192.png └── android-chrome-512x512.png ├── .prettierrc ├── app ├── assets │ ├── css │ │ └── tailwind.css │ └── images │ │ ├── dark.png │ │ ├── logo.png │ │ ├── preview1.png │ │ ├── preview2.png │ │ ├── preview3.png │ │ └── preview4.png ├── types │ └── blog.ts ├── utils │ └── helper.ts ├── components │ ├── footer │ │ ├── site.vue │ │ ├── link.vue │ │ ├── connect.vue │ │ └── developer.vue │ ├── logo │ │ ├── arrow.vue │ │ ├── date.vue │ │ ├── tag.vue │ │ ├── name.vue │ │ ├── dogpow.vue │ │ ├── confused.vue │ │ ├── 404.vue │ │ └── dog.vue │ ├── main │ │ ├── hero.vue │ │ ├── footer.vue │ │ ├── trending.vue │ │ ├── recent.vue │ │ └── header.vue │ ├── archive │ │ ├── hero.vue │ │ └── card.vue │ ├── category │ │ ├── hero.vue │ │ ├── topic.vue │ │ └── card.vue │ ├── blog │ │ ├── Toc.vue │ │ ├── empty.vue │ │ ├── Header.vue │ │ └── card.vue │ ├── content │ │ └── ProseCode.vue │ └── OgImage │ │ ├── About.vue │ │ └── Test.vue ├── layouts │ └── default.vue ├── pages │ ├── [...slug].vue │ ├── index.vue │ ├── categories │ │ ├── index.vue │ │ └── [category].vue │ ├── about.vue │ └── blogs │ │ ├── [blog].vue │ │ └── index.vue ├── app.vue ├── router.options.ts └── data │ └── index.ts ├── tsconfig.json ├── .gitignore ├── eslint.config.mjs ├── .editorconfig ├── tailwind.config.js ├── .vscode └── settings.json ├── content.config.ts ├── .github └── workflows │ └── build.yml ├── content └── blogs │ ├── 6. how-to-fix-vuex-type-issue.md │ ├── 2. fix-tailwindcss-intellisense-in-nuxt3.md │ ├── 3. create-namespace-subdomain-connect-to-vercel.md │ ├── 4. fetch-content-data-render-pages.md │ ├── 5. vue3-awesome-library.md │ ├── 1. connect-namecheap-to-vercel.md │ ├── 7. redis-ttl-jitter-and-how-i-almost-crashed-a-server.md │ └── 8. float-make-my-dollar-float-away.md ├── LICENSE.md ├── package.json ├── nuxt.config.ts └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | v22.14.0 2 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /public/riyad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/riyad.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "semi": false, 4 | "printWidth": 100 5 | } 6 | -------------------------------------------------------------------------------- /app/assets/css/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /public/not-found.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/not-found.jpg -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/favicon-32x32.png -------------------------------------------------------------------------------- /app/assets/images/dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/app/assets/images/dark.png -------------------------------------------------------------------------------- /app/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/app/assets/images/logo.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/blogs-img/blog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog.jpg -------------------------------------------------------------------------------- /public/blogs-img/blog1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog1.jpg -------------------------------------------------------------------------------- /public/blogs-img/blog2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog2.jpg -------------------------------------------------------------------------------- /public/blogs-img/blog3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog3.jpg -------------------------------------------------------------------------------- /public/blogs-img/blog4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog4.jpg -------------------------------------------------------------------------------- /public/blogs-img/blog5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog5.jpg -------------------------------------------------------------------------------- /public/blogs-img/blog6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog6.jpg -------------------------------------------------------------------------------- /public/blogs-img/blog7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog7.png -------------------------------------------------------------------------------- /public/blogs-img/blog8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/blogs-img/blog8.png -------------------------------------------------------------------------------- /app/assets/images/preview1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/app/assets/images/preview1.png -------------------------------------------------------------------------------- /app/assets/images/preview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/app/assets/images/preview2.png -------------------------------------------------------------------------------- /app/assets/images/preview3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/app/assets/images/preview3.png -------------------------------------------------------------------------------- /app/assets/images/preview4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/app/assets/images/preview4.png -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nurRiyad/nuxt-blog/HEAD/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .data 5 | # Node dependencies 6 | node_modules 7 | # System files 8 | *.log 9 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | import withNuxt from './.nuxt/eslint.config.mjs' 3 | 4 | export default withNuxt({ 5 | rules: { 6 | 'vue/html-self-closing': 'off', 7 | }, 8 | }) 9 | -------------------------------------------------------------------------------- /app/types/blog.ts: -------------------------------------------------------------------------------- 1 | export interface BlogPost { 2 | title: string 3 | date: string 4 | description: string 5 | image: string 6 | alt: string 7 | ogImage: string 8 | tags: string[] 9 | published: boolean 10 | } 11 | -------------------------------------------------------------------------------- /app/utils/helper.ts: -------------------------------------------------------------------------------- 1 | export function makeFirstCharUpper(val: string) { 2 | if (val === '') return val 3 | const firstChar = val.at(0)?.toLocaleUpperCase() || '' 4 | const otherChar = val.slice(1) 5 | return firstChar + otherChar 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = space 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /app/components/footer/site.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /app/components/logo/arrow.vue: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /app/components/logo/date.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | // remember to fix this issue wint the tailwind/nuxt 2 | // https://github.com/tailwindlabs/tailwindcss-intellisense/issues/663#issuecomment-1316788128 3 | 4 | module.exports = { 5 | darkMode: 'class', 6 | content: [], 7 | theme: { 8 | extend: { 9 | fontFamily: { 10 | spacegrotesk: ['Space Grotesk', 'sans-serif'], 11 | }, 12 | }, 13 | }, 14 | plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')], 15 | } 16 | -------------------------------------------------------------------------------- /app/components/logo/tag.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /app/layouts/default.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 23 | -------------------------------------------------------------------------------- /app/pages/[...slug].vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 26 | -------------------------------------------------------------------------------- /app/components/footer/link.vue: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "editor.defaultFormatter": "esbenp.prettier-vscode", 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": "explicit" 6 | }, 7 | "tailwindCSS.files.exclude": [ 8 | "**/.git/**", 9 | "**/node_modules/**", 10 | "**/.hg/**", 11 | "**/.svn/**", 12 | "**/.nuxt/**" 13 | ], 14 | "cSpell.words": [ 15 | "cloudinary", 16 | "Namecheap", 17 | "nurriyad", 18 | "Nuxt", 19 | "spacegrotesk", 20 | "Tailwindcss", 21 | "vuex" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /app/components/footer/connect.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | -------------------------------------------------------------------------------- /app/components/main/hero.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | -------------------------------------------------------------------------------- /app/components/archive/hero.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | -------------------------------------------------------------------------------- /app/components/category/hero.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 24 | -------------------------------------------------------------------------------- /content.config.ts: -------------------------------------------------------------------------------- 1 | import { defineCollection, defineContentConfig } from '@nuxt/content' 2 | import { asRobotsCollection } from '@nuxtjs/robots/content' 3 | import { asSitemapCollection } from '@nuxtjs/sitemap/content' 4 | import { asOgImageCollection } from 'nuxt-og-image/content' 5 | 6 | export default defineContentConfig({ 7 | collections: { 8 | content: defineCollection({ 9 | ...asRobotsCollection({ 10 | type: 'page', 11 | source: '**/*.md', 12 | }), 13 | ...asSitemapCollection({ 14 | type: 'page', 15 | source: '**/*.md', 16 | }), 17 | ...asOgImageCollection({ 18 | type: 'page', 19 | source: '**/*.md', 20 | }), 21 | }), 22 | }, 23 | }) 24 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Node.js CI 2 | 3 | on: 4 | push: 5 | branches: ['main'] 6 | pull_request: 7 | branches: ['main'] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Install Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: 22 21 | cache: 'npm' 22 | 23 | - name: Install dependencies 24 | run: npm ci 25 | 26 | - name: Check Linting 27 | run: npm run lint 28 | 29 | - name: Check Format 30 | run: npm run format 31 | 32 | - name: Playgourd build check 33 | run: npm run build 34 | -------------------------------------------------------------------------------- /app/components/category/topic.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 26 | -------------------------------------------------------------------------------- /app/components/blog/Toc.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | -------------------------------------------------------------------------------- /app/pages/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 31 | -------------------------------------------------------------------------------- /app/app.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 46 | -------------------------------------------------------------------------------- /app/components/blog/empty.vue: -------------------------------------------------------------------------------- 1 | 26 | -------------------------------------------------------------------------------- /app/components/content/ProseCode.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 33 | 34 | 44 | -------------------------------------------------------------------------------- /content/blogs/6. how-to-fix-vuex-type-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How to fix vuex type issue 3 | date: 9th June 2024 4 | description: In recent vue project we see that vuex type not working properly. We will fix that type issue and make vuex type workable 5 | image: /blogs-img/blog6.jpg 6 | alt: How to fix vuex type issue 7 | ogImage: /blogs-img/blog6.jpg 8 | tags: ['vue', 'vuex'] 9 | published: true 10 | --- 11 | 12 | ### Introduction 13 | 14 | In recent version of our vue project, when we try to add vuex we see type error and vuex type not found. We can easily fix that issue. 15 | 16 | ### How to fix that issue 17 | 18 | 1. Create a `vuex.d.ts` file inside of your route project. 19 | 2. Pase this code in that file 20 | 21 | ```ts 22 | declare module 'vuex' { 23 | export * from 'vuex/types/index.d.ts' 24 | export * from 'vuex/types/helpers.d.ts' 25 | export * from 'vuex/types/logger.d.ts' 26 | export * from 'vuex/types/vue.d.ts' 27 | } 28 | ``` 29 | 30 | 3. That's it. Your are ok to go. 31 | -------------------------------------------------------------------------------- /server/routes/rss.xml.ts: -------------------------------------------------------------------------------- 1 | import { Feed } from 'feed' 2 | 3 | const basePath = 'https://nurriyad.com' 4 | 5 | export default defineEventHandler(async (event) => { 6 | setHeader(event, 'content-type', 'text/xml') 7 | const docs = await queryCollection(event, 'content').all() 8 | const feed = new Feed({ 9 | title: "Riyad's personal blog site", 10 | description: "Riyad's personal blog site", 11 | id: basePath, 12 | link: basePath, 13 | language: 'en', 14 | favicon: `${basePath}/favicon.ico`, 15 | copyright: 'MIT', 16 | author: { 17 | name: 'Al Asad Nur Riyad', 18 | email: 'asadnurriyad@gmail.com', 19 | link: basePath, 20 | }, 21 | }) 22 | 23 | // Add the feed items 24 | docs.forEach((doc) => { 25 | // console.log(doc) 26 | feed.addItem({ 27 | title: doc.title || '', 28 | id: basePath + doc.path, 29 | link: basePath + doc.path, 30 | description: doc.description, 31 | content: doc.description, 32 | date: new Date(doc.meta?.date as string), 33 | }) 34 | }) 35 | 36 | return feed.rss2() 37 | }) 38 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2023 Scott Chacon and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /app/router.options.ts: -------------------------------------------------------------------------------- 1 | import type { RouterConfig } from '@nuxt/schema' 2 | 3 | // https://router.vuejs.org/api/#routeroptions 4 | export default { 5 | scrollBehavior: (to, from, savedPosition) => { 6 | // scroll to hash, useful for using to="#some-id" in NuxtLink 7 | // ex: To Top 8 | if (to.hash) { 9 | // console.log('to.hash: ', to.hash) 10 | return { 11 | el: to.hash, 12 | top: 100, 13 | behavior: 'smooth', 14 | } 15 | } 16 | 17 | // The remainder is optional but maybe useful as well 18 | 19 | // if link is to same page, scroll to top with smooth behavior 20 | if (to === from) { 21 | return { 22 | left: 0, 23 | top: 0, 24 | behavior: 'smooth', 25 | } 26 | } 27 | 28 | // this will use saved scroll position on browser forward/back navigation 29 | return new Promise((resolve) => { 30 | setTimeout(() => { 31 | resolve({ 32 | left: savedPosition?.left || 0, 33 | top: savedPosition?.top || 0, 34 | }) 35 | }, 500) 36 | }) 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /app/components/category/card.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 45 | 46 | 51 | -------------------------------------------------------------------------------- /app/components/main/footer.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 31 | 32 | 45 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@nurriyad/nuxt-blog", 3 | "version": "2.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "nuxt build", 8 | "dev": "nuxt dev", 9 | "generate": "nuxt generate", 10 | "preview": "nuxt preview", 11 | "postinstall": "nuxt prepare", 12 | "lint": "eslint .", 13 | "lint:fix": "eslint . --fix", 14 | "format": "prettier --check ./", 15 | "format:fix": "prettier --write ./" 16 | }, 17 | "devDependencies": { 18 | "@formkit/auto-animate": "^0.9.0", 19 | "@iconify-json/bi": "^1.2.6", 20 | "@iconify-json/fa": "^1.2.2", 21 | "@iconify-json/mdi": "^1.2.3", 22 | "@iconify-json/noto": "^1.2.7", 23 | "@iconify-json/svg-spinners": "^1.2.4", 24 | "@nuxt/content": "^3.3.0", 25 | "@nuxt/eslint": "^1.4.1", 26 | "@nuxt/fonts": "^0.11.4", 27 | "@nuxt/icon": "^2.0.0", 28 | "@nuxt/image": "^1.11.0", 29 | "@nuxtjs/color-mode": "^3.3.2", 30 | "@nuxtjs/robots": "^5.5.5", 31 | "@nuxtjs/sitemap": "^7.4.7", 32 | "@nuxtjs/tailwindcss": "^6.11.4", 33 | "@stefanobartoletti/nuxt-social-share": "^0.6.1", 34 | "@tailwindcss/forms": "^0.5.7", 35 | "@tailwindcss/typography": "^0.5.18", 36 | "@vueuse/core": "^13.9.0", 37 | "@vueuse/nuxt": "^13.9.0", 38 | "eslint-config-prettier": "^10.1.8", 39 | "feed": "^4.2.2", 40 | "fuse.js": "^7.0.0", 41 | "nuxt": "^4.1.2", 42 | "nuxt-og-image": "^5.1.11", 43 | "prettier": "^3.6.2", 44 | "typescript": "^5.8.2", 45 | "unenv": "^1.10.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | import { seoData } from './app/data' 2 | 3 | // https://nuxt.com/docs/api/configuration/nuxt-config 4 | export default defineNuxtConfig({ 5 | compatibilityDate: '2024-09-30', 6 | 7 | modules: [ 8 | '@nuxt/icon', 9 | '@nuxt/image', 10 | '@nuxt/fonts', 11 | '@nuxt/eslint', 12 | '@vueuse/nuxt', 13 | '@nuxtjs/robots', 14 | '@nuxtjs/sitemap', 15 | 'nuxt-og-image', 16 | '@nuxt/content', 17 | '@nuxtjs/color-mode', 18 | '@nuxtjs/tailwindcss', 19 | '@formkit/auto-animate', 20 | '@stefanobartoletti/nuxt-social-share', 21 | ], 22 | 23 | app: { 24 | head: { 25 | charset: 'utf-16', 26 | viewport: 'width=device-width,initial-scale=1', 27 | title: seoData.title, 28 | titleTemplate: `%s - ${seoData.title}`, 29 | }, 30 | pageTransition: { name: 'page', mode: 'out-in' }, 31 | layoutTransition: { name: 'layout', mode: 'out-in' }, 32 | }, 33 | 34 | sitemap: { 35 | sources: [seoData.mySite], 36 | }, 37 | 38 | site: { 39 | url: seoData.mySite, 40 | name: 'Al Asad Nur Riyad', 41 | }, 42 | 43 | typescript: { 44 | strict: true, 45 | }, 46 | 47 | nitro: { 48 | prerender: { 49 | crawlLinks: true, 50 | routes: ['/', '/rss.xml'], 51 | }, 52 | }, 53 | 54 | colorMode: { 55 | classSuffix: '', 56 | preference: 'dark', 57 | fallback: 'light', 58 | }, 59 | 60 | content: { 61 | build: { 62 | markdown: { 63 | highlight: { 64 | theme: 'dracula', 65 | }, 66 | }, 67 | }, 68 | }, 69 | }) 70 | -------------------------------------------------------------------------------- /app/pages/categories/index.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 58 | -------------------------------------------------------------------------------- /app/components/footer/developer.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 49 | -------------------------------------------------------------------------------- /app/components/blog/Header.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 58 | -------------------------------------------------------------------------------- /content/blogs/2. fix-tailwindcss-intellisense-in-nuxt3.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To Fix TailwindCSS Intellisense In Nuxt3 Project 3 | date: 26th Jan 2023 4 | description: In Nuxt3 project tailwind css intellisense doesn't seems to work properly. In this blog I will share a workaround to fix this issue. 5 | image: /blogs-img/blog2.jpg 6 | alt: Hwo to fix tailwind intellisense in nuxt3 project 7 | ogImage: /blogs-img/blog2.jpg 8 | tags: ['nuxt', 'tailwindcss'] 9 | published: true 10 | --- 11 | 12 | ### Problems 13 | 14 | I had a Nuxt3 and TailwindCSS project. which was opened in VsCode. But the problem was, in my project the tailwind intellisense didn't working properly. I tried to reinstall the vscode tailwind extension but the problem didn't solve properly. Later after doing some research I found a [workaround](https://github.com/tailwindlabs/tailwindcss-intellisense/issues/663#issuecomment-1316788128), That I am sharing here today. 15 | 16 | ### Why It's Not working 17 | 18 | In our nuxt project we have a `.nuxt` directory. Nuxt uses the `.nuxt/` directory in development to generate your Vue application. And if we try to look properly there is also a file called `.nuxt/tailwind.config.cjs`, So tailwind find to config file in the same project, one is in your root directory and another one is in you `.nuxt` directory. 19 | 20 | ### Possible Workaround 21 | 22 | One possible solution is, In your project we call tell the extension to exclude the `.nuxt` directory. To exclude the `.nuxt` directory in your workspace, 23 | 24 | - Create a `/.vscode` folder in your project's root level. 25 | - Inside `.vscode` folder add a `settings.json` file 26 | - Copy the below code to `settings.json` file 27 | 28 | ```json 29 | // /.vscode/settings.json 30 | { 31 | "tailwindCSS.files.exclude": [ 32 | "**/.git/**", 33 | "**/node_modules/**", 34 | "**/.hg/**", 35 | "**/.svn/**", 36 | "**/.nuxt/**" 37 | ] 38 | } 39 | ``` 40 | 41 | Hopefully now tailwind intellisense start working properly. 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

Nuxt Blog A Personal Blog Site

8 | 9 | ### Built with 10 | 11 |

12 | 13 |

14 | 15 | Nuxt Blog built with [Nuxt4](https://nuxt.com), [Nuxt-Content3](https://content.nuxt.com/), [Vue3](https://vuejs.org) & [TailwindCss](https://tailwindcss.com/) 16 | 17 | ## Features 18 | 19 | - Write blog with markdown file 20 | - Auto generate category from blog post 21 | - Blog list page with search and pagination 22 | - About me page for user info 23 | - Auto generate table of content for blog post 24 | - Auto generate Sitemap 25 | - Url preview with Nuxt ogImage 26 | - Dark and light mode 27 | - Server Side Rendered(SSR) with Nuxt4 28 | - RSS feed 29 | 30 | ## How to Make This Blog Template Yours in 5 Minutes 31 | 32 | - Clone this repo or use it as a template 33 | - Go to `./app/data/index.ts` file & add your personal info 34 | - Then head over to the `./content/blogs` folder to add new blogs 35 | - Voilà! You've got a personalized blog site! 36 | 37 | ## Preview 38 | 39 |

40 | 41 | 42 | 43 | 44 | 45 |
46 | Live Demo 47 |
48 |

49 | 50 | ## Demo 51 | 52 | https://blog.nurriyad.com 53 | 54 | > Hosted on [Vercel](https://vercel.com/): `npm run build` 55 | 56 | ## Build Setup 57 | 58 | **Requires Node.js 20.19+** 59 | 60 | ```bash 61 | # install dependencies 62 | yarn install 63 | 64 | # serve in dev mode, with hot reload at localhost:5173 65 | yarn run dev 66 | 67 | # build for production 68 | yarn run build 69 | 70 | # serve in production mode 71 | yarn run preview 72 | 73 | ``` 74 | -------------------------------------------------------------------------------- /app/components/main/trending.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 63 | -------------------------------------------------------------------------------- /app/components/blog/card.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 69 | -------------------------------------------------------------------------------- /app/components/archive/card.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 73 | -------------------------------------------------------------------------------- /app/pages/categories/[category].vue: -------------------------------------------------------------------------------- 1 | 63 | 64 | 85 | -------------------------------------------------------------------------------- /app/components/OgImage/About.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 69 | -------------------------------------------------------------------------------- /content/blogs/3. create-namespace-subdomain-connect-to-vercel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To Create Namespace Subdomain & Connect To Vercel App 3 | date: 1st Mar 2023 4 | description: Here we will learn, How To Create Namespace Subdomain & Connect To Vercel App 5 | image: /blogs-img/blog3.jpg 6 | alt: How To Create Namespace Subdomain & Connect To Vercel App 7 | ogImage: /blogs-img/blog3.jpg 8 | tags: ['nuxt', 'vercel', 'namecheap'] 9 | published: true 10 | --- 11 | 12 | ### Introduction 13 | 14 | Creating a subdomain on Namecheap and connecting it with a Vercel deployed app is a straightforward process. In this blog, we will guide you through the steps required to create a subdomain on Namecheap and connect it to your Vercel deployed app. 15 | 16 | ### Step 1: Create a subdomain on Namecheap 17 | 18 | The first step is to create a subdomain on Namecheap. To do this, log in to your Namecheap account and go to your domain dashboard. Click on the "Advanced DNS" tab and then click on "Add New Record". 19 | 20 | Select "CNAME (Alias)" from the "Type" dropdown menu. In the "Host" field, enter the name of your subdomain (for example, "app" if you want your subdomain to be "app.yourdomain.com"). In the "Value" field, enter the URL of your Vercel deployed app (for example, "yourapp.vercel.app"). 21 | 22 | ### Step 2: Verify the subdomain 23 | 24 | After creating the subdomain, you need to verify that it has been set up correctly. To do this, go to your Vercel deployed app dashboard and click on the "Domains" tab. Click on "Add Domain" and enter the name of your subdomain. Vercel will verify the subdomain and confirm that it has been set up correctly. 25 | 26 | ### Step 3: Add the subdomain to your Vercel deployed app 27 | 28 | Now that your subdomain has been verified, you need to add it to your Vercel deployed app. To do this, go to your app dashboard and click on "Settings". Click on "Domains" and then click on "Add Domain". Enter the name of your subdomain and click on "Add". 29 | 30 | ### Step 4: Verify the subdomain in Vercel 31 | 32 | After adding the subdomain to your Vercel deployed app, you need to verify that it has been set up correctly. To do this, click on the subdomain in your Vercel deployed app dashboard. Click on "Verify DNS Configuration". Vercel will check that the subdomain has been set up correctly and confirm that it is connected to your Vercel deployed app. 33 | 34 | ### Conclusion 35 | 36 | Connecting a subdomain on Namecheap to your Vercel deployed app is a simple process that can be done in a few steps. By following the steps outlined in this blog, you can easily create a subdomain on Namecheap and connect it to your Vercel deployed app. Remember to verify your subdomain in both Namecheap and Vercel to ensure that it has been set up correctly. If you encounter any issues, reach out to Vercel support for assistance. 37 | -------------------------------------------------------------------------------- /app/components/main/recent.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 83 | -------------------------------------------------------------------------------- /content/blogs/4. fetch-content-data-render-pages.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To Properly Fetch Nuxt Content Data and Render It in Nuxt Pages 3 | date: 1st Mar 2023 4 | description: Here we will learn How To Properly Fetch Nuxt Content Data and Render It in Nuxt Pages 5 | image: /blogs-img/blog4.jpg 6 | alt: How To Properly Fetch Nuxt Content Data and Render It in Nuxt Pages 7 | ogImage: /blogs-img/blog4.jpg 8 | tags: ['nuxt', 'nuxt-content'] 9 | published: true 10 | --- 11 | 12 | ### Introduction 13 | 14 | Nuxt.js is a popular open-source framework for building Vue.js applications. With the release of Nuxt 3, developers have access to new features and improvements to streamline the development process. One of these features is Nuxt Content v2, which allows you to create and manage content in a simple and efficient way. In this blog post, we will guide you through the steps to connect Nuxt Content v2 with Nuxt 3. 15 | 16 | ### Step 1: Install the necessary dependencies 17 | 18 | The first step is to install the necessary dependencies for Nuxt Content v2. To do this, run the following command: 19 | 20 | ```js 21 | npm install @nuxt/content@next 22 | 23 | ``` 24 | 25 | ### Step 2: Configure Nuxt Content v2 26 | 27 | Once the dependencies are installed, you need to configure Nuxt Content v2 in your Nuxt 3 project. To do this, create a new file named nuxt.config.js in the root directory of your project and add the following code: 28 | 29 | ```js 30 | export default { 31 | // Enable Nuxt Content module 32 | modules: [ 33 | '@nuxt/content' 34 | ], 35 | 36 | ``` 37 | 38 | In the above code, we have enabled the Nuxt Content module and set the directory where your content will be stored. 39 | 40 | ### Step 3: Create content files 41 | 42 | Once Nuxt Content v2 is configured, you can create content files in the specified directory. By default, Nuxt Content v2 supports Markdown and YAML file formats. You can create a new file in the `content` directory with the following structure: 43 | 44 | ```md 45 | --- 46 | title: 'Hello, world!' 47 | --- 48 | 49 | # Welcome to Nuxt Content v2 50 | 51 | This is an example of a Markdown file created using Nuxt Content v2. 52 | ``` 53 | 54 | In the above code, we have created a Markdown file with a title and a sample content. 55 | 56 | ### Step 4: Display content on a page 57 | 58 | Now that we have created content files, we can display the content on a page. To do this, create a new Vue component in the components directory with the following code: 59 | 60 | ```vue 61 | 65 | 66 | 77 | ``` 78 | -------------------------------------------------------------------------------- /app/components/OgImage/Test.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 78 | -------------------------------------------------------------------------------- /content/blogs/5. vue3-awesome-library.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Some Awesome Libraries For Vue3 3 | date: 1st Jan 2023 4 | description: Vue.js is a popular JavaScript framework for building web applications. In this blog post, we will introduce you to some of the awesome libraries for Vue.js in different categories. 5 | image: /blogs-img/blog5.jpg 6 | alt: Some Awesome Libraries For Vue3 7 | ogImage: /blogs-img/blog5.jpg 8 | tags: ['vue', 'javascript'] 9 | published: true 10 | --- 11 | 12 | ### Introduction 13 | 14 | Vue.js is a popular JavaScript framework for building web applications. It offers a lot of flexibility and ease of use, making it a go-to choice for many developers. One of the advantages of Vue.js is its rich library ecosystem. In this blog post, we will introduce you to some of the awesome libraries for Vue.js in different categories. 15 | 16 | ### Essential 17 | 18 | Some libraries are must have when you are start working with new project, here are my list 19 | 20 | - **Vue Router**: Vue Router is the official router for Vue.js. It deeply integrates with Vue.js core to make building Single Page Applications with Vue.js a breeze 21 | - **Pinia**: Pinia started as an experiment to redesign what a Store for Vue could look like with the Composition API around November 2019. 22 | - **VueUse**: VueUse is a collection of utility functions based on Composition API. We assume you are already familiar with the basic ideas of Composition API before you continue. 23 | - **Vitest**: Vitest is a blazing fast unit test framework powered by Vite. 24 | - **Vue Macro**: Vue Macros is a library that implements proposals or ideas that have not been officially implemented by Vue. That means it will explore and extend more features and syntax sugar to Vue. 25 | 26 | ### UI Libraries 27 | 28 | UI libraries provide pre-built components and styles for building user interfaces. Here are some popular UI libraries for Vue.js: 29 | 30 | - **Naive UI**: A Vue 3 Component Library Fairly Complete, Theme Customizable, Uses TypeScript, Fast Kinda Interesting 31 | - **Vuetify**: Vue Component Framework Vuetify is a no design skills required UI Library with beautifully handcrafted Vue Components. 32 | - **Vuestic**: You can create a new project or integrate Vuestic UI into an existing application. There are three ways to create new Vuestic App. All of them mostly the same and provides the same features. 33 | 34 | ### Others 35 | 36 | - **VueFire**: VueFire Official Firebase bindings for Vue.js 37 | - **Vue I118**: Vue I18n Internationalization plugin for Vue.js 38 | - **Vue Auto Animate**: Add motion to your apps with a single line of code. 39 | - **Vuelidate**: Vuelidate is considered model-based because the validation rules are defined next to your data, and the validation tree structure matches the data model structure. 40 | 41 | ### Conclusion 42 | 43 | In this blog post, we have introduced you to some of the awesome libraries for Vue.js in different categories. These libraries can help you build better and more engaging web applications with Vue.js. Remember to choose the right library based on your project requirements and always refer to the documentation for usage and integration instructions. If you encounter any issues, reach out to the Vue.js community for assistance. 44 | -------------------------------------------------------------------------------- /content/blogs/1. connect-namecheap-to-vercel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: How To Connect You Namecheap Domain With Vercel Deployed App 3 | date: 1st Mar 2023 4 | description: Here you will lean how to connect your namecheap domain to vercel deployed app. 5 | image: /blogs-img/blog1.jpg 6 | alt: How To Connect You Namecheap Domain With Vercel Deployed App 7 | ogImage: /blogs-img/blog1.jpg 8 | tags: ['namecheap', 'vercel'] 9 | published: true 10 | --- 11 | 12 | ### Introduction 13 | 14 | If you've purchased a domain from Namecheap and you want to connect it to your Vercel app, there are a few steps you need to follow. In this blog, we'll guide you through the process of connecting your Namecheap domain with your Vercel app. 15 | 16 | ### Step 1: Add a custom domain to your Vercel app 17 | 18 | The first step is to add your custom domain to your Vercel app. To do this, log in to your Vercel account and go to your app dashboard. Click on "Settings" and then "Domains". Click on "Add Domain" and enter your custom domain name. Then click on "Add". 19 | 20 | ### Step 2: Get the DNS records from Vercel 21 | 22 | Once you've added your custom domain to your Vercel app, you'll need to get the DNS records from Vercel. To do this, go back to the "Domains" section and click on the custom domain you just added. Then click on "DNS Records". 23 | 24 | You'll see a list of DNS records that you need to add to your Namecheap account. These include the A record, the CNAME record, and the TXT record. 25 | 26 | ### Step 3: Add DNS records to Namecheap 27 | 28 | Now that you have the DNS records from Vercel, you need to add them to your Namecheap account. To do this, log in to your Namecheap account and go to your domain dashboard. Click on "Advanced DNS" and then "Add New Record". 29 | 30 | Add the A record first. In the "Type" dropdown menu, select "A (Address)". In the "Host" field, enter "@" (without the quotes). In the "Value" field, enter the IP address from the Vercel DNS records. 31 | 32 | Next, add the CNAME record. In the "Type" dropdown menu, select "CNAME (Alias)". In the "Host" field, enter "www" (without the quotes). In the "Value" field, enter the value from the Vercel DNS records. 33 | 34 | Finally, add the TXT record. In the "Type" dropdown menu, select "TXT (Text)". In the "Host" field, enter "@" (without the quotes). In the "Value" field, enter the value from the Vercel DNS records. 35 | 36 | ### Step 4: Verify DNS records 37 | 38 | Once you've added the DNS records to your Namecheap account, you need to verify that they're correct. To do this, go back to your Vercel app dashboard and click on the custom domain. Then click on "Verify DNS Configuration". Vercel will check if the DNS records have been set up correctly. 39 | 40 | It may take some time for the DNS records to propagate, so be patient. Once the DNS records have propagated, Vercel will verify them and your custom domain will be connected to your Vercel app. 41 | 42 | ### Conclusion 43 | 44 | Connecting your Namecheap domain to your Vercel app is a relatively simple process. By following the steps outlined in this blog, you'll be able to connect your custom domain in no time. Remember to be patient as it may take some time for the DNS records to propagate. If you run into any issues, don't hesitate to reach out to Vercel support for assistance. 45 | -------------------------------------------------------------------------------- /app/pages/about.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 83 | -------------------------------------------------------------------------------- /app/components/main/header.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 79 | 80 | 102 | -------------------------------------------------------------------------------- /app/components/logo/name.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /content/blogs/7. redis-ttl-jitter-and-how-i-almost-crashed-a-server.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Redis TTL, Jitter, and How I Almost Crashed a Server 🚀 3 | date: 18th Sep 2025 4 | description: Recently, I ran into an interesting Redis case that taught me a big lesson Infinite cache TTLs are like hoarding—things pile up until it’s a problem. 5 | image: /blogs-img/blog7.png 6 | alt: Redis TTL, Jitter, and How I Almost Crashed a Server 🚀 7 | ogImage: /blogs-img/blog7.png 8 | tags: ['redis', 'ttl', 'jitter'] 9 | published: true 10 | --- 11 | 12 | ### Redis TTL, Jitter, and How I Almost Crashed a Server 🚀 13 | 14 | Recently, I ran into an interesting Redis case that taught me a big lesson: 15 | **Infinite cache TTLs are like hoarding—things pile up until it’s a problem.** 16 | 17 | --- 18 | 19 | ### The Setup: Infinite Cache 20 | 21 | Once upon a time (okay, just a few months ago), we were saving some data in Redis with **no expiration**. The idea was simple: 22 | 23 | - Data comes from another system (the _real_ source of truth). 24 | - We cache it in Redis for fast access. 25 | - Done. Easy. ✅ 26 | 27 | But here’s the problem: when you never expire cache, it **keeps growing**. And growing. And growing. Like that drawer in your house where you throw every cable you’ve ever owned. 28 | 29 | ### The Task: Add a TTL 30 | 31 | One day, I got the task: 32 | 33 | > “Please set a TTL of two weeks for this cache.” 34 | 35 | Sounds easy, right? Just add: 36 | 37 | ```js 38 | redis.set('mykey', value, 'EX', 1209600) // 2 weeks in seconds 39 | ``` 40 | 41 | Boom. Done. Task finished. Go get coffee. ☕ 42 | 43 | Except… not really. 44 | 45 | ### The Problem: Cache Avalanche 46 | 47 | Think about what happens **two weeks later**. 48 | Every single cached key expires **at the same time**. 49 | 50 | Suddenly, Redis says: 51 | 52 | > “Sorry boss, no cache here!” 53 | 54 | And then our poor backend server (the real source of truth) gets flooded with requests, like: 55 | 56 | ``` 57 | HELP! SEND DATA! SEND DATA! SEND DATA! 58 | ``` 59 | 60 | The server could literally crash under the unexpected load. This is called a **cache avalanche**. 61 | 62 | ### The Solution: Add Jitter 63 | 64 | The trick is simple but powerful: **don’t let all keys expire at once.** 65 | 66 | Instead of setting **exactly 2 weeks**, we add a little randomness (aka _jitter_). For example: 67 | 68 | ```js 69 | // Expire between 14 and 16 days 70 | const baseTTL = 14 * 24 * 60 * 60 // 14 days 71 | const jitter = Math.floor(Math.random() * (2 * 24 * 60 * 60)) // up to 2 days 72 | const ttl = baseTTL + jitter 73 | 74 | redis.set('mykey', value, 'EX', ttl) 75 | ``` 76 | 77 | Now some keys expire in **14 days**, some in **15**, some in **16**. 78 | Which means requests trickle back to the server instead of hitting it like a tsunami. 🌊 79 | 80 | ### Why It Matters 81 | 82 | Without jitter: 83 | 84 | - Day 14 → server gets **millions of requests at once**. Boom. 🔥 85 | 86 | With jitter: 87 | 88 | - Day 14 → some requests 89 | - Day 15 → some more 90 | - Day 16 → a few more 91 | - Server is chill. 😎 92 | 93 | This small change can **save your entire system** from crashing. 94 | 95 | ### Final Thoughts 96 | 97 | Caching is powerful, but it comes with hidden gotchas. 98 | 99 | - Infinite TTL? Your cache becomes a junkyard. 100 | - Fixed TTL? Your server might collapse in 14 days like a time bomb. 101 | - TTL with jitter? Balanced, safe, and production-ready. 102 | 103 | So the next time you set a cache TTL, remember: 104 | 👉 _Always sprinkle some randomness in your Redis life._ 105 | 106 | Your future self (and your backend servers) will thank you. 🙏 107 | 108 | Do you want me to also add a **diagram (ASCII or image idea)** showing the difference between _no jitter vs jitter_ so it’s more visually clear for the blog? 109 | -------------------------------------------------------------------------------- /app/data/index.ts: -------------------------------------------------------------------------------- 1 | export const navbarData = { 2 | homeTitle: "Riyad's Blog", 3 | } 4 | 5 | export const footerData = { 6 | author: 'Al Asad Nur Riyad', 7 | aboutAuthor: 8 | 'Hi! I am Riyad, a Tech enthusiast, problem solver and software engineer. Currently working at FieldNation LLC.', 9 | authorInterest: 10 | "I have a fair amount of knowledge of Javascript, Typescript, VueJs, and Nuxt. If you have an interesting idea, either open source or paid let's connect.", 11 | aboutTheSite: 12 | "This is a personal blog site built with Nuxt3, TailwindCSS, NuxtContent, Nuxt Icon. Currently it's deployed in Vercel.", 13 | } 14 | 15 | export const homePage = { 16 | title: 'Welcome To My Blog Site', 17 | description: 18 | 'Get Web Development, Javascript, Typescript, NodeJs, Vue, and Nuxt, Related Articles, Tips, Learning resources and more.', 19 | } 20 | 21 | export const blogsPage = { 22 | title: 'All Blogs', 23 | description: 'Here you will find all the blog posts I have written & published on this site.', 24 | } 25 | 26 | export const categoryPage = { 27 | title: 'Categories', 28 | description: 29 | 'Blow this category is generated from all the tags are mentioned in the different blog post', 30 | } 31 | 32 | export const aboutPage = { 33 | title: 'Al Asad Nur Riyad', 34 | description: 'Software Engineer, Problem Solver, Web Enthusiast.', 35 | aboutMe: 36 | "Hello, fellow human! I'm a software wizard who spends most of his day crafting code spells at @FieldNation in the Workplace Operation team. When I'm not crafting code, you can find me summoning solutions to problems on online judges. Just don't ask me to cast any love spells, my magic only works on machines!", 37 | } 38 | 39 | export const seoData = { 40 | title: `Riyad's Blog | Riyads Blog`, 41 | ogTitle: `Let's learn Javascript, Typescript, Vue, Nuxt, & Problem Solving - Riyads Blog | Riyad's Blog`, 42 | description: `Hi I am Riyad. A Software Engineer at FieldNation, with over 3.5+ years experience in software development. - Riyads Blog | Riyad's Blog`, 43 | twitterDescription: `Riyad's Blog, where I play around with Nuxt, Vue, and more and showcase my blog, resources, etc - Riyads Blog | Riyad's Blog`, 44 | image: 45 | 'https://res.cloudinary.com/dmecmyphj/image/upload/v1673548905/nuxt-blog/cover_ntgs6u.webp', 46 | mySite: 'https://blog-nurriyad.vercel.app', 47 | twitterHandle: '@qdnvubp', 48 | mailAddress: 'asadnurriyad@gmail.com', 49 | } 50 | 51 | export const socialLinks = { 52 | githubLink: 'https://github.com/nurRiyad', 53 | linkedinLink: 'https://www.linkedin.com/in/nur-riyad/', 54 | twitterLink: 'https://twitter.com/qdnvubp', 55 | stackoverflowLink: 'https://stackoverflow.com/users/16781395/nur-riyad', 56 | } 57 | 58 | export const siteMetaData = [ 59 | { 60 | name: 'description', 61 | content: seoData.description, 62 | }, 63 | // Test on: https://developers.facebook.com/tools/debug/ or https://socialsharepreview.com/ 64 | { property: 'og:site_name', content: seoData.mySite }, 65 | { property: 'og:type', content: 'website' }, 66 | { 67 | property: 'og:url', 68 | content: seoData.mySite, 69 | }, 70 | { 71 | property: 'og:title', 72 | content: seoData.ogTitle, 73 | }, 74 | { 75 | property: 'og:description', 76 | content: seoData.description, 77 | }, 78 | { 79 | property: 'og:image', 80 | content: seoData.image, 81 | }, 82 | // Test on: https://cards-dev.twitter.com/validator or https://socialsharepreview.com/ 83 | { name: 'twitter:site', content: seoData.twitterHandle }, 84 | { name: 'twitter:card', content: 'summary_large_image' }, 85 | { 86 | name: 'twitter:url', 87 | content: seoData.mySite, 88 | }, 89 | { 90 | name: 'twitter:title', 91 | content: seoData.ogTitle, 92 | }, 93 | { 94 | name: 'twitter:description', 95 | content: seoData.twitterDescription, 96 | }, 97 | { 98 | name: 'twitter:image', 99 | content: seoData.image, 100 | }, 101 | ] 102 | -------------------------------------------------------------------------------- /app/pages/blogs/[blog].vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 129 | -------------------------------------------------------------------------------- /app/pages/blogs/index.vue: -------------------------------------------------------------------------------- 1 | 87 | 88 | 138 | -------------------------------------------------------------------------------- /content/blogs/8. float-make-my-dollar-float-away.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: FLOAT Made My Dollars Float Away - FLOAT vs DECIMAL in MySQL 💸 3 | date: 19th Sep 2025 4 | description: Recently I got a task Alter a table column from `FLOAT` to `DECIMAL(10,2) 5 | image: /blogs-img/blog8.png 6 | alt: FLOAT Made My Dollars Float Away - FLOAT vs DECIMAL in MySQL 💸 7 | ogImage: /blogs-img/blog8.png 8 | tags: ['mysql', 'float', 'decimal'] 9 | published: true 10 | --- 11 | 12 | ### FLOAT Made My Dollars Float Away - FLOAT vs DECIMAL in MySQL 13 | 14 | Recently I got a task: 15 | 16 | > **"Alter a table column from `FLOAT` to `DECIMAL(10,2)`"** 17 | 18 | I thought: 19 | _"Pff, easy task. Just run an `ALTER TABLE` and done. Why is this even a ticket?"_ 20 | 21 | But then I read the description. 22 | Turns out, **FLOAT was causing data loss**, and I needed to convert it without losing data. 23 | 24 | That’s when I realized: this isn’t just about one query. It’s about how **FLOAT silently eats your money** in MySQL. 25 | 26 | ### Why FLOAT is a Problem 27 | 28 | `FLOAT` in MySQL is a **binary floating-point type**. 29 | It doesn’t store exact values — only approximations. 30 | 31 | That’s fine for rocket science 🚀 or graphics rendering 🎮, but for **money** where every cent matters? Disaster. 32 | 33 | Think of `FLOAT` as a leaky bucket. You pour in `$1,000,000.25`… and it gives you back `$999,999.94`. 34 | 35 | Not funny when it’s your salary. 36 | 37 | ### Example of Data Loss 38 | 39 | ```sql 40 | CREATE TABLE money_float ( 41 | id INT AUTO_INCREMENT PRIMARY KEY, 42 | amount FLOAT 43 | ); 44 | 45 | INSERT INTO money_float (amount) VALUES (1000000.25), (123456789.99); 46 | 47 | SELECT * FROM money_float; 48 | ``` 49 | 50 | Result: 51 | 52 | | id | amount | 53 | | --- | ------------ | 54 | | 1 | 1000000.25 | 55 | | 2 | 123456792.00 | 56 | 57 | We inserted `123456789.99`, but got back `123456792.00`. 58 | The bigger the number, the worse the corruption. 59 | 60 | ### But Why Does FLOAT Lose Data? 61 | 62 | Here’s the fun part. Let’s make it simple. 63 | 64 | - `FLOAT` stores numbers in **binary (base 2)**. 65 | - But not every decimal number can be written exactly in binary. 66 | 67 | Example: 68 | 69 | - In decimal, `0.1` is simple. 70 | - In binary, `0.1` is **infinite repeating**: `0.0001100110011…` 71 | - So `FLOAT` cuts it off at some point and stores an approximation. 72 | 73 | That’s why when you do: 74 | 75 | ```sql 76 | INSERT INTO money_float (amount) VALUES (0.1); 77 | SELECT amount FROM money_float; 78 | ``` 79 | 80 | You might see something like `0.10000000149`. 81 | 82 | Now imagine this tiny error repeated in **millions of dollars**. 83 | Errors pile up, and suddenly your 9-digit amount looks… off. 84 | 85 | ### DECIMAL to the Rescue 86 | 87 | `DECIMAL` stores numbers differently: 88 | 89 | - Instead of binary approximation, it stores **exact digits as strings** internally. 90 | - That means `123456789.99` is stored as exactly `123456789.99`. 91 | 92 | ```sql 93 | CREATE TABLE money_decimal ( 94 | id INT AUTO_INCREMENT PRIMARY KEY, 95 | amount DECIMAL(15,2) 96 | ); 97 | 98 | INSERT INTO money_decimal (amount) VALUES (1000000.25), (123456789.99); 99 | 100 | SELECT * FROM money_decimal; 101 | ``` 102 | 103 | Result: 104 | 105 | | id | amount | 106 | | --- | ------------ | 107 | | 1 | 1000000.25 | 108 | | 2 | 123456789.99 | 109 | 110 | Perfect. ✅ No rounding surprises. 111 | 112 | ### Why ALTER Won’t Save You 113 | 114 | Here’s the trap I fell into: 115 | 116 | ```sql 117 | ALTER TABLE money_float MODIFY amount DECIMAL(15,2); 118 | ``` 119 | 120 | You’d think this fixes it, right? 121 | Nope. ❌ 122 | 123 | The data was already corrupted when it was first inserted as `FLOAT`. 124 | `ALTER` just moves the already-broken value into `DECIMAL`. 125 | 126 | Garbage in → garbage out. 127 | 128 | ### Visual: FLOAT vs DECIMAL 129 | 130 | ``` 131 | FLOAT (approximation in binary): 132 | 123456789.99 ---> 123456792.00 💀 133 | 134 | DECIMAL (exact digits): 135 | 123456789.99 ---> 123456789.99 ✅ 136 | ``` 137 | 138 | ### Lessons Learned 139 | 140 | - Never use `FLOAT`/`DOUBLE` for money. 141 | - Always use `DECIMAL(precision, scale)` (e.g., `DECIMAL(15,2)`). 142 | - If your table already has money in `FLOAT`, you cannot fix the lost precision with `ALTER`. You’ll need to re-import or clean it at the source. 143 | 144 | ### Final Thought 145 | 146 | Using `FLOAT` for money is like paying your salary in **Monopoly money**. 🎲💵 147 | It looks okay until you try to spend it — then you realize it’s worthless. 148 | 149 | Stick with `DECIMAL`, and your dollars will stay safe. ✅ 150 | -------------------------------------------------------------------------------- /app/components/logo/dogpow.vue: -------------------------------------------------------------------------------- 1 | 193 | -------------------------------------------------------------------------------- /app/components/logo/confused.vue: -------------------------------------------------------------------------------- 1 | 235 | -------------------------------------------------------------------------------- /app/components/logo/404.vue: -------------------------------------------------------------------------------- 1 | 731 | -------------------------------------------------------------------------------- /app/components/logo/dog.vue: -------------------------------------------------------------------------------- 1 | 489 | --------------------------------------------------------------------------------