Skip to main content

Getting Started with Full Stack Development

Muhammad Ubaid Raza
Tuesday, November 19, 2024
Node.jsVue.jsFull Stack
Getting Started with Full Stack Development

Full Stack Development with Node.js and Vue.js: A Complete Guide

Full stack development can feel overwhelming when you're just starting out. With so many technologies, frameworks, and best practices to consider, it's easy to get lost in the complexity. But here's the thing—once you understand the core concepts and have a solid foundation, building full stack applications becomes not just manageable, but genuinely enjoyable.

After years of building applications with various technology stacks, I've found that the combination of Node.js and Vue.js offers one of the most developer-friendly and powerful approaches to full stack development. In this comprehensive guide, I'll walk you through everything you need to know to start building production-ready applications with this stack.

Why Node.js and Vue.js Make a Perfect Pair

Before diving into the technical details, let me explain why this combination has become my go-to choice for full stack development:

JavaScript Everywhere

Using Node.js for the backend and Vue.js for the frontend means you're writing JavaScript across your entire application. This unification brings several benefits:

  • Reduced context switching between different programming languages
  • Shared code and utilities between frontend and backend
  • Consistent development patterns across your entire stack
  • Easier team collaboration when everyone speaks the same language

Modern Development Experience

Both technologies embrace modern JavaScript features and development practices:

  • ES6+ syntax with async/await, destructuring, and modules
  • NPM ecosystem with access to millions of packages
  • Hot reloading for rapid development iteration
  • Rich tooling for debugging, testing, and deployment

Scalability and Performance

This stack is designed to handle growth:

  • Node.js non-blocking I/O handles concurrent requests efficiently
  • Vue.js virtual DOM ensures smooth user interfaces
  • Microservices ready architecture for scaling individual components

Learning Curve and Productivity

Perhaps most importantly, this stack is designed with developer experience in mind:

  • Vue.js gentle learning curve makes it accessible to developers of all levels
  • Node.js familiar syntax for anyone coming from frontend JavaScript
  • Comprehensive documentation and active communities for both technologies
  • Rich ecosystem of plugins, libraries, and tools

Understanding the Full Stack Architecture

Let me break down how a typical Node.js + Vue.js application is structured:

my-fullstack-app/
├── client/                 # Vue.js frontend
   ├── src/
   ├── components/
   ├── views/
   ├── router/
   ├── store/
   └── main.js
   ├── public/
   └── package.json
├── server/                 # Node.js backend
   ├── controllers/
   ├── middleware/
   ├── models/
   ├── routes/
   ├── config/
   └── app.js
├── shared/                 # Shared utilities
└── package.json

This separation of concerns allows you to:

  • Develop independently - Work on frontend and backend separately
  • Deploy flexibly - Host them on different servers if needed
  • Scale independently - Scale frontend and backend based on demand
  • Test effectively - Unit test each layer in isolation

Getting Your Environment Ready

Before we start building, let's set up a development environment that will make your life easier:

Prerequisites

First, make sure you have these tools installed:

# Node.js (includes npm)
node --version  # Should be 16.x or higher
npm --version   # Should be 8.x or higher

# Vue CLI (for scaffolding projects)
npm install -g @vue/cli

# Optional but recommended
npm install -g nodemon  # Auto-restart server on changes
npm install -g concurrently  # Run multiple commands simultaneously

Project Initialization

Let's create a new full stack project from scratch:

# Create the main project directory
mkdir my-fullstack-app
cd my-fullstack-app

# Initialize the main package.json
npm init -y

# Create the client (Vue.js) application
vue create client
cd client
# Choose your preferred configuration
# I recommend: Vue 3, Router, Vuex, ESLint + Prettier

# Go back and create the server directory
cd ..
mkdir server
cd server
npm init -y

# Install essential server dependencies
npm install express cors helmet morgan compression
npm install -D nodemon

cd .. # Back to root

Development Scripts

Add these scripts to your root package.json for easier development:

{
  "scripts": {
    "dev": "concurrently \"npm run server\" \"npm run client\"",
    "server": "cd server && npm run dev",
    "client": "cd client && npm run serve",
    "build": "cd client && npm run build",
    "start": "cd server && npm run start"
  },
  "devDependencies": {
    "concurrently": "^7.6.0"
  }
}

Now you can run npm run dev from the root directory to start both your frontend and backend simultaneously.

Building Your First Full Stack Application

Let's build a practical example together—a task management application that demonstrates all the key concepts. This will help you understand how the pieces fit together.

Setting Up the Backend (Node.js + Express)

First, let's create a robust backend API:

// server/app.js
const express = require("express")
const cors = require("cors")
const helmet = require("helmet")
const morgan = require("morgan")
const compression = require("compression")

const app = express()
const PORT = process.env.PORT || 3000

// Middleware
app.use(helmet()) // Security headers
app.use(cors()) // Enable CORS for frontend
app.use(compression()) // Gzip compression
app.use(morgan("combined")) // Logging
app.use(express.json()) // Parse JSON bodies
app.use(express.urlencoded({ extended: true }))

// Routes
app.use("/api/tasks", require("./routes/tasks"))
app.use("/api/auth", require("./routes/auth"))

// Error handling middleware
app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).json({ error: "Something went wrong!" })
})

// Health check endpoint
app.get("/health", (req, res) => {
  res.json({ status: "OK", timestamp: new Date().toISOString() })
})

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})

module.exports = app

Creating the API Routes

// server/routes/tasks.js
const express = require("express")
const router = express.Router()

// In-memory storage for demo (use a database in production)
let tasks = [
  { id: 1, title: "Learn Node.js", completed: false, createdAt: new Date() },
  { id: 2, title: "Build Vue.js app", completed: false, createdAt: new Date() },
]

// GET /api/tasks - Retrieve all tasks
router.get("/", (req, res) => {
  res.json({
    success: true,
    data: tasks,
    count: tasks.length,
  })
})

// POST /api/tasks - Create a new task
router.post("/", (req, res) => {
  const { title } = req.body

  if (!title || title.trim() === "") {
    return res.status(400).json({
      success: false,
      error: "Task title is required",
    })
  }

  const newTask = {
    id: Date.now(), // Simple ID generation
    title: title.trim(),
    completed: false,
    createdAt: new Date(),
  }

  tasks.push(newTask)

  res.status(201).json({
    success: true,
    data: newTask,
  })
})

// PUT /api/tasks/:id - Update a task
router.put("/:id", (req, res) => {
  const taskId = parseInt(req.params.id)
  const { title, completed } = req.body

  const taskIndex = tasks.findIndex((task) => task.id === taskId)

  if (taskIndex === -1) {
    return res.status(404).json({
      success: false,
      error: "Task not found",
    })
  }

  // Update task properties
  if (title !== undefined) tasks[taskIndex].title = title
  if (completed !== undefined) tasks[taskIndex].completed = completed
  tasks[taskIndex].updatedAt = new Date()

  res.json({
    success: true,
    data: tasks[taskIndex],
  })
})

// DELETE /api/tasks/:id - Delete a task
router.delete("/:id", (req, res) => {
  const taskId = parseInt(req.params.id)
  const taskIndex = tasks.findIndex((task) => task.id === taskId)

  if (taskIndex === -1) {
    return res.status(404).json({
      success: false,
      error: "Task not found",
    })
  }

  const deletedTask = tasks.splice(taskIndex, 1)[0]

  res.json({
    success: true,
    data: deletedTask,
  })
})

module.exports = router

Building the Frontend (Vue.js)

Now let's create a clean, functional frontend:

<!-- client/src/App.vue -->
<template>
  <div id="app">
    <header class="app-header">
      <h1>Task Manager</h1>
      <p>Built with Node.js + Vue.js</p>
    </header>

    <main class="container">
      <TaskForm @task-created="addTask" />
      <TaskList
        :tasks="tasks"
        @task-updated="updateTask"
        @task-deleted="deleteTask"
      />
    </main>
  </div>
</template>

<script>
import TaskForm from "./components/TaskForm.vue"
import TaskList from "./components/TaskList.vue"
import { taskService } from "./services/taskService"

export default {
  name: "App",
  components: {
    TaskForm,
    TaskList,
  },
  data() {
    return {
      tasks: [],
      loading: false,
      error: null,
    }
  },
  async created() {
    await this.loadTasks()
  },
  methods: {
    async loadTasks() {
      this.loading = true
      try {
        const response = await taskService.getTasks()
        this.tasks = response.data
      } catch (error) {
        this.error = "Failed to load tasks"
        console.error("Error loading tasks:", error)
      } finally {
        this.loading = false
      }
    },

    async addTask(task) {
      try {
        const response = await taskService.createTask(task)
        this.tasks.push(response.data)
      } catch (error) {
        console.error("Error creating task:", error)
      }
    },

    async updateTask(task) {
      try {
        const response = await taskService.updateTask(task.id, task)
        const index = this.tasks.findIndex((t) => t.id === task.id)
        if (index !== -1) {
          this.tasks.splice(index, 1, response.data)
        }
      } catch (error) {
        console.error("Error updating task:", error)
      }
    },

    async deleteTask(taskId) {
      try {
        await taskService.deleteTask(taskId)
        this.tasks = this.tasks.filter((task) => task.id !== taskId)
      } catch (error) {
        console.error("Error deleting task:", error)
      }
    },
  },
}
</script>

<style>
.app-header {
  background: #2c3e50;
  color: white;
  padding: 2rem;
  text-align: center;
}

.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 2rem;
}
</style>

Creating Reusable Components

<!-- client/src/components/TaskForm.vue -->
<template>
  <form @submit.prevent="submitTask" class="task-form">
    <div class="form-group">
      <input
        v-model="newTaskTitle"
        type="text"
        placeholder="Enter a new task..."
        class="form-input"
        required
      />
      <button type="submit" class="btn btn-primary" :disabled="loading">
        {{ loading ? "Adding..." : "Add Task" }}
      </button>
    </div>
  </form>
</template>

<script>
export default {
  name: "TaskForm",
  data() {
    return {
      newTaskTitle: "",
      loading: false,
    }
  },
  methods: {
    async submitTask() {
      if (!this.newTaskTitle.trim()) return

      this.loading = true
      try {
        await this.$emit("task-created", {
          title: this.newTaskTitle.trim(),
        })
        this.newTaskTitle = ""
      } finally {
        this.loading = false
      }
    },
  },
}
</script>

<style scoped>
.task-form {
  margin-bottom: 2rem;
}

.form-group {
  display: flex;
  gap: 1rem;
}

.form-input {
  flex: 1;
  padding: 0.75rem;
  border: 1px solid #ddd;
  border-radius: 4px;
  font-size: 1rem;
}

.btn {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 1rem;
}

.btn-primary {
  background: #3498db;
  color: white;
}

.btn:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
</style>

Setting Up API Communication

// client/src/services/taskService.js
const API_BASE_URL = process.env.VUE_APP_API_URL || "http://localhost:3000/api"

class TaskService {
  async request(endpoint, options = {}) {
    const url = `${API_BASE_URL}${endpoint}`
    const config = {
      headers: {
        "Content-Type": "application/json",
        ...options.headers,
      },
      ...options,
    }

    const response = await fetch(url, config)

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`)
    }

    return response.json()
  }

  async getTasks() {
    return this.request("/tasks")
  }

  async createTask(task) {
    return this.request("/tasks", {
      method: "POST",
      body: JSON.stringify(task),
    })
  }

  async updateTask(id, task) {
    return this.request(`/tasks/${id}`, {
      method: "PUT",
      body: JSON.stringify(task),
    })
  }

  async deleteTask(id) {
    return this.request(`/tasks/${id}`, {
      method: "DELETE",
    })
  }
}

export const taskService = new TaskService()

Advanced Patterns and Best Practices

Now that we have a working application, let's explore some advanced patterns that will make your full stack applications more robust and maintainable.

Environment Configuration

// server/config/database.js
const config = {
  development: {
    host: process.env.DB_HOST || "localhost",
    port: process.env.DB_PORT || 5432,
    database: process.env.DB_NAME || "myapp_dev",
    username: process.env.DB_USER || "postgres",
    password: process.env.DB_PASS || "password",
  },
  production: {
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    database: process.env.DB_NAME,
    username: process.env.DB_USER,
    password: process.env.DB_PASS,
    ssl: true,
  },
}

module.exports = config[process.env.NODE_ENV || "development"]

Error Handling and Validation

// server/middleware/validation.js
const { body, validationResult } = require("express-validator")

const validateTask = [
  body("title")
    .trim()
    .isLength({ min: 1, max: 255 })
    .withMessage("Title must be between 1 and 255 characters"),

  body("completed")
    .optional()
    .isBoolean()
    .withMessage("Completed must be a boolean"),

  (req, res, next) => {
    const errors = validationResult(req)
    if (!errors.isEmpty()) {
      return res.status(400).json({
        success: false,
        errors: errors.array(),
      })
    }
    next()
  },
]

module.exports = { validateTask }

State Management with Vuex

// client/src/store/modules/tasks.js
const state = {
  tasks: [],
  loading: false,
  error: null,
}

const mutations = {
  SET_LOADING(state, loading) {
    state.loading = loading
  },
  SET_ERROR(state, error) {
    state.error = error
  },
  SET_TASKS(state, tasks) {
    state.tasks = tasks
  },
  ADD_TASK(state, task) {
    state.tasks.push(task)
  },
  UPDATE_TASK(state, updatedTask) {
    const index = state.tasks.findIndex((task) => task.id === updatedTask.id)
    if (index !== -1) {
      state.tasks.splice(index, 1, updatedTask)
    }
  },
  DELETE_TASK(state, taskId) {
    state.tasks = state.tasks.filter((task) => task.id !== taskId)
  },
}

const actions = {
  async fetchTasks({ commit }) {
    commit("SET_LOADING", true)
    try {
      const response = await taskService.getTasks()
      commit("SET_TASKS", response.data)
    } catch (error) {
      commit("SET_ERROR", error.message)
    } finally {
      commit("SET_LOADING", false)
    }
  },
}

const getters = {
  completedTasks: (state) => state.tasks.filter((task) => task.completed),
  pendingTasks: (state) => state.tasks.filter((task) => !task.completed),
  taskCount: (state) => state.tasks.length,
}

export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters,
}

Testing Your Full Stack Application

Testing is crucial for maintaining code quality and preventing bugs. Here's how to set up testing for both frontend and backend:

Backend Testing with Jest

// server/tests/routes/tasks.test.js
const request = require("supertest")
const app = require("../../app")

describe("Tasks API", () => {
  describe("GET /api/tasks", () => {
    it("should return all tasks", async () => {
      const response = await request(app).get("/api/tasks").expect(200)

      expect(response.body.success).toBe(true)
      expect(Array.isArray(response.body.data)).toBe(true)
    })
  })

  describe("POST /api/tasks", () => {
    it("should create a new task", async () => {
      const newTask = { title: "Test task" }

      const response = await request(app)
        .post("/api/tasks")
        .send(newTask)
        .expect(201)

      expect(response.body.success).toBe(true)
      expect(response.body.data.title).toBe(newTask.title)
      expect(response.body.data.completed).toBe(false)
    })

    it("should return error for empty title", async () => {
      const response = await request(app)
        .post("/api/tasks")
        .send({ title: "" })
        .expect(400)

      expect(response.body.success).toBe(false)
    })
  })
})

Frontend Testing with Vue Test Utils

// client/tests/unit/TaskForm.spec.js
import { shallowMount } from "@vue/test-utils"
import TaskForm from "@/components/TaskForm.vue"

describe("TaskForm.vue", () => {
  it("emits task-created event when form is submitted", async () => {
    const wrapper = shallowMount(TaskForm)
    const input = wrapper.find(".form-input")
    const form = wrapper.find(".task-form")

    await input.setValue("New task")
    await form.trigger("submit.prevent")

    expect(wrapper.emitted("task-created")).toBeTruthy()
    expect(wrapper.emitted("task-created")[0][0]).toEqual({
      title: "New task",
    })
  })

  it("clears input after successful submission", async () => {
    const wrapper = shallowMount(TaskForm)
    const input = wrapper.find(".form-input")

    await input.setValue("New task")
    await wrapper.find(".task-form").trigger("submit.prevent")

    expect(wrapper.vm.newTaskTitle).toBe("")
  })
})

Deployment and Production Considerations

When you're ready to deploy your application, here are some key considerations:

Environment Variables

# .env.production
NODE_ENV=production
PORT=80
DB_HOST=your-production-db-host
DB_NAME=your-production-db
DB_USER=your-production-user
DB_PASS=your-secure-password
JWT_SECRET=your-super-secure-jwt-secret
CORS_ORIGIN=https://yourdomain.com

Production Build Script

{
  "scripts": {
    "build": "cd client && npm run build && cd ../server && npm run build",
    "start:prod": "NODE_ENV=production node server/dist/app.js",
    "deploy": "npm run build && npm run start:prod"
  }
}

Docker Configuration

# Dockerfile
FROM node:16-alpine

WORKDIR /app

# Copy package files
COPY package*.json ./
COPY client/package*.json ./client/
COPY server/package*.json ./server/

# Install dependencies
RUN npm ci --only=production
RUN cd client && npm ci --only=production
RUN cd server && npm ci --only=production

# Copy source code
COPY . .

# Build application
RUN npm run build

EXPOSE 3000

CMD ["npm", "run", "start:prod"]

Key Takeaways and Next Steps

Building full stack applications with Node.js and Vue.js offers a powerful, unified development experience. Here are the most important points to remember:

What We've Covered

  1. Unified JavaScript ecosystem - One language across your entire stack
  2. Modern development practices - Hot reloading, component-based architecture, and RESTful APIs
  3. Practical implementation - Real code examples you can use immediately
  4. Testing strategies - Both unit and integration testing approaches
  5. Production deployment - Environment management and containerization

Best Practices to Follow

  • Keep your code modular - Separate concerns between frontend and backend
  • Use environment variables - Never hardcode configuration values
  • Implement proper error handling - Both on server and client sides
  • Write tests - They save time and prevent bugs
  • Plan for scaling - Use patterns that grow with your application
  1. Add a database - Replace in-memory storage with PostgreSQL or MongoDB
  2. Implement authentication - Add user registration and login functionality
  3. Add real-time features - Use Socket.io for live updates
  4. Optimize performance - Implement caching and code splitting
  5. Set up CI/CD - Automate your testing and deployment process

If you're interested in extending this to browser extensions, check out my comprehensive guide on building modern browser extensions with similar technologies.

The journey of full stack development is continuous learning, but with Node.js and Vue.js, you have a solid foundation to build amazing applications. Start with the basics, practice regularly, and gradually add more advanced features as your confidence grows.

Remember, every expert was once a beginner. The key is to start building, learn from your mistakes, and keep improving. Happy coding!

Full-stack engineer leading teams to build scalable systems & tools, with solid DevOps expertise.

Muhammad Ubaid R.

\n\n\n",[270,3065,3066,3071,3082,3102,3124,3145,3162,3171,3175,3195,3217,3224,3237,3251,3265,3270,3278,3287,3295,3299,3308,3326,3342,3363,3367,3377,3393,3402,3409,3416,3420,3429,3435,3447,3458,3467,3472,3476,3488,3501,3505,3514,3526,3539,3546,3568,3584,3600,3615,3639,3648,3659,3664,3669,3673,3688,3694,3717,3739,3753,3776,3780,3784,3788,3803,3809,3839,3884,3904,3933,3938,3952,3975,3979,3983,3987,4003,4009,4026,4065,4079,4102,4106,4110,4114,4118,4126,4130,4139,4147,4164,4176,4189,4202,4207,4212,4221,4234,4250,4261,4265],{"__ignoreMap":268},[273,3067,3068],{"class":275,"line":276},[273,3069,3070],{"class":293},"\n",[273,3072,3073,3076,3079],{"class":275,"line":283},[273,3074,3075],{"class":820},"<",[273,3077,3078],{"class":1333},"template",[273,3080,3081],{"class":820},">\n",[273,3083,3084,3087,3090,3092,3094,3096,3098,3100],{"class":275,"line":16},[273,3085,3086],{"class":820}," <",[273,3088,3089],{"class":1333},"div",[273,3091,1810],{"class":829},[273,3093,1048],{"class":820},[273,3095,833],{"class":820},[273,3097,1214],{"class":289},[273,3099,833],{"class":820},[273,3101,3081],{"class":820},[273,3103,3104,3107,3110,3113,3115,3117,3120,3122],{"class":275,"line":308},[273,3105,3106],{"class":820}," <",[273,3108,3109],{"class":1333},"header",[273,3111,3112],{"class":829}," class",[273,3114,1048],{"class":820},[273,3116,833],{"class":820},[273,3118,3119],{"class":289},"app-header",[273,3121,833],{"class":820},[273,3123,3081],{"class":820},[273,3125,3126,3129,3132,3135,3138,3141,3143],{"class":275,"line":321},[273,3127,3128],{"class":820}," <",[273,3130,3131],{"class":1333},"h1",[273,3133,3134],{"class":820},">",[273,3136,3137],{"class":860},"Task Manager",[273,3139,3140],{"class":820},"\n",[273,3218,3219,3221],{"class":275,"line":390},[273,3220,3128],{"class":820},[273,3222,3223],{"class":1333},"TaskList\n",[273,3225,3226,3229,3231,3233,3235],{"class":275,"line":401},[273,3227,3228],{"class":829}," :tasks",[273,3230,1048],{"class":820},[273,3232,833],{"class":820},[273,3234,2617],{"class":289},[273,3236,958],{"class":820},[273,3238,3239,3242,3244,3246,3249],{"class":275,"line":29},[273,3240,3241],{"class":829}," @task-updated",[273,3243,1048],{"class":820},[273,3245,833],{"class":820},[273,3247,3248],{"class":289},"updateTask",[273,3250,958],{"class":820},[273,3252,3253,3256,3258,3260,3263],{"class":275,"line":420},[273,3254,3255],{"class":829}," @task-deleted",[273,3257,1048],{"class":820},[273,3259,833],{"class":820},[273,3261,3262],{"class":289},"deleteTask",[273,3264,958],{"class":820},[273,3266,3267],{"class":275,"line":430},[273,3268,3269],{"class":820}," />\n",[273,3271,3272,3274,3276],{"class":275,"line":440},[273,3273,3166],{"class":820},[273,3275,3181],{"class":1333},[273,3277,3081],{"class":820},[273,3279,3280,3283,3285],{"class":275,"line":450},[273,3281,3282],{"class":820}," \n\n\n\n\n\n",[270,4282,4283,4288,4296,4328,4347,4354,4368,4382,4396,4410,4415,4419,4462,4467,4476,4484,4492,4500,4504,4512,4520,4534,4542,4548,4559,4569,4573,4577,4585,4596,4617,4621,4631,4637,4659,4678,4685,4696,4704,4714,4718,4722,4726,4730,4738,4742,4753,4761,4772,4776,4780,4788,4800,4812,4816,4820,4828,4839,4850,4871,4883,4894,4898,4902,4911,4924,4935,4945,4957,4967,4971,4975,4984,4997,5007,5011,5015,5028,5040,5051,5055],{"__ignoreMap":268},[273,4284,4285],{"class":275,"line":276},[273,4286,4287],{"class":293},"\n",[273,4289,4290,4292,4294],{"class":275,"line":283},[273,4291,3075],{"class":820},[273,4293,3078],{"class":1333},[273,4295,3081],{"class":820},[273,4297,4298,4300,4303,4306,4308,4310,4313,4315,4317,4319,4321,4324,4326],{"class":275,"line":16},[273,4299,3086],{"class":820},[273,4301,4302],{"class":1333},"form",[273,4304,4305],{"class":829}," @submit.prevent",[273,4307,1048],{"class":820},[273,4309,833],{"class":820},[273,4311,4312],{"class":289},"submitTask",[273,4314,833],{"class":820},[273,4316,3112],{"class":829},[273,4318,1048],{"class":820},[273,4320,833],{"class":820},[273,4322,4323],{"class":289},"task-form",[273,4325,833],{"class":820},[273,4327,3081],{"class":820},[273,4329,4330,4332,4334,4336,4338,4340,4343,4345],{"class":275,"line":308},[273,4331,3106],{"class":820},[273,4333,3089],{"class":1333},[273,4335,3112],{"class":829},[273,4337,1048],{"class":820},[273,4339,833],{"class":820},[273,4341,4342],{"class":289},"form-group",[273,4344,833],{"class":820},[273,4346,3081],{"class":820},[273,4348,4349,4351],{"class":275,"line":321},[273,4350,3128],{"class":820},[273,4352,4353],{"class":1333},"input\n",[273,4355,4356,4359,4361,4363,4366],{"class":275,"line":333},[273,4357,4358],{"class":829}," v-model",[273,4360,1048],{"class":820},[273,4362,833],{"class":820},[273,4364,4365],{"class":289},"newTaskTitle",[273,4367,958],{"class":820},[273,4369,4370,4373,4375,4377,4380],{"class":275,"line":345},[273,4371,4372],{"class":829}," type",[273,4374,1048],{"class":820},[273,4376,833],{"class":820},[273,4378,4379],{"class":289},"text",[273,4381,958],{"class":820},[273,4383,4384,4387,4389,4391,4394],{"class":275,"line":357},[273,4385,4386],{"class":829}," placeholder",[273,4388,1048],{"class":820},[273,4390,833],{"class":820},[273,4392,4393],{"class":289},"Enter a new task...",[273,4395,958],{"class":820},[273,4397,4398,4401,4403,4405,4408],{"class":275,"line":370},[273,4399,4400],{"class":829}," class",[273,4402,1048],{"class":820},[273,4404,833],{"class":820},[273,4406,4407],{"class":289},"form-input",[273,4409,958],{"class":820},[273,4411,4412],{"class":275,"line":380},[273,4413,4414],{"class":829}," required\n",[273,4416,4417],{"class":275,"line":390},[273,4418,3269],{"class":820},[273,4420,4421,4423,4426,4429,4431,4433,4436,4438,4440,4442,4444,4447,4449,4452,4454,4456,4458,4460],{"class":275,"line":401},[273,4422,3128],{"class":820},[273,4424,4425],{"class":1333},"button",[273,4427,4428],{"class":829}," type",[273,4430,1048],{"class":820},[273,4432,833],{"class":820},[273,4434,4435],{"class":289},"submit",[273,4437,833],{"class":820},[273,4439,3112],{"class":829},[273,4441,1048],{"class":820},[273,4443,833],{"class":820},[273,4445,4446],{"class":289},"btn btn-primary",[273,4448,833],{"class":820},[273,4450,4451],{"class":829}," :disabled",[273,4453,1048],{"class":820},[273,4455,833],{"class":820},[273,4457,3533],{"class":289},[273,4459,833],{"class":820},[273,4461,3081],{"class":820},[273,4463,4464],{"class":275,"line":29},[273,4465,4466],{"class":860}," {{ loading ? \"Adding...\" : \"Add Task\" }}\n",[273,4468,4469,4472,4474],{"class":275,"line":420},[273,4470,4471],{"class":820}," {\n const errors = validationResult(req)\n if (!errors.isEmpty()) {\n return res.status(400).json({\n success: false,\n errors: errors.array(),\n })\n }\n next()\n },\n]\n\nmodule.exports = { validateTask }\n",[270,6009,6010,6015,6046,6050,6061,6076,6085,6117,6137,6141,6155,6164,6173,6192,6196,6217,6235,6255,6280,6291,6309,6315,6319,6326,6330,6334,6338],{"__ignoreMap":268},[273,6011,6012],{"class":275,"line":276},[273,6013,6014],{"class":293},"// server/middleware/validation.js\n",[273,6016,6017,6019,6021,6024,6026,6029,6031,6033,6035,6037,6039,6042,6044],{"class":275,"line":283},[273,6018,1042],{"class":829},[273,6020,2062],{"class":820},[273,6022,6023],{"class":860}," body",[273,6025,1375],{"class":820},[273,6027,6028],{"class":860}," validationResult ",[273,6030,1538],{"class":820},[273,6032,1725],{"class":820},[273,6034,1051],{"class":641},[273,6036,1054],{"class":860},[273,6038,833],{"class":820},[273,6040,6041],{"class":289},"express-validator",[273,6043,833],{"class":820},[273,6045,1064],{"class":860},[273,6047,6048],{"class":275,"line":16},[273,6049,556],{"emptyLinePlaceholder":555},[273,6051,6052,6054,6057,6059],{"class":275,"line":308},[273,6053,1042],{"class":829},[273,6055,6056],{"class":860}," validateTask ",[273,6058,1048],{"class":820},[273,6060,1802],{"class":860},[273,6062,6063,6066,6068,6070,6072,6074],{"class":275,"line":321},[273,6064,6065],{"class":641}," body",[273,6067,1054],{"class":860},[273,6069,833],{"class":820},[273,6071,2093],{"class":289},[273,6073,833],{"class":820},[273,6075,1064],{"class":860},[273,6077,6078,6081,6083],{"class":275,"line":333},[273,6079,6080],{"class":820}," .",[273,6082,2103],{"class":641},[273,6084,1170],{"class":860},[273,6086,6087,6089,6092,6094,6096,6099,6101,6103,6105,6108,6110,6113,6115],{"class":275,"line":345},[273,6088,6080],{"class":820},[273,6090,6091],{"class":641},"isLength",[273,6093,1054],{"class":860},[273,6095,1330],{"class":820},[273,6097,6098],{"class":1333}," min",[273,6100,836],{"class":820},[273,6102,1815],{"class":1199},[273,6104,1375],{"class":820},[273,6106,6107],{"class":1333}," max",[273,6109,836],{"class":820},[273,6111,6112],{"class":1199}," 255",[273,6114,1343],{"class":820},[273,6116,1064],{"class":860},[273,6118,6119,6121,6124,6126,6128,6131,6133,6135],{"class":275,"line":357},[273,6120,6080],{"class":820},[273,6122,6123],{"class":641},"withMessage",[273,6125,1054],{"class":860},[273,6127,833],{"class":820},[273,6129,6130],{"class":289},"Title must be between 1 and 255 characters",[273,6132,833],{"class":820},[273,6134,1463],{"class":860},[273,6136,879],{"class":820},[273,6138,6139],{"class":275,"line":370},[273,6140,556],{"emptyLinePlaceholder":555},[273,6142,6143,6145,6147,6149,6151,6153],{"class":275,"line":380},[273,6144,6065],{"class":641},[273,6146,1054],{"class":860},[273,6148,833],{"class":820},[273,6150,2644],{"class":289},[273,6152,833],{"class":820},[273,6154,1064],{"class":860},[273,6156,6157,6159,6162],{"class":275,"line":390},[273,6158,6080],{"class":820},[273,6160,6161],{"class":641},"optional",[273,6163,1170],{"class":860},[273,6165,6166,6168,6171],{"class":275,"line":401},[273,6167,6080],{"class":820},[273,6169,6170],{"class":641},"isBoolean",[273,6172,1170],{"class":860},[273,6174,6175,6177,6179,6181,6183,6186,6188,6190],{"class":275,"line":29},[273,6176,6080],{"class":820},[273,6178,6123],{"class":641},[273,6180,1054],{"class":860},[273,6182,833],{"class":820},[273,6184,6185],{"class":289},"Completed must be a boolean",[273,6187,833],{"class":820},[273,6189,1463],{"class":860},[273,6191,879],{"class":820},[273,6193,6194],{"class":275,"line":420},[273,6195,556],{"emptyLinePlaceholder":555},[273,6197,6198,6201,6203,6205,6207,6209,6211,6213,6215],{"class":275,"line":430},[273,6199,6200],{"class":820}," (",[273,6202,1578],{"class":1444},[273,6204,1375],{"class":820},[273,6206,1455],{"class":1444},[273,6208,1375],{"class":820},[273,6210,1460],{"class":1444},[273,6212,1463],{"class":820},[273,6214,1466],{"class":829},[273,6216,839],{"class":820},[273,6218,6219,6221,6224,6226,6229,6231,6233],{"class":275,"line":440},[273,6220,5148],{"class":829},[273,6222,6223],{"class":860}," errors",[273,6225,1725],{"class":820},[273,6227,6228],{"class":641}," validationResult",[273,6230,1054],{"class":1333},[273,6232,1578],{"class":860},[273,6234,1064],{"class":1333},[273,6236,6237,6239,6241,6243,6246,6248,6251,6253],{"class":275,"line":450},[273,6238,5278],{"class":2084},[273,6240,1575],{"class":1333},[273,6242,2090],{"class":820},[273,6244,6245],{"class":860},"errors",[273,6247,1185],{"class":820},[273,6249,6250],{"class":641},"isEmpty",[273,6252,1226],{"class":1333},[273,6254,821],{"class":820},[273,6256,6257,6260,6262,6264,6266,6268,6270,6272,6274,6276,6278],{"class":275,"line":460},[273,6258,6259],{"class":2084}," return",[273,6261,1455],{"class":860},[273,6263,1185],{"class":820},[273,6265,1501],{"class":641},[273,6267,1054],{"class":1333},[273,6269,2132],{"class":1199},[273,6271,1463],{"class":1333},[273,6273,1185],{"class":820},[273,6275,813],{"class":641},[273,6277,1054],{"class":1333},[273,6279,821],{"class":820},[273,6281,6282,6285,6287,6289],{"class":275,"line":471},[273,6283,6284],{"class":1333}," success",[273,6286,836],{"class":820},[273,6288,1839],{"class":1339},[273,6290,879],{"class":820},[273,6292,6293,6296,6298,6300,6302,6305,6307],{"class":275,"line":743},[273,6294,6295],{"class":1333}," errors",[273,6297,836],{"class":820},[273,6299,6223],{"class":860},[273,6301,1185],{"class":820},[273,6303,6304],{"class":641},"array",[273,6306,1629],{"class":1333},[273,6308,879],{"class":820},[273,6310,6311,6313],{"class":275,"line":749},[273,6312,3588],{"class":820},[273,6314,1064],{"class":1333},[273,6316,6317],{"class":275,"line":771},[273,6318,3471],{"class":820},[273,6320,6321,6324],{"class":275,"line":784},[273,6322,6323],{"class":641}," next",[273,6325,1170],{"class":1333},[273,6327,6328],{"class":275,"line":789},[273,6329,963],{"class":820},[273,6331,6332],{"class":275,"line":1471},[273,6333,1906],{"class":860},[273,6335,6336],{"class":275,"line":1493},[273,6337,556],{"emptyLinePlaceholder":555},[273,6339,6340,6342,6344,6346,6348],{"class":275,"line":1535},[273,6341,1722],{"class":820},[273,6343,1725],{"class":820},[273,6345,2062],{"class":820},[273,6347,6056],{"class":860},[273,6349,1004],{"class":820},[127,6351,6353],{"id":6352},"state-management-with-vuex","State Management with Vuex",[263,6355,6357],{"className":1028,"code":6356,"language":1030,"meta":268,"style":268},"// client/src/store/modules/tasks.js\nconst state = {\n tasks: [],\n loading: false,\n error: null,\n}\n\nconst mutations = {\n SET_LOADING(state, loading) {\n state.loading = loading\n },\n SET_ERROR(state, error) {\n state.error = error\n },\n SET_TASKS(state, tasks) {\n state.tasks = tasks\n },\n ADD_TASK(state, task) {\n state.tasks.push(task)\n },\n UPDATE_TASK(state, updatedTask) {\n const index = state.tasks.findIndex((task) => task.id === updatedTask.id)\n if (index !== -1) {\n state.tasks.splice(index, 1, updatedTask)\n }\n },\n DELETE_TASK(state, taskId) {\n state.tasks = state.tasks.filter((task) => task.id !== taskId)\n },\n}\n\nconst actions = {\n async fetchTasks({ commit }) {\n commit(\"SET_LOADING\", true)\n try {\n const response = await taskService.getTasks()\n commit(\"SET_TASKS\", response.data)\n } catch (error) {\n commit(\"SET_ERROR\", error.message)\n } finally {\n commit(\"SET_LOADING\", false)\n }\n },\n}\n\nconst getters = {\n completedTasks: (state) => state.tasks.filter((task) => task.completed),\n pendingTasks: (state) => state.tasks.filter((task) => !task.completed),\n taskCount: (state) => state.tasks.length,\n}\n\nexport default {\n namespaced: true,\n state,\n mutations,\n actions,\n getters,\n}\n",[270,6358,6359,6364,6375,6385,6396,6405,6409,6413,6424,6443,6457,6461,6478,6491,6495,6512,6525,6529,6546,6564,6568,6586,6631,6649,6676,6680,6684,6701,6743,6747,6751,6755,6766,6784,6804,6811,6830,6854,6868,6892,6900,6918,6922,6926,6930,6934,6945,6989,7035,7062,7066,7070,7078,7089,7096,7103,7110,7117],{"__ignoreMap":268},[273,6360,6361],{"class":275,"line":276},[273,6362,6363],{"class":293},"// client/src/store/modules/tasks.js\n",[273,6365,6366,6368,6371,6373],{"class":275,"line":283},[273,6367,1042],{"class":829},[273,6369,6370],{"class":860}," state ",[273,6372,1048],{"class":820},[273,6374,839],{"class":820},[273,6376,6377,6379,6381,6383],{"class":275,"line":16},[273,6378,2272],{"class":1333},[273,6380,836],{"class":820},[273,6382,3444],{"class":860},[273,6384,879],{"class":820},[273,6386,6387,6390,6392,6394],{"class":275,"line":308},[273,6388,6389],{"class":1333}," loading",[273,6391,836],{"class":820},[273,6393,1839],{"class":1339},[273,6395,879],{"class":820},[273,6397,6398,6401,6403],{"class":275,"line":321},[273,6399,6400],{"class":1333}," error",[273,6402,836],{"class":820},[273,6404,3466],{"class":820},[273,6406,6407],{"class":275,"line":333},[273,6408,1004],{"class":820},[273,6410,6411],{"class":275,"line":345},[273,6412,556],{"emptyLinePlaceholder":555},[273,6414,6415,6417,6420,6422],{"class":275,"line":357},[273,6416,1042],{"class":829},[273,6418,6419],{"class":860}," mutations ",[273,6421,1048],{"class":820},[273,6423,839],{"class":820},[273,6425,6426,6429,6431,6434,6436,6439,6441],{"class":275,"line":370},[273,6427,6428],{"class":1333}," SET_LOADING",[273,6430,1054],{"class":820},[273,6432,6433],{"class":1444},"state",[273,6435,1375],{"class":820},[273,6437,6438],{"class":1444}," loading",[273,6440,1463],{"class":820},[273,6442,839],{"class":820},[273,6444,6445,6448,6450,6452,6454],{"class":275,"line":380},[273,6446,6447],{"class":860}," state",[273,6449,1185],{"class":820},[273,6451,3533],{"class":860},[273,6453,1725],{"class":820},[273,6455,6456],{"class":860}," loading\n",[273,6458,6459],{"class":275,"line":390},[273,6460,963],{"class":820},[273,6462,6463,6466,6468,6470,6472,6474,6476],{"class":275,"line":401},[273,6464,6465],{"class":1333}," SET_ERROR",[273,6467,1054],{"class":820},[273,6469,6433],{"class":1444},[273,6471,1375],{"class":820},[273,6473,1519],{"class":1444},[273,6475,1463],{"class":820},[273,6477,839],{"class":820},[273,6479,6480,6482,6484,6486,6488],{"class":275,"line":29},[273,6481,6447],{"class":860},[273,6483,1185],{"class":820},[273,6485,1479],{"class":860},[273,6487,1725],{"class":820},[273,6489,6490],{"class":860}," error\n",[273,6492,6493],{"class":275,"line":420},[273,6494,963],{"class":820},[273,6496,6497,6500,6502,6504,6506,6508,6510],{"class":275,"line":430},[273,6498,6499],{"class":1333}," SET_TASKS",[273,6501,1054],{"class":820},[273,6503,6433],{"class":1444},[273,6505,1375],{"class":820},[273,6507,1981],{"class":1444},[273,6509,1463],{"class":820},[273,6511,839],{"class":820},[273,6513,6514,6516,6518,6520,6522],{"class":275,"line":440},[273,6515,6447],{"class":860},[273,6517,1185],{"class":820},[273,6519,2617],{"class":860},[273,6521,1725],{"class":820},[273,6523,6524],{"class":860}," tasks\n",[273,6526,6527],{"class":275,"line":450},[273,6528,963],{"class":820},[273,6530,6531,6534,6536,6538,6540,6542,6544],{"class":275,"line":460},[273,6532,6533],{"class":1333}," ADD_TASK",[273,6535,1054],{"class":820},[273,6537,6433],{"class":1444},[273,6539,1375],{"class":820},[273,6541,2483],{"class":1444},[273,6543,1463],{"class":820},[273,6545,839],{"class":820},[273,6547,6548,6550,6552,6554,6556,6558,6560,6562],{"class":275,"line":471},[273,6549,6447],{"class":860},[273,6551,1185],{"class":820},[273,6553,2617],{"class":860},[273,6555,1185],{"class":820},[273,6557,2277],{"class":641},[273,6559,1054],{"class":1333},[273,6561,2476],{"class":860},[273,6563,1064],{"class":1333},[273,6565,6566],{"class":275,"line":743},[273,6567,963],{"class":820},[273,6569,6570,6573,6575,6577,6579,6582,6584],{"class":275,"line":749},[273,6571,6572],{"class":1333}," UPDATE_TASK",[273,6574,1054],{"class":820},[273,6576,6433],{"class":1444},[273,6578,1375],{"class":820},[273,6580,6581],{"class":1444}," updatedTask",[273,6583,1463],{"class":820},[273,6585,839],{"class":820},[273,6587,6588,6590,6592,6594,6597,6599,6601,6603,6605,6607,6609,6611,6613,6615,6617,6619,6621,6623,6625,6627,6629],{"class":275,"line":771},[273,6589,5148],{"class":829},[273,6591,3845],{"class":860},[273,6593,1725],{"class":820},[273,6595,6596],{"class":860}," state",[273,6598,1185],{"class":820},[273,6600,2617],{"class":860},[273,6602,1185],{"class":820},[273,6604,2469],{"class":641},[273,6606,1054],{"class":1333},[273,6608,1054],{"class":820},[273,6610,2476],{"class":1444},[273,6612,1463],{"class":820},[273,6614,1466],{"class":829},[273,6616,2483],{"class":860},[273,6618,1185],{"class":820},[273,6620,2422],{"class":860},[273,6622,2490],{"class":820},[273,6624,6581],{"class":860},[273,6626,1185],{"class":820},[273,6628,2422],{"class":860},[273,6630,1064],{"class":1333},[273,6632,6633,6635,6637,6639,6641,6643,6645,6647],{"class":275,"line":784},[273,6634,5278],{"class":2084},[273,6636,1575],{"class":1333},[273,6638,3893],{"class":860},[273,6640,2609],{"class":820},[273,6642,2514],{"class":820},[273,6644,2517],{"class":1199},[273,6646,2114],{"class":1333},[273,6648,821],{"class":820},[273,6650,6651,6654,6656,6658,6660,6662,6664,6666,6668,6670,6672,6674],{"class":275,"line":789},[273,6652,6653],{"class":860}," state",[273,6655,1185],{"class":820},[273,6657,2617],{"class":860},[273,6659,1185],{"class":820},[273,6661,2967],{"class":641},[273,6663,1054],{"class":1333},[273,6665,3893],{"class":860},[273,6667,1375],{"class":820},[273,6669,1815],{"class":1199},[273,6671,1375],{"class":820},[273,6673,6581],{"class":860},[273,6675,1064],{"class":1333},[273,6677,6678],{"class":275,"line":1471},[273,6679,3471],{"class":820},[273,6681,6682],{"class":275,"line":1493},[273,6683,963],{"class":820},[273,6685,6686,6689,6691,6693,6695,6697,6699],{"class":275,"line":1535},[273,6687,6688],{"class":1333}," DELETE_TASK",[273,6690,1054],{"class":820},[273,6692,6433],{"class":1444},[273,6694,1375],{"class":820},[273,6696,2403],{"class":1444},[273,6698,1463],{"class":820},[273,6700,839],{"class":820},[273,6702,6703,6705,6707,6709,6711,6713,6715,6717,6719,6721,6723,6725,6727,6729,6731,6733,6735,6737,6739,6741],{"class":275,"line":1543},[273,6704,6447],{"class":860},[273,6706,1185],{"class":820},[273,6708,2617],{"class":860},[273,6710,1725],{"class":820},[273,6712,6596],{"class":860},[273,6714,1185],{"class":820},[273,6716,2617],{"class":860},[273,6718,1185],{"class":820},[273,6720,4042],{"class":641},[273,6722,1054],{"class":1333},[273,6724,1054],{"class":820},[273,6726,2476],{"class":1444},[273,6728,1463],{"class":820},[273,6730,1466],{"class":829},[273,6732,2483],{"class":860},[273,6734,1185],{"class":820},[273,6736,2422],{"class":860},[273,6738,2609],{"class":820},[273,6740,2403],{"class":860},[273,6742,1064],{"class":1333},[273,6744,6745],{"class":275,"line":1548},[273,6746,963],{"class":820},[273,6748,6749],{"class":275,"line":1554},[273,6750,1004],{"class":820},[273,6752,6753],{"class":275,"line":1591},[273,6754,556],{"emptyLinePlaceholder":555},[273,6756,6757,6759,6762,6764],{"class":275,"line":1644},[273,6758,1042],{"class":829},[273,6760,6761],{"class":860}," actions ",[273,6763,1048],{"class":820},[273,6765,839],{"class":820},[273,6767,6768,6770,6773,6776,6779,6782],{"class":275,"line":1651},[273,6769,3480],{"class":829},[273,6771,6772],{"class":1333}," fetchTasks",[273,6774,6775],{"class":820},"({",[273,6777,6778],{"class":1444}," commit",[273,6780,6781],{"class":820}," })",[273,6783,839],{"class":820},[273,6785,6786,6789,6791,6793,6796,6798,6800,6802],{"class":275,"line":1656},[273,6787,6788],{"class":641}," commit",[273,6790,1054],{"class":1333},[273,6792,833],{"class":820},[273,6794,6795],{"class":289},"SET_LOADING",[273,6797,833],{"class":820},[273,6799,1375],{"class":820},[273,6801,1340],{"class":1339},[273,6803,1064],{"class":1333},[273,6805,6806,6809],{"class":275,"line":1678},[273,6807,6808],{"class":2084}," try",[273,6810,839],{"class":820},[273,6812,6813,6816,6818,6820,6822,6824,6826,6828],{"class":275,"line":1707},[273,6814,6815],{"class":829}," const",[273,6817,3553],{"class":860},[273,6819,1725],{"class":820},[273,6821,3558],{"class":2084},[273,6823,3350],{"class":860},[273,6825,1185],{"class":820},[273,6827,3565],{"class":641},[273,6829,1170],{"class":1333},[273,6831,6832,6835,6837,6839,6842,6844,6846,6848,6850,6852],{"class":275,"line":1714},[273,6833,6834],{"class":641}," commit",[273,6836,1054],{"class":1333},[273,6838,833],{"class":820},[273,6840,6841],{"class":289},"SET_TASKS",[273,6843,833],{"class":820},[273,6845,1375],{"class":820},[273,6847,3553],{"class":860},[273,6849,1185],{"class":820},[273,6851,3736],{"class":860},[273,6853,1064],{"class":1333},[273,6855,6856,6858,6860,6862,6864,6866],{"class":275,"line":1719},[273,6857,2174],{"class":820},[273,6859,3591],{"class":2084},[273,6861,1575],{"class":1333},[273,6863,1479],{"class":860},[273,6865,2114],{"class":1333},[273,6867,821],{"class":820},[273,6869,6870,6872,6874,6876,6879,6881,6883,6885,6887,6890],{"class":275,"line":2287},[273,6871,6834],{"class":641},[273,6873,1054],{"class":1333},[273,6875,833],{"class":820},[273,6877,6878],{"class":289},"SET_ERROR",[273,6880,833],{"class":820},[273,6882,1375],{"class":820},[273,6884,1519],{"class":860},[273,6886,1185],{"class":820},[273,6888,6889],{"class":860},"message",[273,6891,1064],{"class":1333},[273,6893,6894,6896,6898],{"class":275,"line":2292},[273,6895,2174],{"class":820},[273,6897,3645],{"class":2084},[273,6899,839],{"class":820},[273,6901,6902,6904,6906,6908,6910,6912,6914,6916],{"class":275,"line":2316},[273,6903,6834],{"class":641},[273,6905,1054],{"class":1333},[273,6907,833],{"class":820},[273,6909,6795],{"class":289},[273,6911,833],{"class":820},[273,6913,1375],{"class":820},[273,6915,1839],{"class":1339},[273,6917,1064],{"class":1333},[273,6919,6920],{"class":275,"line":2327},[273,6921,3471],{"class":820},[273,6923,6924],{"class":275,"line":2338},[273,6925,963],{"class":820},[273,6927,6928],{"class":275,"line":2345},[273,6929,1004],{"class":820},[273,6931,6932],{"class":275,"line":2352},[273,6933,556],{"emptyLinePlaceholder":555},[273,6935,6936,6938,6941,6943],{"class":275,"line":2357},[273,6937,1042],{"class":829},[273,6939,6940],{"class":860}," getters ",[273,6942,1048],{"class":820},[273,6944,839],{"class":820},[273,6946,6947,6950,6952,6954,6956,6958,6960,6962,6964,6966,6968,6970,6972,6974,6976,6978,6980,6982,6984,6987],{"class":275,"line":2363},[273,6948,6949],{"class":641}," completedTasks",[273,6951,836],{"class":820},[273,6953,1575],{"class":820},[273,6955,6433],{"class":1444},[273,6957,1463],{"class":820},[273,6959,1466],{"class":829},[273,6961,6596],{"class":860},[273,6963,1185],{"class":820},[273,6965,2617],{"class":860},[273,6967,1185],{"class":820},[273,6969,4042],{"class":641},[273,6971,1054],{"class":860},[273,6973,1054],{"class":820},[273,6975,2476],{"class":1444},[273,6977,1463],{"class":820},[273,6979,1466],{"class":829},[273,6981,2483],{"class":860},[273,6983,1185],{"class":820},[273,6985,6986],{"class":860},"completed)",[273,6988,879],{"class":820},[273,6990,6991,6994,6996,6998,7000,7002,7004,7006,7008,7010,7012,7014,7016,7018,7020,7022,7024,7027,7029,7031,7033],{"class":275,"line":2398},[273,6992,6993],{"class":641}," pendingTasks",[273,6995,836],{"class":820},[273,6997,1575],{"class":820},[273,6999,6433],{"class":1444},[273,7001,1463],{"class":820},[273,7003,1466],{"class":829},[273,7005,6596],{"class":860},[273,7007,1185],{"class":820},[273,7009,2617],{"class":860},[273,7011,1185],{"class":820},[273,7013,4042],{"class":641},[273,7015,1054],{"class":860},[273,7017,1054],{"class":820},[273,7019,2476],{"class":1444},[273,7021,1463],{"class":820},[273,7023,1466],{"class":829},[273,7025,7026],{"class":820}," !",[273,7028,2476],{"class":860},[273,7030,1185],{"class":820},[273,7032,6986],{"class":860},[273,7034,879],{"class":820},[273,7036,7037,7040,7042,7044,7046,7048,7050,7052,7054,7056,7058,7060],{"class":275,"line":2427},[273,7038,7039],{"class":641}," taskCount",[273,7041,836],{"class":820},[273,7043,1575],{"class":820},[273,7045,6433],{"class":1444},[273,7047,1463],{"class":820},[273,7049,1466],{"class":829},[273,7051,6596],{"class":860},[273,7053,1185],{"class":820},[273,7055,2617],{"class":860},[273,7057,1185],{"class":820},[273,7059,1997],{"class":860},[273,7061,879],{"class":820},[273,7063,7064],{"class":275,"line":2450},[273,7065,1004],{"class":820},[273,7067,7068],{"class":275,"line":2455},[273,7069,556],{"emptyLinePlaceholder":555},[273,7071,7072,7074,7076],{"class":275,"line":2497},[273,7073,3371],{"class":2084},[273,7075,3374],{"class":2084},[273,7077,839],{"class":820},[273,7079,7080,7083,7085,7087],{"class":275,"line":2502},[273,7081,7082],{"class":1333}," namespaced",[273,7084,836],{"class":820},[273,7086,1340],{"class":1339},[273,7088,879],{"class":820},[273,7090,7091,7094],{"class":275,"line":2524},[273,7092,7093],{"class":860}," state",[273,7095,879],{"class":820},[273,7097,7098,7101],{"class":275,"line":2550},[273,7099,7100],{"class":860}," mutations",[273,7102,879],{"class":820},[273,7104,7105,7108],{"class":275,"line":2561},[273,7106,7107],{"class":860}," actions",[273,7109,879],{"class":820},[273,7111,7112,7115],{"class":275,"line":2577},[273,7113,7114],{"class":860}," getters",[273,7116,879],{"class":820},[273,7118,7119],{"class":275,"line":2584},[273,7120,1004],{"class":820},[99,7122,7124],{"id":7123},"testing-your-full-stack-application","Testing Your Full Stack Application",[104,7126,7127],{},"Testing is crucial for maintaining code quality and preventing bugs. Here's how to set up testing for both frontend and backend:",[127,7129,7131],{"id":7130},"backend-testing-with-jest","Backend Testing with Jest",[263,7133,7135],{"className":1028,"code":7134,"language":1030,"meta":268,"style":268},"// server/tests/routes/tasks.test.js\nconst request = require(\"supertest\")\nconst app = require(\"../../app\")\n\ndescribe(\"Tasks API\", () => {\n describe(\"GET /api/tasks\", () => {\n it(\"should return all tasks\", async () => {\n const response = await request(app).get(\"/api/tasks\").expect(200)\n\n expect(response.body.success).toBe(true)\n expect(Array.isArray(response.body.data)).toBe(true)\n })\n })\n\n describe(\"POST /api/tasks\", () => {\n it(\"should create a new task\", async () => {\n const newTask = { title: \"Test task\" }\n\n const response = await request(app)\n .post(\"/api/tasks\")\n .send(newTask)\n .expect(201)\n\n expect(response.body.success).toBe(true)\n expect(response.body.data.title).toBe(newTask.title)\n expect(response.body.data.completed).toBe(false)\n })\n\n it(\"should return error for empty title\", async () => {\n const response = await request(app)\n .post(\"/api/tasks\")\n .send({ title: \"\" })\n .expect(400)\n\n expect(response.body.success).toBe(false)\n })\n })\n})\n",[270,7136,7137,7142,7164,7185,7189,7211,7233,7258,7302,7306,7339,7378,7384,7390,7394,7415,7438,7462,7466,7484,7501,7514,7526,7530,7558,7594,7627,7633,7637,7660,7678,7694,7714,7726,7730,7758,7764,7770],{"__ignoreMap":268},[273,7138,7139],{"class":275,"line":276},[273,7140,7141],{"class":293},"// server/tests/routes/tasks.test.js\n",[273,7143,7144,7146,7149,7151,7153,7155,7157,7160,7162],{"class":275,"line":283},[273,7145,1042],{"class":829},[273,7147,7148],{"class":860}," request ",[273,7150,1048],{"class":820},[273,7152,1051],{"class":641},[273,7154,1054],{"class":860},[273,7156,833],{"class":820},[273,7158,7159],{"class":289},"supertest",[273,7161,833],{"class":820},[273,7163,1064],{"class":860},[273,7165,7166,7168,7170,7172,7174,7176,7178,7181,7183],{"class":275,"line":16},[273,7167,1042],{"class":829},[273,7169,1163],{"class":860},[273,7171,1048],{"class":820},[273,7173,1051],{"class":641},[273,7175,1054],{"class":860},[273,7177,833],{"class":820},[273,7179,7180],{"class":289},"../../app",[273,7182,833],{"class":820},[273,7184,1064],{"class":860},[273,7186,7187],{"class":275,"line":308},[273,7188,556],{"emptyLinePlaceholder":555},[273,7190,7191,7194,7196,7198,7201,7203,7205,7207,7209],{"class":275,"line":321},[273,7192,7193],{"class":641},"describe",[273,7195,1054],{"class":860},[273,7197,833],{"class":820},[273,7199,7200],{"class":289},"Tasks API",[273,7202,833],{"class":820},[273,7204,1375],{"class":820},[273,7206,1671],{"class":820},[273,7208,1466],{"class":829},[273,7210,839],{"class":820},[273,7212,7213,7216,7218,7220,7223,7225,7227,7229,7231],{"class":275,"line":333},[273,7214,7215],{"class":641}," describe",[273,7217,1054],{"class":1333},[273,7219,833],{"class":820},[273,7221,7222],{"class":289},"GET /api/tasks",[273,7224,833],{"class":820},[273,7226,1375],{"class":820},[273,7228,1671],{"class":820},[273,7230,1466],{"class":829},[273,7232,839],{"class":820},[273,7234,7235,7238,7240,7242,7245,7247,7249,7252,7254,7256],{"class":275,"line":345},[273,7236,7237],{"class":641}," it",[273,7239,1054],{"class":1333},[273,7241,833],{"class":820},[273,7243,7244],{"class":289},"should return all tasks",[273,7246,833],{"class":820},[273,7248,1375],{"class":820},[273,7250,7251],{"class":829}," async",[273,7253,1671],{"class":820},[273,7255,1466],{"class":829},[273,7257,839],{"class":820},[273,7259,7260,7262,7264,7266,7268,7270,7272,7274,7276,7278,7280,7282,7284,7286,7288,7290,7292,7295,7297,7300],{"class":275,"line":357},[273,7261,6815],{"class":829},[273,7263,3553],{"class":860},[273,7265,1725],{"class":820},[273,7267,3558],{"class":2084},[273,7269,5126],{"class":641},[273,7271,1054],{"class":1333},[273,7273,1214],{"class":860},[273,7275,1463],{"class":1333},[273,7277,1185],{"class":820},[273,7279,1561],{"class":641},[273,7281,1054],{"class":1333},[273,7283,833],{"class":820},[273,7285,1370],{"class":289},[273,7287,833],{"class":820},[273,7289,1463],{"class":1333},[273,7291,1185],{"class":820},[273,7293,7294],{"class":641},"expect",[273,7296,1054],{"class":1333},[273,7298,7299],{"class":1199},"200",[273,7301,1064],{"class":1333},[273,7303,7304],{"class":275,"line":370},[273,7305,556],{"emptyLinePlaceholder":555},[273,7307,7308,7311,7313,7315,7317,7320,7322,7325,7327,7329,7332,7334,7337],{"class":275,"line":380},[273,7309,7310],{"class":641}," expect",[273,7312,1054],{"class":1333},[273,7314,3731],{"class":860},[273,7316,1185],{"class":820},[273,7318,7319],{"class":860},"body",[273,7321,1185],{"class":820},[273,7323,7324],{"class":860},"success",[273,7326,1463],{"class":1333},[273,7328,1185],{"class":820},[273,7330,7331],{"class":641},"toBe",[273,7333,1054],{"class":1333},[273,7335,7336],{"class":1339},"true",[273,7338,1064],{"class":1333},[273,7340,7341,7343,7345,7348,7350,7353,7355,7357,7359,7361,7363,7365,7368,7370,7372,7374,7376],{"class":275,"line":390},[273,7342,7310],{"class":641},[273,7344,1054],{"class":1333},[273,7346,7347],{"class":860},"Array",[273,7349,1185],{"class":820},[273,7351,7352],{"class":641},"isArray",[273,7354,1054],{"class":1333},[273,7356,3731],{"class":860},[273,7358,1185],{"class":820},[273,7360,7319],{"class":860},[273,7362,1185],{"class":820},[273,7364,3736],{"class":860},[273,7366,7367],{"class":1333},"))",[273,7369,1185],{"class":820},[273,7371,7331],{"class":641},[273,7373,1054],{"class":1333},[273,7375,7336],{"class":1339},[273,7377,1064],{"class":1333},[273,7379,7380,7382],{"class":275,"line":401},[273,7381,2174],{"class":820},[273,7383,1064],{"class":1333},[273,7385,7386,7388],{"class":275,"line":29},[273,7387,2004],{"class":820},[273,7389,1064],{"class":1333},[273,7391,7392],{"class":275,"line":420},[273,7393,556],{"emptyLinePlaceholder":555},[273,7395,7396,7398,7400,7402,7405,7407,7409,7411,7413],{"class":275,"line":430},[273,7397,7215],{"class":641},[273,7399,1054],{"class":1333},[273,7401,833],{"class":820},[273,7403,7404],{"class":289},"POST /api/tasks",[273,7406,833],{"class":820},[273,7408,1375],{"class":820},[273,7410,1671],{"class":820},[273,7412,1466],{"class":829},[273,7414,839],{"class":820},[273,7416,7417,7419,7421,7423,7426,7428,7430,7432,7434,7436],{"class":275,"line":440},[273,7418,7237],{"class":641},[273,7420,1054],{"class":1333},[273,7422,833],{"class":820},[273,7424,7425],{"class":289},"should create a new task",[273,7427,833],{"class":820},[273,7429,1375],{"class":820},[273,7431,7251],{"class":829},[273,7433,1671],{"class":820},[273,7435,1466],{"class":829},[273,7437,839],{"class":820},[273,7439,7440,7442,7444,7446,7448,7450,7452,7454,7457,7459],{"class":275,"line":450},[273,7441,6815],{"class":829},[273,7443,2191],{"class":860},[273,7445,1725],{"class":820},[273,7447,2062],{"class":820},[273,7449,1820],{"class":1333},[273,7451,836],{"class":820},[273,7453,854],{"class":820},[273,7455,7456],{"class":289},"Test task",[273,7458,833],{"class":820},[273,7460,7461],{"class":820}," }\n",[273,7463,7464],{"class":275,"line":460},[273,7465,556],{"emptyLinePlaceholder":555},[273,7467,7468,7470,7472,7474,7476,7478,7480,7482],{"class":275,"line":471},[273,7469,6815],{"class":829},[273,7471,3553],{"class":860},[273,7473,1725],{"class":820},[273,7475,3558],{"class":2084},[273,7477,5126],{"class":641},[273,7479,1054],{"class":1333},[273,7481,1214],{"class":860},[273,7483,1064],{"class":1333},[273,7485,7486,7489,7491,7493,7495,7497,7499],{"class":275,"line":743},[273,7487,7488],{"class":820}," .",[273,7490,2030],{"class":641},[273,7492,1054],{"class":1333},[273,7494,833],{"class":820},[273,7496,1370],{"class":289},[273,7498,833],{"class":820},[273,7500,1064],{"class":1333},[273,7502,7503,7505,7508,7510,7512],{"class":275,"line":749},[273,7504,7488],{"class":820},[273,7506,7507],{"class":641},"send",[273,7509,1054],{"class":1333},[273,7511,2282],{"class":860},[273,7513,1064],{"class":1333},[273,7515,7516,7518,7520,7522,7524],{"class":275,"line":771},[273,7517,7488],{"class":820},[273,7519,7294],{"class":641},[273,7521,1054],{"class":1333},[273,7523,2303],{"class":1199},[273,7525,1064],{"class":1333},[273,7527,7528],{"class":275,"line":784},[273,7529,556],{"emptyLinePlaceholder":555},[273,7531,7532,7534,7536,7538,7540,7542,7544,7546,7548,7550,7552,7554,7556],{"class":275,"line":789},[273,7533,7310],{"class":641},[273,7535,1054],{"class":1333},[273,7537,3731],{"class":860},[273,7539,1185],{"class":820},[273,7541,7319],{"class":860},[273,7543,1185],{"class":820},[273,7545,7324],{"class":860},[273,7547,1463],{"class":1333},[273,7549,1185],{"class":820},[273,7551,7331],{"class":641},[273,7553,1054],{"class":1333},[273,7555,7336],{"class":1339},[273,7557,1064],{"class":1333},[273,7559,7560,7562,7564,7566,7568,7570,7572,7574,7576,7578,7580,7582,7584,7586,7588,7590,7592],{"class":275,"line":1471},[273,7561,7310],{"class":641},[273,7563,1054],{"class":1333},[273,7565,3731],{"class":860},[273,7567,1185],{"class":820},[273,7569,7319],{"class":860},[273,7571,1185],{"class":820},[273,7573,3736],{"class":860},[273,7575,1185],{"class":820},[273,7577,2093],{"class":860},[273,7579,1463],{"class":1333},[273,7581,1185],{"class":820},[273,7583,7331],{"class":641},[273,7585,1054],{"class":1333},[273,7587,2282],{"class":860},[273,7589,1185],{"class":820},[273,7591,2093],{"class":860},[273,7593,1064],{"class":1333},[273,7595,7596,7598,7600,7602,7604,7606,7608,7610,7612,7614,7616,7618,7620,7622,7625],{"class":275,"line":1493},[273,7597,7310],{"class":641},[273,7599,1054],{"class":1333},[273,7601,3731],{"class":860},[273,7603,1185],{"class":820},[273,7605,7319],{"class":860},[273,7607,1185],{"class":820},[273,7609,3736],{"class":860},[273,7611,1185],{"class":820},[273,7613,2644],{"class":860},[273,7615,1463],{"class":1333},[273,7617,1185],{"class":820},[273,7619,7331],{"class":641},[273,7621,1054],{"class":1333},[273,7623,7624],{"class":1339},"false",[273,7626,1064],{"class":1333},[273,7628,7629,7631],{"class":275,"line":1535},[273,7630,2174],{"class":820},[273,7632,1064],{"class":1333},[273,7634,7635],{"class":275,"line":1543},[273,7636,556],{"emptyLinePlaceholder":555},[273,7638,7639,7641,7643,7645,7648,7650,7652,7654,7656,7658],{"class":275,"line":1548},[273,7640,7237],{"class":641},[273,7642,1054],{"class":1333},[273,7644,833],{"class":820},[273,7646,7647],{"class":289},"should return error for empty title",[273,7649,833],{"class":820},[273,7651,1375],{"class":820},[273,7653,7251],{"class":829},[273,7655,1671],{"class":820},[273,7657,1466],{"class":829},[273,7659,839],{"class":820},[273,7661,7662,7664,7666,7668,7670,7672,7674,7676],{"class":275,"line":1554},[273,7663,6815],{"class":829},[273,7665,3553],{"class":860},[273,7667,1725],{"class":820},[273,7669,3558],{"class":2084},[273,7671,5126],{"class":641},[273,7673,1054],{"class":1333},[273,7675,1214],{"class":860},[273,7677,1064],{"class":1333},[273,7679,7680,7682,7684,7686,7688,7690,7692],{"class":275,"line":1591},[273,7681,7488],{"class":820},[273,7683,2030],{"class":641},[273,7685,1054],{"class":1333},[273,7687,833],{"class":820},[273,7689,1370],{"class":289},[273,7691,833],{"class":820},[273,7693,1064],{"class":1333},[273,7695,7696,7698,7700,7702,7704,7706,7708,7710,7712],{"class":275,"line":1644},[273,7697,7488],{"class":820},[273,7699,7507],{"class":641},[273,7701,1054],{"class":1333},[273,7703,1330],{"class":820},[273,7705,1820],{"class":1333},[273,7707,836],{"class":820},[273,7709,2111],{"class":820},[273,7711,1343],{"class":820},[273,7713,1064],{"class":1333},[273,7715,7716,7718,7720,7722,7724],{"class":275,"line":1651},[273,7717,7488],{"class":820},[273,7719,7294],{"class":641},[273,7721,1054],{"class":1333},[273,7723,2132],{"class":1199},[273,7725,1064],{"class":1333},[273,7727,7728],{"class":275,"line":1656},[273,7729,556],{"emptyLinePlaceholder":555},[273,7731,7732,7734,7736,7738,7740,7742,7744,7746,7748,7750,7752,7754,7756],{"class":275,"line":1678},[273,7733,7310],{"class":641},[273,7735,1054],{"class":1333},[273,7737,3731],{"class":860},[273,7739,1185],{"class":820},[273,7741,7319],{"class":860},[273,7743,1185],{"class":820},[273,7745,7324],{"class":860},[273,7747,1463],{"class":1333},[273,7749,1185],{"class":820},[273,7751,7331],{"class":641},[273,7753,1054],{"class":1333},[273,7755,7624],{"class":1339},[273,7757,1064],{"class":1333},[273,7759,7760,7762],{"class":275,"line":1707},[273,7761,2174],{"class":820},[273,7763,1064],{"class":1333},[273,7765,7766,7768],{"class":275,"line":1714},[273,7767,2004],{"class":820},[273,7769,1064],{"class":1333},[273,7771,7772,7774],{"class":275,"line":1719},[273,7773,1538],{"class":820},[273,7775,1064],{"class":860},[127,7777,7779],{"id":7778},"frontend-testing-with-vue-test-utils","Frontend Testing with Vue Test Utils",[263,7781,7783],{"className":1028,"code":7782,"language":1030,"meta":268,"style":268},"// client/tests/unit/TaskForm.spec.js\nimport { shallowMount } from \"@vue/test-utils\"\nimport TaskForm from \"@/components/TaskForm.vue\"\n\ndescribe(\"TaskForm.vue\", () => {\n it(\"emits task-created event when form is submitted\", async () => {\n const wrapper = shallowMount(TaskForm)\n const input = wrapper.find(\".form-input\")\n const form = wrapper.find(\".task-form\")\n\n await input.setValue(\"New task\")\n await form.trigger(\"submit.prevent\")\n\n expect(wrapper.emitted(\"task-created\")).toBeTruthy()\n expect(wrapper.emitted(\"task-created\")[0][0]).toEqual({\n title: \"New task\",\n })\n })\n\n it(\"clears input after successful submission\", async () => {\n const wrapper = shallowMount(TaskForm)\n const input = wrapper.find(\".form-input\")\n\n await input.setValue(\"New task\")\n await wrapper.find(\".task-form\").trigger(\"submit.prevent\")\n\n expect(wrapper.vm.newTaskTitle).toBe(\"\")\n })\n})\n",[270,7784,7785,7790,7810,7825,7829,7850,7874,7891,7918,7944,7948,7970,7992,7996,8028,8069,8084,8090,8096,8100,8123,8139,8163,8167,8187,8221,8225,8255,8261],{"__ignoreMap":268},[273,7786,7787],{"class":275,"line":276},[273,7788,7789],{"class":293},"// client/tests/unit/TaskForm.spec.js\n",[273,7791,7792,7794,7796,7799,7801,7803,7805,7808],{"class":275,"line":283},[273,7793,3312],{"class":2084},[273,7795,2062],{"class":820},[273,7797,7798],{"class":860}," shallowMount",[273,7800,1343],{"class":820},[273,7802,3355],{"class":2084},[273,7804,854],{"class":820},[273,7806,7807],{"class":289},"@vue/test-utils",[273,7809,958],{"class":820},[273,7811,7812,7814,7816,7818,7820,7823],{"class":275,"line":16},[273,7813,3312],{"class":2084},[273,7815,3315],{"class":860},[273,7817,3318],{"class":2084},[273,7819,854],{"class":820},[273,7821,7822],{"class":289},"@/components/TaskForm.vue",[273,7824,958],{"class":820},[273,7826,7827],{"class":275,"line":308},[273,7828,556],{"emptyLinePlaceholder":555},[273,7830,7831,7833,7835,7837,7840,7842,7844,7846,7848],{"class":275,"line":321},[273,7832,7193],{"class":641},[273,7834,1054],{"class":860},[273,7836,833],{"class":820},[273,7838,7839],{"class":289},"TaskForm.vue",[273,7841,833],{"class":820},[273,7843,1375],{"class":820},[273,7845,1671],{"class":820},[273,7847,1466],{"class":829},[273,7849,839],{"class":820},[273,7851,7852,7855,7857,7859,7862,7864,7866,7868,7870,7872],{"class":275,"line":333},[273,7853,7854],{"class":641}," it",[273,7856,1054],{"class":1333},[273,7858,833],{"class":820},[273,7860,7861],{"class":289},"emits task-created event when form is submitted",[273,7863,833],{"class":820},[273,7865,1375],{"class":820},[273,7867,7251],{"class":829},[273,7869,1671],{"class":820},[273,7871,1466],{"class":829},[273,7873,839],{"class":820},[273,7875,7876,7878,7881,7883,7885,7887,7889],{"class":275,"line":345},[273,7877,5148],{"class":829},[273,7879,7880],{"class":860}," wrapper",[273,7882,1725],{"class":820},[273,7884,7798],{"class":641},[273,7886,1054],{"class":1333},[273,7888,3201],{"class":860},[273,7890,1064],{"class":1333},[273,7892,7893,7895,7898,7900,7902,7904,7907,7909,7911,7914,7916],{"class":275,"line":357},[273,7894,5148],{"class":829},[273,7896,7897],{"class":860}," input",[273,7899,1725],{"class":820},[273,7901,7880],{"class":860},[273,7903,1185],{"class":820},[273,7905,7906],{"class":641},"find",[273,7908,1054],{"class":1333},[273,7910,833],{"class":820},[273,7912,7913],{"class":289},".form-input",[273,7915,833],{"class":820},[273,7917,1064],{"class":1333},[273,7919,7920,7922,7925,7927,7929,7931,7933,7935,7937,7940,7942],{"class":275,"line":370},[273,7921,5148],{"class":829},[273,7923,7924],{"class":860}," form",[273,7926,1725],{"class":820},[273,7928,7880],{"class":860},[273,7930,1185],{"class":820},[273,7932,7906],{"class":641},[273,7934,1054],{"class":1333},[273,7936,833],{"class":820},[273,7938,7939],{"class":289},".task-form",[273,7941,833],{"class":820},[273,7943,1064],{"class":1333},[273,7945,7946],{"class":275,"line":380},[273,7947,556],{"emptyLinePlaceholder":555},[273,7949,7950,7952,7954,7956,7959,7961,7963,7966,7968],{"class":275,"line":390},[273,7951,3492],{"class":2084},[273,7953,7897],{"class":860},[273,7955,1185],{"class":820},[273,7957,7958],{"class":641},"setValue",[273,7960,1054],{"class":1333},[273,7962,833],{"class":820},[273,7964,7965],{"class":289},"New task",[273,7967,833],{"class":820},[273,7969,1064],{"class":1333},[273,7971,7972,7974,7976,7978,7981,7983,7985,7988,7990],{"class":275,"line":401},[273,7973,3492],{"class":2084},[273,7975,7924],{"class":860},[273,7977,1185],{"class":820},[273,7979,7980],{"class":641},"trigger",[273,7982,1054],{"class":1333},[273,7984,833],{"class":820},[273,7986,7987],{"class":289},"submit.prevent",[273,7989,833],{"class":820},[273,7991,1064],{"class":1333},[273,7993,7994],{"class":275,"line":29},[273,7995,556],{"emptyLinePlaceholder":555},[273,7997,7998,8001,8003,8006,8008,8011,8013,8015,8017,8019,8021,8023,8026],{"class":275,"line":420},[273,7999,8000],{"class":641}," expect",[273,8002,1054],{"class":1333},[273,8004,8005],{"class":860},"wrapper",[273,8007,1185],{"class":820},[273,8009,8010],{"class":641},"emitted",[273,8012,1054],{"class":1333},[273,8014,833],{"class":820},[273,8016,4652],{"class":289},[273,8018,833],{"class":820},[273,8020,7367],{"class":1333},[273,8022,1185],{"class":820},[273,8024,8025],{"class":641},"toBeTruthy",[273,8027,1170],{"class":1333},[273,8029,8030,8032,8034,8036,8038,8040,8042,8044,8046,8048,8050,8052,8055,8057,8060,8062,8065,8067],{"class":275,"line":430},[273,8031,8000],{"class":641},[273,8033,1054],{"class":1333},[273,8035,8005],{"class":860},[273,8037,1185],{"class":820},[273,8039,8010],{"class":641},[273,8041,1054],{"class":1333},[273,8043,833],{"class":820},[273,8045,4652],{"class":289},[273,8047,833],{"class":820},[273,8049,2978],{"class":1333},[273,8051,2981],{"class":1199},[273,8053,8054],{"class":1333},"][",[273,8056,2981],{"class":1199},[273,8058,8059],{"class":1333},"])",[273,8061,1185],{"class":820},[273,8063,8064],{"class":641},"toEqual",[273,8066,1054],{"class":1333},[273,8068,821],{"class":820},[273,8070,8071,8074,8076,8078,8080,8082],{"class":275,"line":440},[273,8072,8073],{"class":1333}," title",[273,8075,836],{"class":820},[273,8077,854],{"class":820},[273,8079,7965],{"class":289},[273,8081,833],{"class":820},[273,8083,879],{"class":820},[273,8085,8086,8088],{"class":275,"line":450},[273,8087,2174],{"class":820},[273,8089,1064],{"class":1333},[273,8091,8092,8094],{"class":275,"line":460},[273,8093,2004],{"class":820},[273,8095,1064],{"class":1333},[273,8097,8098],{"class":275,"line":471},[273,8099,556],{"emptyLinePlaceholder":555},[273,8101,8102,8104,8106,8108,8111,8113,8115,8117,8119,8121],{"class":275,"line":743},[273,8103,7854],{"class":641},[273,8105,1054],{"class":1333},[273,8107,833],{"class":820},[273,8109,8110],{"class":289},"clears input after successful submission",[273,8112,833],{"class":820},[273,8114,1375],{"class":820},[273,8116,7251],{"class":829},[273,8118,1671],{"class":820},[273,8120,1466],{"class":829},[273,8122,839],{"class":820},[273,8124,8125,8127,8129,8131,8133,8135,8137],{"class":275,"line":749},[273,8126,5148],{"class":829},[273,8128,7880],{"class":860},[273,8130,1725],{"class":820},[273,8132,7798],{"class":641},[273,8134,1054],{"class":1333},[273,8136,3201],{"class":860},[273,8138,1064],{"class":1333},[273,8140,8141,8143,8145,8147,8149,8151,8153,8155,8157,8159,8161],{"class":275,"line":771},[273,8142,5148],{"class":829},[273,8144,7897],{"class":860},[273,8146,1725],{"class":820},[273,8148,7880],{"class":860},[273,8150,1185],{"class":820},[273,8152,7906],{"class":641},[273,8154,1054],{"class":1333},[273,8156,833],{"class":820},[273,8158,7913],{"class":289},[273,8160,833],{"class":820},[273,8162,1064],{"class":1333},[273,8164,8165],{"class":275,"line":784},[273,8166,556],{"emptyLinePlaceholder":555},[273,8168,8169,8171,8173,8175,8177,8179,8181,8183,8185],{"class":275,"line":789},[273,8170,3492],{"class":2084},[273,8172,7897],{"class":860},[273,8174,1185],{"class":820},[273,8176,7958],{"class":641},[273,8178,1054],{"class":1333},[273,8180,833],{"class":820},[273,8182,7965],{"class":289},[273,8184,833],{"class":820},[273,8186,1064],{"class":1333},[273,8188,8189,8191,8193,8195,8197,8199,8201,8203,8205,8207,8209,8211,8213,8215,8217,8219],{"class":275,"line":1471},[273,8190,3492],{"class":2084},[273,8192,7880],{"class":860},[273,8194,1185],{"class":820},[273,8196,7906],{"class":641},[273,8198,1054],{"class":1333},[273,8200,833],{"class":820},[273,8202,7939],{"class":289},[273,8204,833],{"class":820},[273,8206,1463],{"class":1333},[273,8208,1185],{"class":820},[273,8210,7980],{"class":641},[273,8212,1054],{"class":1333},[273,8214,833],{"class":820},[273,8216,7987],{"class":289},[273,8218,833],{"class":820},[273,8220,1064],{"class":1333},[273,8222,8223],{"class":275,"line":1493},[273,8224,556],{"emptyLinePlaceholder":555},[273,8226,8227,8229,8231,8233,8235,8238,8240,8242,8244,8246,8248,8250,8253],{"class":275,"line":1535},[273,8228,8000],{"class":641},[273,8230,1054],{"class":1333},[273,8232,8005],{"class":860},[273,8234,1185],{"class":820},[273,8236,8237],{"class":860},"vm",[273,8239,1185],{"class":820},[273,8241,4365],{"class":860},[273,8243,1463],{"class":1333},[273,8245,1185],{"class":820},[273,8247,7331],{"class":641},[273,8249,1054],{"class":1333},[273,8251,8252],{"class":820},"\"\"",[273,8254,1064],{"class":1333},[273,8256,8257,8259],{"class":275,"line":1543},[273,8258,2004],{"class":820},[273,8260,1064],{"class":1333},[273,8262,8263,8265],{"class":275,"line":1548},[273,8264,1538],{"class":820},[273,8266,1064],{"class":860},[99,8268,8270],{"id":8269},"deployment-and-production-considerations","Deployment and Production Considerations",[104,8272,8273],{},"When you're ready to deploy your application, here are some key considerations:",[127,8275,8277],{"id":8276},"environment-variables","Environment Variables",[263,8279,8281],{"className":265,"code":8280,"language":267,"meta":268,"style":268},"# .env.production\nNODE_ENV=production\nPORT=80\nDB_HOST=your-production-db-host\nDB_NAME=your-production-db\nDB_USER=your-production-user\nDB_PASS=your-secure-password\nJWT_SECRET=your-super-secure-jwt-secret\nCORS_ORIGIN=https://yourdomain.com\n",[270,8282,8283,8288,8298,8307,8316,8325,8334,8343,8353],{"__ignoreMap":268},[273,8284,8285],{"class":275,"line":276},[273,8286,8287],{"class":293},"# .env.production\n",[273,8289,8290,8293,8295],{"class":275,"line":283},[273,8291,8292],{"class":860},"NODE_ENV",[273,8294,1048],{"class":820},[273,8296,8297],{"class":289},"production\n",[273,8299,8300,8302,8304],{"class":275,"line":16},[273,8301,1699],{"class":860},[273,8303,1048],{"class":820},[273,8305,8306],{"class":289},"80\n",[273,8308,8309,8311,8313],{"class":275,"line":308},[273,8310,5870],{"class":860},[273,8312,1048],{"class":820},[273,8314,8315],{"class":289},"your-production-db-host\n",[273,8317,8318,8320,8322],{"class":275,"line":321},[273,8319,5908],{"class":860},[273,8321,1048],{"class":820},[273,8323,8324],{"class":289},"your-production-db\n",[273,8326,8327,8329,8331],{"class":275,"line":333},[273,8328,5927],{"class":860},[273,8330,1048],{"class":820},[273,8332,8333],{"class":289},"your-production-user\n",[273,8335,8336,8338,8340],{"class":275,"line":345},[273,8337,5946],{"class":860},[273,8339,1048],{"class":820},[273,8341,8342],{"class":289},"your-secure-password\n",[273,8344,8345,8348,8350],{"class":275,"line":357},[273,8346,8347],{"class":860},"JWT_SECRET",[273,8349,1048],{"class":820},[273,8351,8352],{"class":289},"your-super-secure-jwt-secret\n",[273,8354,8355,8358,8360],{"class":275,"line":370},[273,8356,8357],{"class":860},"CORS_ORIGIN",[273,8359,1048],{"class":820},[273,8361,8362],{"class":289},"https://yourdomain.com\n",[127,8364,8366],{"id":8365},"production-build-script","Production Build Script",[263,8368,8370],{"className":811,"code":8369,"language":813,"meta":268,"style":268},"{\n \"scripts\": {\n \"build\": \"cd client && npm run build && cd ../server && npm run build\",\n \"start:prod\": \"NODE_ENV=production node server/dist/app.js\",\n \"deploy\": \"npm run build && npm run start:prod\"\n }\n}\n",[270,8371,8372,8376,8388,8407,8427,8445,8449],{"__ignoreMap":268},[273,8373,8374],{"class":275,"line":276},[273,8375,821],{"class":820},[273,8377,8378,8380,8382,8384,8386],{"class":275,"line":283},[273,8379,826],{"class":820},[273,8381,830],{"class":829},[273,8383,833],{"class":820},[273,8385,836],{"class":820},[273,8387,839],{"class":820},[273,8389,8390,8392,8394,8396,8398,8400,8403,8405],{"class":275,"line":16},[273,8391,844],{"class":820},[273,8393,926],{"class":279},[273,8395,833],{"class":820},[273,8397,836],{"class":820},[273,8399,854],{"class":820},[273,8401,8402],{"class":289},"cd client && npm run build && cd ../server && npm run build",[273,8404,833],{"class":820},[273,8406,879],{"class":820},[273,8408,8409,8411,8414,8416,8418,8420,8423,8425],{"class":275,"line":308},[273,8410,844],{"class":820},[273,8412,8413],{"class":279},"start:prod",[273,8415,833],{"class":820},[273,8417,836],{"class":820},[273,8419,854],{"class":820},[273,8421,8422],{"class":289},"NODE_ENV=production node server/dist/app.js",[273,8424,833],{"class":820},[273,8426,879],{"class":820},[273,8428,8429,8431,8434,8436,8438,8440,8443],{"class":275,"line":321},[273,8430,844],{"class":820},[273,8432,8433],{"class":279},"deploy",[273,8435,833],{"class":820},[273,8437,836],{"class":820},[273,8439,854],{"class":820},[273,8441,8442],{"class":289},"npm run build && npm run start:prod",[273,8444,958],{"class":820},[273,8446,8447],{"class":275,"line":333},[273,8448,999],{"class":820},[273,8450,8451],{"class":275,"line":345},[273,8452,1004],{"class":820},[127,8454,8456],{"id":8455},"docker-configuration","Docker Configuration",[263,8458,8462],{"className":8459,"code":8460,"language":8461,"meta":268,"style":268},"language-dockerfile shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Dockerfile\nFROM node:16-alpine\n\nWORKDIR /app\n\n# Copy package files\nCOPY package*.json ./\nCOPY client/package*.json ./client/\nCOPY server/package*.json ./server/\n\n# Install dependencies\nRUN npm ci --only=production\nRUN cd client && npm ci --only=production\nRUN cd server && npm ci --only=production\n\n# Copy source code\nCOPY . .\n\n# Build application\nRUN npm run build\n\nEXPOSE 3000\n\nCMD [\"npm\", \"run\", \"start:prod\"]\n","dockerfile",[270,8463,8464,8469,8477,8481,8489,8493,8498,8506,8513,8520,8524,8529,8537,8544,8551,8555,8560,8567,8571,8576,8583,8587,8594,8598],{"__ignoreMap":268},[273,8465,8466],{"class":275,"line":276},[273,8467,8468],{"class":293},"# Dockerfile\n",[273,8470,8471,8474],{"class":275,"line":283},[273,8472,8473],{"class":1199},"FROM",[273,8475,8476],{"class":860}," node:16-alpine\n",[273,8478,8479],{"class":275,"line":16},[273,8480,556],{"emptyLinePlaceholder":555},[273,8482,8483,8486],{"class":275,"line":308},[273,8484,8485],{"class":1199},"WORKDIR",[273,8487,8488],{"class":860}," /app\n",[273,8490,8491],{"class":275,"line":321},[273,8492,556],{"emptyLinePlaceholder":555},[273,8494,8495],{"class":275,"line":333},[273,8496,8497],{"class":293},"# Copy package files\n",[273,8499,8500,8503],{"class":275,"line":345},[273,8501,8502],{"class":1199},"COPY",[273,8504,8505],{"class":860}," package*.json ./\n",[273,8507,8508,8510],{"class":275,"line":357},[273,8509,8502],{"class":1199},[273,8511,8512],{"class":860}," client/package*.json ./client/\n",[273,8514,8515,8517],{"class":275,"line":370},[273,8516,8502],{"class":1199},[273,8518,8519],{"class":860}," server/package*.json ./server/\n",[273,8521,8522],{"class":275,"line":380},[273,8523,556],{"emptyLinePlaceholder":555},[273,8525,8526],{"class":275,"line":390},[273,8527,8528],{"class":293},"# Install dependencies\n",[273,8530,8531,8534],{"class":275,"line":401},[273,8532,8533],{"class":1199},"RUN",[273,8535,8536],{"class":860}," npm ci --only=production\n",[273,8538,8539,8541],{"class":275,"line":29},[273,8540,8533],{"class":1199},[273,8542,8543],{"class":860}," cd client && npm ci --only=production\n",[273,8545,8546,8548],{"class":275,"line":420},[273,8547,8533],{"class":1199},[273,8549,8550],{"class":860}," cd server && npm ci --only=production\n",[273,8552,8553],{"class":275,"line":430},[273,8554,556],{"emptyLinePlaceholder":555},[273,8556,8557],{"class":275,"line":440},[273,8558,8559],{"class":293},"# Copy source code\n",[273,8561,8562,8564],{"class":275,"line":450},[273,8563,8502],{"class":1199},[273,8565,8566],{"class":860}," . .\n",[273,8568,8569],{"class":275,"line":460},[273,8570,556],{"emptyLinePlaceholder":555},[273,8572,8573],{"class":275,"line":471},[273,8574,8575],{"class":293},"# Build application\n",[273,8577,8578,8580],{"class":275,"line":743},[273,8579,8533],{"class":1199},[273,8581,8582],{"class":860}," npm run build\n",[273,8584,8585],{"class":275,"line":749},[273,8586,556],{"emptyLinePlaceholder":555},[273,8588,8589,8592],{"class":275,"line":771},[273,8590,8591],{"class":1199},"EXPOSE",[273,8593,1200],{"class":860},[273,8595,8596],{"class":275,"line":784},[273,8597,556],{"emptyLinePlaceholder":555},[273,8599,8600,8603,8606,8609,8612,8615,8617,8620],{"class":275,"line":789},[273,8601,8602],{"class":1199},"CMD",[273,8604,8605],{"class":860}," [",[273,8607,8608],{"class":289},"\"npm\"",[273,8610,8611],{"class":860},", ",[273,8613,8614],{"class":289},"\"run\"",[273,8616,8611],{"class":860},[273,8618,8619],{"class":289},"\"start:prod\"",[273,8621,1906],{"class":860},[99,8623,8625],{"id":8624},"key-takeaways-and-next-steps","Key Takeaways and Next Steps",[104,8627,8628],{},"Building full stack applications with Node.js and Vue.js offers a powerful, unified development experience. Here are the most important points to remember:",[127,8630,8632],{"id":8631},"what-weve-covered","What We've Covered",[8634,8635,8636,8642,8648,8654,8660],"ol",{},[138,8637,8638,8641],{},[111,8639,8640],{},"Unified JavaScript ecosystem"," - One language across your entire stack",[138,8643,8644,8647],{},[111,8645,8646],{},"Modern development practices"," - Hot reloading, component-based architecture, and RESTful APIs",[138,8649,8650,8653],{},[111,8651,8652],{},"Practical implementation"," - Real code examples you can use immediately",[138,8655,8656,8659],{},[111,8657,8658],{},"Testing strategies"," - Both unit and integration testing approaches",[138,8661,8662,8665],{},[111,8663,8664],{},"Production deployment"," - Environment management and containerization",[127,8667,8669],{"id":8668},"best-practices-to-follow","Best Practices to Follow",[135,8671,8672,8678,8684,8690,8696],{},[138,8673,8674,8677],{},[111,8675,8676],{},"Keep your code modular"," - Separate concerns between frontend and backend",[138,8679,8680,8683],{},[111,8681,8682],{},"Use environment variables"," - Never hardcode configuration values",[138,8685,8686,8689],{},[111,8687,8688],{},"Implement proper error handling"," - Both on server and client sides",[138,8691,8692,8695],{},[111,8693,8694],{},"Write tests"," - They save time and prevent bugs",[138,8697,8698,8701],{},[111,8699,8700],{},"Plan for scaling"," - Use patterns that grow with your application",[127,8703,8705],{"id":8704},"recommended-next-steps","Recommended Next Steps",[8634,8707,8708,8714,8720,8726,8732],{},[138,8709,8710,8713],{},[111,8711,8712],{},"Add a database"," - Replace in-memory storage with PostgreSQL or MongoDB",[138,8715,8716,8719],{},[111,8717,8718],{},"Implement authentication"," - Add user registration and login functionality",[138,8721,8722,8725],{},[111,8723,8724],{},"Add real-time features"," - Use Socket.io for live updates",[138,8727,8728,8731],{},[111,8729,8730],{},"Optimize performance"," - Implement caching and code splitting",[138,8733,8734,8737],{},[111,8735,8736],{},"Set up CI/CD"," - Automate your testing and deployment process",[104,8739,8740,8741,8746],{},"If you're interested in extending this to browser extensions, check out my ",[8742,8743,8745],"a",{"href":8744},"/blog/3-vite-vue3-browser-extension-v3","comprehensive guide on building modern browser extensions"," with similar technologies.",[104,8748,8749],{},"The journey of full stack development is continuous learning, but with Node.js and Vue.js, you have a solid foundation to build amazing applications. Start with the basics, practice regularly, and gradually add more advanced features as your confidence grows.",[104,8751,8752],{},"Remember, every expert was once a beginner. The key is to start building, learn from your mistakes, and keep improving. Happy coding!",[4136,8754,8755],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sqsOY, html code.shiki .sqsOY{--shiki-light:#8796B0;--shiki-default:#B2CCD6;--shiki-dark:#B2CCD6}",{"title":268,"searchDepth":283,"depth":283,"links":8757},[8758,8759,8765,8766,8771,8778,8783,8787,8792],{"id":101,"depth":283,"text":102},{"id":121,"depth":283,"text":122,"children":8760},[8761,8762,8763,8764],{"id":129,"depth":16,"text":130},{"id":164,"depth":16,"text":165},{"id":197,"depth":16,"text":198},{"id":224,"depth":16,"text":225},{"id":257,"depth":283,"text":258},{"id":508,"depth":283,"text":509,"children":8767},[8768,8769,8770],{"id":515,"depth":16,"text":516},{"id":614,"depth":16,"text":615},{"id":800,"depth":16,"text":801},{"id":1014,"depth":283,"text":1015,"children":8772},[8773,8774,8775,8776,8777],{"id":1021,"depth":16,"text":1022},{"id":1731,"depth":16,"text":1732},{"id":3055,"depth":16,"text":3056},{"id":4276,"depth":16,"text":4277},{"id":5065,"depth":16,"text":5066},{"id":5661,"depth":283,"text":5662,"children":8779},[8780,8781,8782],{"id":5668,"depth":16,"text":5669},{"id":6003,"depth":16,"text":6004},{"id":6352,"depth":16,"text":6353},{"id":7123,"depth":283,"text":7124,"children":8784},[8785,8786],{"id":7130,"depth":16,"text":7131},{"id":7778,"depth":16,"text":7779},{"id":8269,"depth":283,"text":8270,"children":8788},[8789,8790,8791],{"id":8276,"depth":16,"text":8277},{"id":8365,"depth":16,"text":8366},{"id":8455,"depth":16,"text":8456},{"id":8624,"depth":283,"text":8625,"children":8793},[8794,8795,8796],{"id":8631,"depth":16,"text":8632},{"id":8668,"depth":16,"text":8669},{"id":8704,"depth":16,"text":8705},"2024-11-19","Master full stack development with Node.js and Vue.js. Learn project structuring, environment setup, and best practices for building scalable web applications.",null,"md",false,"/img/blog/4-full-stack-development/banner.svg",{"readingTime":8804},"25 min read","/blog/4-full-stack-development",{"title":91,"description":8798},"blog/4-full-stack-development",[113,117,8809],"Full Stack","Ko0UQuR9gyGVI8LuM__KEgJ6EYl3fihDE7dbLOrWb8c",[8812,9335,11000],{"id":8813,"title":8814,"abstract":8799,"author":8815,"authorUrl":8799,"body":8816,"date":9301,"dateUpdated":9302,"description":9303,"excerpt":8799,"extension":8800,"featured":555,"headline":8799,"image":8799,"meta":9304,"navigation":555,"ogImage":8799,"path":9317,"seo":9318,"series":9319,"seriesDescription":9320,"seriesOrder":308,"socialImage":9321,"stem":9327,"tags":9328,"__hash__":9334},"blog/blog/16-nuxt-4-ssr-performance-optimization.md","Nuxt 4 SSR Performance Optimization: Complete Guide to Core Web Vitals","mubaidr",{"type":96,"value":8817,"toc":9283},[8818,8822,8828,8833,8847,8850,8853,8857,8860,8874,8878,8882,8885,9035,9039,9056,9060,9063,9067,9070,9073,9077,9081,9087,9091,9094,9114,9117,9121,9128,9148,9155,9159,9162,9197,9200,9204,9207,9239,9242,9245,9249,9252,9255,9260,9280],[99,8819,8821],{"id":8820},"nuxt-4-ssr-performance-optimization","Nuxt 4 SSR Performance Optimization",[104,8823,8824,8827],{},[111,8825,8826],{},"Last updated:"," February 2026",[104,8829,8830],{},[111,8831,8832],{},"What changed in this update:",[135,8834,8835,8838,8841,8844],{},[138,8836,8837],{},"Enhanced with Core Web Vitals optimization strategies",[138,8839,8840],{},"Added detailed caching configuration examples",[138,8842,8843],{},"Expanded monitoring and measurement section",[138,8845,8846],{},"Updated for Nuxt 4.0+ latest features",[104,8848,8849],{},"Server-side rendering (SSR) is one of Nuxt's most powerful features, delivering fast initial page loads and excellent SEO out of the box. However, as applications grow, optimizing SSR performance becomes crucial for maintaining sub-second response times and ensuring a smooth user experience.",[104,8851,8852],{},"After deploying multiple Nuxt 4 applications to production, I've identified key strategies that consistently deliver measurable performance improvements.",[99,8854,8856],{"id":8855},"understanding-nuxt-4-ssr-architecture","Understanding Nuxt 4 SSR Architecture",[104,8858,8859],{},"Nuxt 4 introduces several architectural improvements that impact SSR performance. The framework now uses Nitro as its server engine, which provides better caching mechanisms, more efficient rendering pipelines, and improved hydration strategies. The key to optimization lies in understanding how data flows through the SSR process: server receives request → fetches data → renders HTML → sends to client → client hydrates Vue application. Each stage presents optimization opportunities.",[104,8861,8862,8863,114,8866,8869,8870,8873],{},"The most common performance bottleneck I've encountered is data fetching. In Nuxt 4, ",[270,8864,8865],{},"useAsyncData",[270,8867,8868],{},"useFetch"," are optimized for SSR, but improper usage can lead to unnecessary server round trips. The golden rule is to fetch data as close to where it's needed as possible and leverage Nuxt's built-in caching. For example, fetching global site configuration in ",[270,8871,8872],{},"app.vue"," with a long cache time prevents repeated database queries on every page request.",[99,8875,8877],{"id":8876},"essential-optimization-strategies","Essential Optimization Strategies",[127,8879,8881],{"id":8880},"implement-smart-caching","Implement Smart Caching",[104,8883,8884],{},"Caching is the most impactful optimization technique. Nuxt 4 provides multiple caching layers that work together to reduce server load. Route-level caching stores rendered HTML for static or semi-static content, while data caching memoizes API responses. For a blog site like this one, caching individual blog posts for 24 hours and the homepage for 5 minutes reduces server load by over 80%.",[263,8886,8890],{"className":8887,"code":8888,"language":8889,"meta":268,"style":268},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","// In nuxt.config.ts\nexport default defineNuxtConfig({\n routeRules: {\n \"/blog/**\": { isr: 86400 }, // 24 hours\n \"/\": { isr: 300 }, // 5 minutes\n \"/api/**\": {\n cors: true,\n headers: { \"Cache-Control\": \"public, max-age=3600\" },\n },\n },\n})\n","typescript",[270,8891,8892,8897,8910,8919,8946,8970,8983,8994,9021,9025,9029],{"__ignoreMap":268},[273,8893,8894],{"class":275,"line":276},[273,8895,8896],{"class":293},"// In nuxt.config.ts\n",[273,8898,8899,8901,8903,8906,8908],{"class":275,"line":283},[273,8900,3371],{"class":2084},[273,8902,3374],{"class":2084},[273,8904,8905],{"class":641}," defineNuxtConfig",[273,8907,1054],{"class":860},[273,8909,821],{"class":820},[273,8911,8912,8915,8917],{"class":275,"line":16},[273,8913,8914],{"class":1333}," routeRules",[273,8916,836],{"class":820},[273,8918,839],{"class":820},[273,8920,8921,8923,8926,8928,8930,8932,8935,8937,8940,8943],{"class":275,"line":308},[273,8922,844],{"class":820},[273,8924,8925],{"class":1333},"/blog/**",[273,8927,833],{"class":820},[273,8929,836],{"class":820},[273,8931,2062],{"class":820},[273,8933,8934],{"class":1333}," isr",[273,8936,836],{"class":820},[273,8938,8939],{"class":1199}," 86400",[273,8941,8942],{"class":820}," },",[273,8944,8945],{"class":293}," // 24 hours\n",[273,8947,8948,8950,8952,8954,8956,8958,8960,8962,8965,8967],{"class":275,"line":321},[273,8949,844],{"class":820},[273,8951,94],{"class":1333},[273,8953,833],{"class":820},[273,8955,836],{"class":820},[273,8957,2062],{"class":820},[273,8959,8934],{"class":1333},[273,8961,836],{"class":820},[273,8963,8964],{"class":1199}," 300",[273,8966,8942],{"class":820},[273,8968,8969],{"class":293}," // 5 minutes\n",[273,8971,8972,8974,8977,8979,8981],{"class":275,"line":333},[273,8973,844],{"class":820},[273,8975,8976],{"class":1333},"/api/**",[273,8978,833],{"class":820},[273,8980,836],{"class":820},[273,8982,839],{"class":820},[273,8984,8985,8988,8990,8992],{"class":275,"line":345},[273,8986,8987],{"class":1333}," cors",[273,8989,836],{"class":820},[273,8991,1340],{"class":1339},[273,8993,879],{"class":820},[273,8995,8996,8998,9000,9002,9004,9007,9009,9011,9013,9016,9018],{"class":275,"line":357},[273,8997,5183],{"class":1333},[273,8999,836],{"class":820},[273,9001,2062],{"class":820},[273,9003,854],{"class":820},[273,9005,9006],{"class":1333},"Cache-Control",[273,9008,833],{"class":820},[273,9010,836],{"class":820},[273,9012,854],{"class":820},[273,9014,9015],{"class":289},"public, max-age=3600",[273,9017,833],{"class":820},[273,9019,9020],{"class":820}," },\n",[273,9022,9023],{"class":275,"line":370},[273,9024,3668],{"class":820},[273,9026,9027],{"class":275,"line":380},[273,9028,963],{"class":820},[273,9030,9031,9033],{"class":275,"line":390},[273,9032,1538],{"class":820},[273,9034,1064],{"class":860},[127,9036,9038],{"id":9037},"optimize-data-fetching-patterns","Optimize Data Fetching Patterns",[104,9040,9041,9042,9044,9045,9048,9049,114,9052,9055],{},"Proper data fetching patterns significantly impact SSR performance. Always use ",[270,9043,8865],{}," with a unique key to enable request deduplication. When fetching multiple independent data sources, use ",[270,9046,9047],{},"Promise.all"," to parallelize requests instead of awaiting them sequentially. Additionally, leverage ",[270,9050,9051],{},"pick",[270,9053,9054],{},"transform"," options to minimize payload size—sending only the data you need reduces both network transfer and processing time.",[127,9057,9059],{"id":9058},"leverage-static-generation-where-possible","Leverage Static Generation Where Possible",[104,9061,9062],{},"Not every page needs full SSR. Nuxt 4's hybrid rendering allows you to choose the optimal rendering strategy per route. Marketing pages, documentation, and blog posts work perfectly with static generation or incremental static regeneration (ISR). Reserve full SSR for pages with highly dynamic content like user dashboards or real-time data displays. This approach dramatically reduces server load while maintaining the benefits of server rendering for SEO.",[99,9064,9066],{"id":9065},"measuring-and-monitoring-performance","Measuring and Monitoring Performance",[104,9068,9069],{},"Optimization without measurement is guesswork. Implement comprehensive monitoring to track SSR performance metrics. Key metrics to watch include Time to First Byte (TTFB), First Contentful Paint (FCP), and server response times. Nuxt 4 integrates seamlessly with monitoring tools like Sentry and DataDog, providing real-time insights into performance bottlenecks.",[104,9071,9072],{},"Set up performance budgets in your CI/CD pipeline to catch regressions early. For example, fail the build if TTFB increases by more than 100ms compared to the previous deployment. This proactive approach prevents performance degradation from accumulating over time.",[99,9074,9076],{"id":9075},"faq-nuxt-4-ssr-performance","FAQ: Nuxt 4 SSR Performance",[127,9078,9080],{"id":9079},"what-is-the-most-impactful-ssr-optimization-for-nuxt-4","What is the most impactful SSR optimization for Nuxt 4?",[104,9082,9083,9086],{},[111,9084,9085],{},"Caching is the highest-impact optimization."," Implementing route-level caching with ISR (Incremental Static Regeneration) can reduce server load by 80% or more. For a blog or content site, cache pages for 5-60 minutes and API responses for 1-24 hours depending on how frequently data changes.",[127,9088,9090],{"id":9089},"how-do-i-improve-core-web-vitals-with-nuxt-4","How do I improve Core Web Vitals with Nuxt 4?",[104,9092,9093],{},"Focus on these key areas:",[135,9095,9096,9102,9108],{},[138,9097,9098,9101],{},[111,9099,9100],{},"LCP (Largest Contentful Paint)",": Use ISR, optimize images, implement smart caching",[138,9103,9104,9107],{},[111,9105,9106],{},"FID (First Input Delay)",": Code splitting, reduce JavaScript bundle size",[138,9109,9110,9113],{},[111,9111,9112],{},"CLS (Cumulative Layout Shift)",": Reserve space for images and dynamic content, use skeleton loaders",[104,9115,9116],{},"Nuxt 4's hybrid rendering and Nitro caching make achieving good Core Web Vitals significantly easier than previous versions.",[127,9118,9120],{"id":9119},"should-i-use-ssr-or-static-generation-for-my-nuxt-app","Should I use SSR or static generation for my Nuxt app?",[104,9122,9123,9124,9127],{},"Use ",[111,9125,9126],{},"hybrid rendering"," based on page requirements:",[135,9129,9130,9136,9142],{},[138,9131,9132,9135],{},[111,9133,9134],{},"Static generation (prerender)",": Blog posts, documentation, marketing pages",[138,9137,9138,9141],{},[111,9139,9140],{},"ISR (Incremental Static Regeneration)",": Homepage, category pages, product listings",[138,9143,9144,9147],{},[111,9145,9146],{},"Full SSR",": User dashboards, real-time data, personalized content",[104,9149,9150,9151,9154],{},"Nuxt 4's ",[270,9152,9153],{},"routeRules"," make it easy to mix strategies in a single application.",[127,9156,9158],{"id":9157},"why-is-my-nuxt-app-slow-despite-using-ssr","Why is my Nuxt app slow despite using SSR?",[104,9160,9161],{},"Common causes:",[8634,9163,9164,9170,9179,9185,9191],{},[138,9165,9166,9169],{},[111,9167,9168],{},"No caching",": Every request hits the server",[138,9171,9172,9175,9176,9178],{},[111,9173,9174],{},"Sequential data fetching",": Not using ",[270,9177,9047],{}," for parallel requests",[138,9180,9181,9184],{},[111,9182,9183],{},"Over-fetching",": Loading more data than needed",[138,9186,9187,9190],{},[111,9188,9189],{},"Large bundles",": Not code splitting effectively",[138,9192,9193,9196],{},[111,9194,9195],{},"Database bottlenecks",": Missing database query optimization",[104,9198,9199],{},"Start by implementing caching—it typically provides 50-80% performance improvement.",[127,9201,9203],{"id":9202},"how-do-i-monitor-ssr-performance-in-production","How do I monitor SSR performance in production?",[104,9205,9206],{},"Use these tools and metrics:",[135,9208,9209,9215,9221,9227,9233],{},[138,9210,9211,9214],{},[111,9212,9213],{},"Nuxt DevTools",": Local development performance",[138,9216,9217,9220],{},[111,9218,9219],{},"Sentry",": Error tracking and performance monitoring",[138,9222,9223,9226],{},[111,9224,9225],{},"Google Analytics 4",": Real user monitoring (RUM)",[138,9228,9229,9232],{},[111,9230,9231],{},"Lighthouse CI",": Automated performance budgets in CI/CD",[138,9234,9235,9238],{},[111,9236,9237],{},"Nitro server logs",": TTFB and server response times",[104,9240,9241],{},"Key metrics to track: TTFB <200ms, LCP <2.5s, FID <100ms, CLS <0.1.",[9243,9244],"hr",{},[99,9246,9248],{"id":9247},"conclusion","Conclusion",[104,9250,9251],{},"Optimizing Nuxt 4 SSR performance is an iterative process that requires understanding the framework's architecture, implementing smart caching strategies, and continuously monitoring results. The strategies outlined here—smart caching, optimized data fetching, hybrid rendering, and comprehensive monitoring—have consistently delivered 50-80% performance improvements in production applications.",[104,9253,9254],{},"Start with the highest-impact optimizations (caching and static generation), measure the results, and iterate based on real-world performance data. Your users—and search engines—will thank you.",[104,9256,9257],{},[111,9258,9259],{},"Related Reading:",[135,9261,9262,9268,9274],{},[138,9263,9264],{},[8742,9265,9267],{"href":9266},"/blog/22-nuxt-3-to-4-migration-guide-2026","Nuxt 3 to Nuxt 4 Migration Guide",[138,9269,9270],{},[8742,9271,9273],{"href":9272},"/blog/23-ai-seo-developer-blog-guide-2026","AI SEO for Developer Blogs",[138,9275,9276],{},[8742,9277,9279],{"href":9278},"/blog/24-nuxt-4-edge-computing-guide","Edge Computing with Nuxt 4",[4136,9281,9282],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":268,"searchDepth":283,"depth":283,"links":9284},[9285,9286,9287,9292,9293,9300],{"id":8820,"depth":283,"text":8821},{"id":8855,"depth":283,"text":8856},{"id":8876,"depth":283,"text":8877,"children":9288},[9289,9290,9291],{"id":8880,"depth":16,"text":8881},{"id":9037,"depth":16,"text":9038},{"id":9058,"depth":16,"text":9059},{"id":9065,"depth":283,"text":9066},{"id":9075,"depth":283,"text":9076,"children":9294},[9295,9296,9297,9298,9299],{"id":9079,"depth":16,"text":9080},{"id":9089,"depth":16,"text":9090},{"id":9119,"depth":16,"text":9120},{"id":9157,"depth":16,"text":9158},{"id":9202,"depth":16,"text":9203},{"id":9247,"depth":283,"text":9248},"2026-02-22","2026-02-25","Master Nuxt 4 SSR performance with proven strategies for Core Web Vitals optimization. Learn caching, data fetching, hybrid rendering, and monitoring for lightning-fast page loads.",{"readingTime":9305,"keywords":9306},"10 min read",[9307,9308,9309,9310,9311,9312,9313,9314,9315,9316],"nuxt 4 performance","core web vitals nuxt","nuxt lighthouse score","ssr optimization","nuxt caching strategies","nuxt 4 speed","vue ssr performance","nuxt nitro caching","web vitals optimization","nuxt 4 best practices","/blog/16-nuxt-4-ssr-performance-optimization",{"title":8814,"description":9303},"Modern Development","Exploring cutting-edge development practices and tools",{"src":9322,"mime":9323,"alt":9324,"width":9325,"height":9326},"/img/blog/16-nuxt-4-ssr-performance-optimization/banner.svg","svg","Nuxt 4 SSR Performance Optimization - Lightning-fast page loads with speedometer and lightning bolt icons on green gradient background",1200,630,"blog/16-nuxt-4-ssr-performance-optimization",[9329,117,9330,9331,9332,9333],"Nuxt","SSR","Performance","Optimization","Core Web Vitals","XhRUZiLhn6SJsObNSFfkCf0T1Lb4RVB7phsNfmDX75E",{"id":9336,"title":9337,"abstract":8799,"author":8815,"authorUrl":8799,"body":9338,"date":10986,"dateUpdated":10986,"description":10987,"excerpt":8799,"extension":8800,"featured":555,"headline":8799,"image":10988,"meta":10989,"navigation":555,"ogImage":8799,"path":10990,"seo":10991,"series":10992,"seriesDescription":10993,"seriesOrder":283,"socialImage":8799,"stem":10994,"tags":10995,"__hash__":10999},"blog/blog/5-vue-3-composition-api-best-practices.md","Vue 3 Composition API: Best Practices for Modern Development",{"type":96,"value":9339,"toc":10964},[9340,9343,9346,9350,9353,9373,9377,9381,9639,9643,9646,9978,9986,9989,10142,10146,10150,10153,10361,10365,10368,10623,10627,10635,10638,10692,10699,10702,10752,10756,10759,10953,10955,10958,10961],[99,9341,9337],{"id":9342},"vue-3-composition-api-best-practices-for-modern-development",[104,9344,9345],{},"The Vue 3 Composition API has revolutionized how we write Vue applications, offering better logic reuse, improved TypeScript support, and more flexible component organization. After working with it in production for over two years, here are the essential best practices I've discovered.",[99,9347,9349],{"id":9348},"why-composition-api-matters","Why Composition API Matters",[104,9351,9352],{},"The Composition API addresses several limitations of the Options API:",[135,9354,9355,9361,9367],{},[138,9356,9357,9360],{},[111,9358,9359],{},"Better Logic Reuse",": Composables make it easy to extract and share reactive logic",[138,9362,9363,9366],{},[111,9364,9365],{},"Improved TypeScript Support",": Better type inference and IDE support",[138,9368,9369,9372],{},[111,9370,9371],{},"Flexible Organization",": Group related logic together instead of separating by option type",[99,9374,9376],{"id":9375},"essential-best-practices","Essential Best Practices",[127,9378,9380],{"id":9379},"_1-use-composables-for-reusable-logic","1. Use Composables for Reusable Logic",[263,9382,9384],{"className":1028,"code":9383,"language":1030,"meta":268,"style":268},"// composables/useCounter.js\nimport { ref, computed } from \"vue\"\n\nexport function useCounter(initialValue = 0) {\n const count = ref(initialValue)\n\n const increment = () => count.value++\n const decrement = () => count.value--\n const reset = () => (count.value = initialValue)\n\n const isEven = computed(() => count.value % 2 === 0)\n\n return {\n count: readonly(count),\n increment,\n decrement,\n reset,\n isEven,\n }\n}\n",[270,9385,9386,9391,9415,9419,9442,9459,9463,9486,9508,9537,9541,9575,9579,9586,9603,9610,9617,9624,9631,9635],{"__ignoreMap":268},[273,9387,9388],{"class":275,"line":276},[273,9389,9390],{"class":293},"// composables/useCounter.js\n",[273,9392,9393,9395,9397,9400,9402,9405,9407,9409,9411,9413],{"class":275,"line":283},[273,9394,3312],{"class":2084},[273,9396,2062],{"class":820},[273,9398,9399],{"class":860}," ref",[273,9401,1375],{"class":820},[273,9403,9404],{"class":860}," computed",[273,9406,1343],{"class":820},[273,9408,3355],{"class":2084},[273,9410,854],{"class":820},[273,9412,677],{"class":289},[273,9414,958],{"class":820},[273,9416,9417],{"class":275,"line":16},[273,9418,556],{"emptyLinePlaceholder":555},[273,9420,9421,9423,9426,9429,9431,9434,9436,9438,9440],{"class":275,"line":308},[273,9422,3371],{"class":2084},[273,9424,9425],{"class":829}," function",[273,9427,9428],{"class":641}," useCounter",[273,9430,1054],{"class":820},[273,9432,9433],{"class":1444},"initialValue",[273,9435,1725],{"class":820},[273,9437,4244],{"class":1199},[273,9439,1463],{"class":820},[273,9441,839],{"class":820},[273,9443,9444,9446,9449,9451,9453,9455,9457],{"class":275,"line":321},[273,9445,2059],{"class":829},[273,9447,9448],{"class":860}," count",[273,9450,1725],{"class":820},[273,9452,9399],{"class":641},[273,9454,1054],{"class":1333},[273,9456,9433],{"class":860},[273,9458,1064],{"class":1333},[273,9460,9461],{"class":275,"line":333},[273,9462,556],{"emptyLinePlaceholder":555},[273,9464,9465,9467,9470,9472,9474,9476,9478,9480,9483],{"class":275,"line":345},[273,9466,2059],{"class":829},[273,9468,9469],{"class":860}," increment",[273,9471,1725],{"class":820},[273,9473,1671],{"class":820},[273,9475,1466],{"class":829},[273,9477,9448],{"class":860},[273,9479,1185],{"class":820},[273,9481,9482],{"class":860},"value",[273,9484,9485],{"class":820},"++\n",[273,9487,9488,9490,9493,9495,9497,9499,9501,9503,9505],{"class":275,"line":357},[273,9489,2059],{"class":829},[273,9491,9492],{"class":860}," decrement",[273,9494,1725],{"class":820},[273,9496,1671],{"class":820},[273,9498,1466],{"class":829},[273,9500,9448],{"class":860},[273,9502,1185],{"class":820},[273,9504,9482],{"class":860},[273,9506,9507],{"class":820},"--\n",[273,9509,9510,9512,9515,9517,9519,9521,9523,9526,9528,9530,9532,9535],{"class":275,"line":370},[273,9511,2059],{"class":829},[273,9513,9514],{"class":860}," reset",[273,9516,1725],{"class":820},[273,9518,1671],{"class":820},[273,9520,1466],{"class":829},[273,9522,1575],{"class":1333},[273,9524,9525],{"class":860},"count",[273,9527,1185],{"class":820},[273,9529,9482],{"class":860},[273,9531,1725],{"class":820},[273,9533,9534],{"class":860}," initialValue",[273,9536,1064],{"class":1333},[273,9538,9539],{"class":275,"line":380},[273,9540,556],{"emptyLinePlaceholder":555},[273,9542,9543,9545,9548,9550,9552,9554,9556,9558,9560,9562,9564,9567,9569,9571,9573],{"class":275,"line":390},[273,9544,2059],{"class":829},[273,9546,9547],{"class":860}," isEven",[273,9549,1725],{"class":820},[273,9551,9404],{"class":641},[273,9553,1054],{"class":1333},[273,9555,1629],{"class":820},[273,9557,1466],{"class":829},[273,9559,9448],{"class":860},[273,9561,1185],{"class":820},[273,9563,9482],{"class":860},[273,9565,9566],{"class":820}," %",[273,9568,1866],{"class":1199},[273,9570,2490],{"class":820},[273,9572,4244],{"class":1199},[273,9574,1064],{"class":1333},[273,9576,9577],{"class":275,"line":401},[273,9578,556],{"emptyLinePlaceholder":555},[273,9580,9581,9584],{"class":275,"line":29},[273,9582,9583],{"class":2084}," return",[273,9585,839],{"class":820},[273,9587,9588,9590,9592,9595,9597,9599,9601],{"class":275,"line":420},[273,9589,1988],{"class":1333},[273,9591,836],{"class":820},[273,9593,9594],{"class":641}," readonly",[273,9596,1054],{"class":1333},[273,9598,9525],{"class":860},[273,9600,1463],{"class":1333},[273,9602,879],{"class":820},[273,9604,9605,9608],{"class":275,"line":430},[273,9606,9607],{"class":860}," increment",[273,9609,879],{"class":820},[273,9611,9612,9615],{"class":275,"line":440},[273,9613,9614],{"class":860}," decrement",[273,9616,879],{"class":820},[273,9618,9619,9622],{"class":275,"line":450},[273,9620,9621],{"class":860}," reset",[273,9623,879],{"class":820},[273,9625,9626,9629],{"class":275,"line":460},[273,9627,9628],{"class":860}," isEven",[273,9630,879],{"class":820},[273,9632,9633],{"class":275,"line":471},[273,9634,999],{"class":820},[273,9636,9637],{"class":275,"line":743},[273,9638,1004],{"class":820},[127,9640,9642],{"id":9641},"_2-organize-code-by-feature-not-by-type","2. Organize Code by Feature, Not by Type",[104,9644,9645],{},"Instead of grouping all refs together, group related logic:",[263,9647,9649],{"className":1028,"code":9648,"language":1030,"meta":268,"style":268},"// ❌ Avoid - grouped by type\nexport default {\n setup() {\n // All refs\n const user = ref(null)\n const posts = ref([])\n const loading = ref(false)\n\n // All computed\n const userDisplayName = computed(...)\n const sortedPosts = computed(...)\n\n // All methods\n const fetchUser = () => {}\n const createPost = () => {}\n }\n}\n\n// ✅ Better - grouped by feature\nexport default {\n setup() {\n // User management\n const user = ref(null)\n const userDisplayName = computed(...)\n const fetchUser = () => {}\n\n // Posts management\n const posts = ref([])\n const sortedPosts = computed(...)\n const createPost = () => {}\n\n // UI state\n const loading = ref(false)\n }\n}\n",[270,9650,9651,9656,9664,9673,9678,9696,9710,9726,9730,9735,9753,9770,9774,9779,9795,9810,9814,9818,9822,9827,9835,9843,9848,9864,9880,9894,9898,9903,9915,9931,9945,9949,9954,9970,9974],{"__ignoreMap":268},[273,9652,9653],{"class":275,"line":276},[273,9654,9655],{"class":293},"// ❌ Avoid - grouped by type\n",[273,9657,9658,9660,9662],{"class":275,"line":283},[273,9659,3371],{"class":2084},[273,9661,3374],{"class":2084},[273,9663,839],{"class":820},[273,9665,9666,9669,9671],{"class":275,"line":16},[273,9667,9668],{"class":1333}," setup",[273,9670,1629],{"class":820},[273,9672,839],{"class":820},[273,9674,9675],{"class":275,"line":308},[273,9676,9677],{"class":293}," // All refs\n",[273,9679,9680,9682,9685,9687,9689,9691,9694],{"class":275,"line":321},[273,9681,5148],{"class":829},[273,9683,9684],{"class":860}," user",[273,9686,1725],{"class":820},[273,9688,9399],{"class":641},[273,9690,1054],{"class":1333},[273,9692,9693],{"class":820},"null",[273,9695,1064],{"class":1333},[273,9697,9698,9700,9703,9705,9707],{"class":275,"line":333},[273,9699,5148],{"class":829},[273,9701,9702],{"class":860}," posts",[273,9704,1725],{"class":820},[273,9706,9399],{"class":641},[273,9708,9709],{"class":1333},"([])\n",[273,9711,9712,9714,9716,9718,9720,9722,9724],{"class":275,"line":345},[273,9713,5148],{"class":829},[273,9715,6438],{"class":860},[273,9717,1725],{"class":820},[273,9719,9399],{"class":641},[273,9721,1054],{"class":1333},[273,9723,7624],{"class":1339},[273,9725,1064],{"class":1333},[273,9727,9728],{"class":275,"line":357},[273,9729,556],{"emptyLinePlaceholder":555},[273,9731,9732],{"class":275,"line":370},[273,9733,9734],{"class":293}," // All computed\n",[273,9736,9737,9739,9742,9744,9746,9748,9751],{"class":275,"line":380},[273,9738,5148],{"class":829},[273,9740,9741],{"class":860}," userDisplayName",[273,9743,1725],{"class":820},[273,9745,9404],{"class":641},[273,9747,1054],{"class":1333},[273,9749,9750],{"class":820},"...",[273,9752,1064],{"class":1333},[273,9754,9755,9757,9760,9762,9764,9766,9768],{"class":275,"line":390},[273,9756,5148],{"class":829},[273,9758,9759],{"class":860}," sortedPosts",[273,9761,1725],{"class":820},[273,9763,9404],{"class":641},[273,9765,1054],{"class":1333},[273,9767,9750],{"class":820},[273,9769,1064],{"class":1333},[273,9771,9772],{"class":275,"line":401},[273,9773,556],{"emptyLinePlaceholder":555},[273,9775,9776],{"class":275,"line":29},[273,9777,9778],{"class":293}," // All methods\n",[273,9780,9781,9783,9786,9788,9790,9792],{"class":275,"line":420},[273,9782,5148],{"class":829},[273,9784,9785],{"class":860}," fetchUser",[273,9787,1725],{"class":820},[273,9789,1671],{"class":820},[273,9791,1466],{"class":829},[273,9793,9794],{"class":820}," {}\n",[273,9796,9797,9799,9802,9804,9806,9808],{"class":275,"line":430},[273,9798,5148],{"class":829},[273,9800,9801],{"class":860}," createPost",[273,9803,1725],{"class":820},[273,9805,1671],{"class":820},[273,9807,1466],{"class":829},[273,9809,9794],{"class":820},[273,9811,9812],{"class":275,"line":440},[273,9813,999],{"class":820},[273,9815,9816],{"class":275,"line":450},[273,9817,1004],{"class":820},[273,9819,9820],{"class":275,"line":460},[273,9821,556],{"emptyLinePlaceholder":555},[273,9823,9824],{"class":275,"line":471},[273,9825,9826],{"class":293},"// ✅ Better - grouped by feature\n",[273,9828,9829,9831,9833],{"class":275,"line":743},[273,9830,3371],{"class":2084},[273,9832,3374],{"class":2084},[273,9834,839],{"class":820},[273,9836,9837,9839,9841],{"class":275,"line":749},[273,9838,9668],{"class":1333},[273,9840,1629],{"class":820},[273,9842,839],{"class":820},[273,9844,9845],{"class":275,"line":771},[273,9846,9847],{"class":293}," // User management\n",[273,9849,9850,9852,9854,9856,9858,9860,9862],{"class":275,"line":784},[273,9851,5148],{"class":829},[273,9853,9684],{"class":860},[273,9855,1725],{"class":820},[273,9857,9399],{"class":641},[273,9859,1054],{"class":1333},[273,9861,9693],{"class":820},[273,9863,1064],{"class":1333},[273,9865,9866,9868,9870,9872,9874,9876,9878],{"class":275,"line":789},[273,9867,5148],{"class":829},[273,9869,9741],{"class":860},[273,9871,1725],{"class":820},[273,9873,9404],{"class":641},[273,9875,1054],{"class":1333},[273,9877,9750],{"class":820},[273,9879,1064],{"class":1333},[273,9881,9882,9884,9886,9888,9890,9892],{"class":275,"line":1471},[273,9883,5148],{"class":829},[273,9885,9785],{"class":860},[273,9887,1725],{"class":820},[273,9889,1671],{"class":820},[273,9891,1466],{"class":829},[273,9893,9794],{"class":820},[273,9895,9896],{"class":275,"line":1493},[273,9897,556],{"emptyLinePlaceholder":555},[273,9899,9900],{"class":275,"line":1535},[273,9901,9902],{"class":293}," // Posts management\n",[273,9904,9905,9907,9909,9911,9913],{"class":275,"line":1543},[273,9906,5148],{"class":829},[273,9908,9702],{"class":860},[273,9910,1725],{"class":820},[273,9912,9399],{"class":641},[273,9914,9709],{"class":1333},[273,9916,9917,9919,9921,9923,9925,9927,9929],{"class":275,"line":1548},[273,9918,5148],{"class":829},[273,9920,9759],{"class":860},[273,9922,1725],{"class":820},[273,9924,9404],{"class":641},[273,9926,1054],{"class":1333},[273,9928,9750],{"class":820},[273,9930,1064],{"class":1333},[273,9932,9933,9935,9937,9939,9941,9943],{"class":275,"line":1554},[273,9934,5148],{"class":829},[273,9936,9801],{"class":860},[273,9938,1725],{"class":820},[273,9940,1671],{"class":820},[273,9942,1466],{"class":829},[273,9944,9794],{"class":820},[273,9946,9947],{"class":275,"line":1591},[273,9948,556],{"emptyLinePlaceholder":555},[273,9950,9951],{"class":275,"line":1644},[273,9952,9953],{"class":293}," // UI state\n",[273,9955,9956,9958,9960,9962,9964,9966,9968],{"class":275,"line":1651},[273,9957,5148],{"class":829},[273,9959,6438],{"class":860},[273,9961,1725],{"class":820},[273,9963,9399],{"class":641},[273,9965,1054],{"class":1333},[273,9967,7624],{"class":1339},[273,9969,1064],{"class":1333},[273,9971,9972],{"class":275,"line":1656},[273,9973,999],{"class":820},[273,9975,9976],{"class":275,"line":1678},[273,9977,1004],{"class":820},[127,9979,9981,9982,9985],{"id":9980},"_3-use-readonly-for-exposed-state","3. Use ",[270,9983,9984],{},"readonly"," for Exposed State",[104,9987,9988],{},"When creating composables, expose state as readonly to prevent external mutations:",[263,9990,9992],{"className":1028,"code":9991,"language":1030,"meta":268,"style":268},"export function useUserStore() {\n const users = ref([])\n const loading = ref(false)\n\n const addUser = (user) => {\n users.value.push(user)\n }\n\n return {\n users: readonly(users),\n loading: readonly(loading),\n addUser,\n }\n}\n",[270,9993,9994,10007,10020,10036,10040,10060,10079,10083,10087,10093,10110,10127,10134,10138],{"__ignoreMap":268},[273,9995,9996,9998,10000,10003,10005],{"class":275,"line":276},[273,9997,3371],{"class":2084},[273,9999,9425],{"class":829},[273,10001,10002],{"class":641}," useUserStore",[273,10004,1629],{"class":820},[273,10006,839],{"class":820},[273,10008,10009,10011,10014,10016,10018],{"class":275,"line":283},[273,10010,2059],{"class":829},[273,10012,10013],{"class":860}," users",[273,10015,1725],{"class":820},[273,10017,9399],{"class":641},[273,10019,9709],{"class":1333},[273,10021,10022,10024,10026,10028,10030,10032,10034],{"class":275,"line":16},[273,10023,2059],{"class":829},[273,10025,6438],{"class":860},[273,10027,1725],{"class":820},[273,10029,9399],{"class":641},[273,10031,1054],{"class":1333},[273,10033,7624],{"class":1339},[273,10035,1064],{"class":1333},[273,10037,10038],{"class":275,"line":308},[273,10039,556],{"emptyLinePlaceholder":555},[273,10041,10042,10044,10047,10049,10051,10054,10056,10058],{"class":275,"line":321},[273,10043,2059],{"class":829},[273,10045,10046],{"class":860}," addUser",[273,10048,1725],{"class":820},[273,10050,1575],{"class":820},[273,10052,10053],{"class":1444},"user",[273,10055,1463],{"class":820},[273,10057,1466],{"class":829},[273,10059,839],{"class":820},[273,10061,10062,10065,10067,10069,10071,10073,10075,10077],{"class":275,"line":333},[273,10063,10064],{"class":860}," users",[273,10066,1185],{"class":820},[273,10068,9482],{"class":860},[273,10070,1185],{"class":820},[273,10072,2277],{"class":641},[273,10074,1054],{"class":1333},[273,10076,10053],{"class":860},[273,10078,1064],{"class":1333},[273,10080,10081],{"class":275,"line":345},[273,10082,999],{"class":820},[273,10084,10085],{"class":275,"line":357},[273,10086,556],{"emptyLinePlaceholder":555},[273,10088,10089,10091],{"class":275,"line":370},[273,10090,9583],{"class":2084},[273,10092,839],{"class":820},[273,10094,10095,10097,10099,10101,10103,10106,10108],{"class":275,"line":380},[273,10096,10064],{"class":1333},[273,10098,836],{"class":820},[273,10100,9594],{"class":641},[273,10102,1054],{"class":1333},[273,10104,10105],{"class":860},"users",[273,10107,1463],{"class":1333},[273,10109,879],{"class":820},[273,10111,10112,10115,10117,10119,10121,10123,10125],{"class":275,"line":390},[273,10113,10114],{"class":1333}," loading",[273,10116,836],{"class":820},[273,10118,9594],{"class":641},[273,10120,1054],{"class":1333},[273,10122,3533],{"class":860},[273,10124,1463],{"class":1333},[273,10126,879],{"class":820},[273,10128,10129,10132],{"class":275,"line":401},[273,10130,10131],{"class":860}," addUser",[273,10133,879],{"class":820},[273,10135,10136],{"class":275,"line":29},[273,10137,999],{"class":820},[273,10139,10140],{"class":275,"line":420},[273,10141,1004],{"class":820},[99,10143,10145],{"id":10144},"advanced-patterns","Advanced Patterns",[127,10147,10149],{"id":10148},"custom-ref-patterns","Custom Ref Patterns",[104,10151,10152],{},"Create custom refs for complex state management:",[263,10154,10156],{"className":1028,"code":10155,"language":1030,"meta":268,"style":268},"import { customRef } from \"vue\"\n\nfunction useDebouncedRef(value, delay = 200) {\n let timeout\n return customRef((track, trigger) => {\n return {\n get() {\n track()\n return value\n },\n set(newValue) {\n clearTimeout(timeout)\n timeout = setTimeout(() => {\n value = newValue\n trigger()\n }, delay)\n },\n }\n })\n}\n",[270,10157,10158,10177,10181,10207,10215,10239,10245,10254,10261,10269,10273,10287,10299,10317,10327,10334,10343,10347,10351,10357],{"__ignoreMap":268},[273,10159,10160,10162,10164,10167,10169,10171,10173,10175],{"class":275,"line":276},[273,10161,3312],{"class":2084},[273,10163,2062],{"class":820},[273,10165,10166],{"class":860}," customRef",[273,10168,1343],{"class":820},[273,10170,3355],{"class":2084},[273,10172,854],{"class":820},[273,10174,677],{"class":289},[273,10176,958],{"class":820},[273,10178,10179],{"class":275,"line":283},[273,10180,556],{"emptyLinePlaceholder":555},[273,10182,10183,10186,10189,10191,10193,10195,10198,10200,10203,10205],{"class":275,"line":16},[273,10184,10185],{"class":829},"function",[273,10187,10188],{"class":641}," useDebouncedRef",[273,10190,1054],{"class":820},[273,10192,9482],{"class":1444},[273,10194,1375],{"class":820},[273,10196,10197],{"class":1444}," delay",[273,10199,1725],{"class":820},[273,10201,10202],{"class":1199}," 200",[273,10204,1463],{"class":820},[273,10206,839],{"class":820},[273,10208,10209,10212],{"class":275,"line":308},[273,10210,10211],{"class":829}," let",[273,10213,10214],{"class":860}," timeout\n",[273,10216,10217,10219,10221,10223,10225,10228,10230,10233,10235,10237],{"class":275,"line":321},[273,10218,9583],{"class":2084},[273,10220,10166],{"class":641},[273,10222,1054],{"class":1333},[273,10224,1054],{"class":820},[273,10226,10227],{"class":1444},"track",[273,10229,1375],{"class":820},[273,10231,10232],{"class":1444}," trigger",[273,10234,1463],{"class":820},[273,10236,1466],{"class":829},[273,10238,839],{"class":820},[273,10240,10241,10243],{"class":275,"line":333},[273,10242,2121],{"class":2084},[273,10244,839],{"class":820},[273,10246,10247,10250,10252],{"class":275,"line":345},[273,10248,10249],{"class":1333}," get",[273,10251,1629],{"class":820},[273,10253,839],{"class":820},[273,10255,10256,10259],{"class":275,"line":357},[273,10257,10258],{"class":641}," track",[273,10260,1170],{"class":1333},[273,10262,10263,10266],{"class":275,"line":370},[273,10264,10265],{"class":2084}," return",[273,10267,10268],{"class":860}," value\n",[273,10270,10271],{"class":275,"line":380},[273,10272,5228],{"class":820},[273,10274,10275,10278,10280,10283,10285],{"class":275,"line":390},[273,10276,10277],{"class":1333}," set",[273,10279,1054],{"class":820},[273,10281,10282],{"class":1444},"newValue",[273,10284,1463],{"class":820},[273,10286,839],{"class":820},[273,10288,10289,10292,10294,10297],{"class":275,"line":401},[273,10290,10291],{"class":641}," clearTimeout",[273,10293,1054],{"class":1333},[273,10295,10296],{"class":860},"timeout",[273,10298,1064],{"class":1333},[273,10300,10301,10304,10306,10309,10311,10313,10315],{"class":275,"line":29},[273,10302,10303],{"class":860}," timeout",[273,10305,1725],{"class":820},[273,10307,10308],{"class":641}," setTimeout",[273,10310,1054],{"class":1333},[273,10312,1629],{"class":820},[273,10314,1466],{"class":829},[273,10316,839],{"class":820},[273,10318,10319,10322,10324],{"class":275,"line":420},[273,10320,10321],{"class":860}," value",[273,10323,1725],{"class":820},[273,10325,10326],{"class":860}," newValue\n",[273,10328,10329,10332],{"class":275,"line":430},[273,10330,10331],{"class":641}," trigger",[273,10333,1170],{"class":1333},[273,10335,10336,10339,10341],{"class":275,"line":440},[273,10337,10338],{"class":820}," },",[273,10340,10197],{"class":860},[273,10342,1064],{"class":1333},[273,10344,10345],{"class":275,"line":450},[273,10346,5228],{"class":820},[273,10348,10349],{"class":275,"line":460},[273,10350,3471],{"class":820},[273,10352,10353,10355],{"class":275,"line":471},[273,10354,2004],{"class":820},[273,10356,1064],{"class":1333},[273,10358,10359],{"class":275,"line":743},[273,10360,1004],{"class":820},[127,10362,10364],{"id":10363},"lifecycle-composition","Lifecycle Composition",[104,10366,10367],{},"Combine lifecycle hooks with reactive state:",[263,10369,10371],{"className":1028,"code":10370,"language":1030,"meta":268,"style":268},"export function useAsyncData(url) {\n const data = ref(null)\n const error = ref(null)\n const loading = ref(false)\n\n const fetchData = async () => {\n loading.value = true\n try {\n const response = await fetch(url)\n data.value = await response.json()\n } catch (err) {\n error.value = err\n } finally {\n loading.value = false\n }\n }\n\n onMounted(fetchData)\n\n return { data, error, loading, refetch: fetchData }\n}\n",[270,10372,10373,10390,10407,10423,10439,10443,10460,10472,10478,10496,10517,10531,10544,10552,10564,10568,10572,10576,10588,10592,10619],{"__ignoreMap":268},[273,10374,10375,10377,10379,10382,10384,10386,10388],{"class":275,"line":276},[273,10376,3371],{"class":2084},[273,10378,9425],{"class":829},[273,10380,10381],{"class":641}," useAsyncData",[273,10383,1054],{"class":820},[273,10385,5263],{"class":1444},[273,10387,1463],{"class":820},[273,10389,839],{"class":820},[273,10391,10392,10394,10397,10399,10401,10403,10405],{"class":275,"line":283},[273,10393,2059],{"class":829},[273,10395,10396],{"class":860}," data",[273,10398,1725],{"class":820},[273,10400,9399],{"class":641},[273,10402,1054],{"class":1333},[273,10404,9693],{"class":820},[273,10406,1064],{"class":1333},[273,10408,10409,10411,10413,10415,10417,10419,10421],{"class":275,"line":16},[273,10410,2059],{"class":829},[273,10412,1519],{"class":860},[273,10414,1725],{"class":820},[273,10416,9399],{"class":641},[273,10418,1054],{"class":1333},[273,10420,9693],{"class":820},[273,10422,1064],{"class":1333},[273,10424,10425,10427,10429,10431,10433,10435,10437],{"class":275,"line":308},[273,10426,2059],{"class":829},[273,10428,6438],{"class":860},[273,10430,1725],{"class":820},[273,10432,9399],{"class":641},[273,10434,1054],{"class":1333},[273,10436,7624],{"class":1339},[273,10438,1064],{"class":1333},[273,10440,10441],{"class":275,"line":321},[273,10442,556],{"emptyLinePlaceholder":555},[273,10444,10445,10447,10450,10452,10454,10456,10458],{"class":275,"line":333},[273,10446,2059],{"class":829},[273,10448,10449],{"class":860}," fetchData",[273,10451,1725],{"class":820},[273,10453,7251],{"class":829},[273,10455,1671],{"class":820},[273,10457,1466],{"class":829},[273,10459,839],{"class":820},[273,10461,10462,10464,10466,10468,10470],{"class":275,"line":345},[273,10463,10114],{"class":860},[273,10465,1185],{"class":820},[273,10467,9482],{"class":860},[273,10469,1725],{"class":820},[273,10471,3538],{"class":1339},[273,10473,10474,10476],{"class":275,"line":357},[273,10475,6808],{"class":2084},[273,10477,839],{"class":820},[273,10479,10480,10482,10484,10486,10488,10490,10492,10494],{"class":275,"line":370},[273,10481,6815],{"class":829},[273,10483,3553],{"class":860},[273,10485,1725],{"class":820},[273,10487,3558],{"class":2084},[273,10489,5258],{"class":641},[273,10491,1054],{"class":1333},[273,10493,5263],{"class":860},[273,10495,1064],{"class":1333},[273,10497,10498,10501,10503,10505,10507,10509,10511,10513,10515],{"class":275,"line":380},[273,10499,10500],{"class":860}," data",[273,10502,1185],{"class":820},[273,10504,9482],{"class":860},[273,10506,1725],{"class":820},[273,10508,3558],{"class":2084},[273,10510,3553],{"class":860},[273,10512,1185],{"class":820},[273,10514,813],{"class":641},[273,10516,1170],{"class":1333},[273,10518,10519,10521,10523,10525,10527,10529],{"class":275,"line":390},[273,10520,2174],{"class":820},[273,10522,3591],{"class":2084},[273,10524,1575],{"class":1333},[273,10526,1445],{"class":860},[273,10528,2114],{"class":1333},[273,10530,821],{"class":820},[273,10532,10533,10535,10537,10539,10541],{"class":275,"line":401},[273,10534,2158],{"class":860},[273,10536,1185],{"class":820},[273,10538,9482],{"class":860},[273,10540,1725],{"class":820},[273,10542,10543],{"class":860}," err\n",[273,10545,10546,10548,10550],{"class":275,"line":29},[273,10547,2174],{"class":820},[273,10549,3645],{"class":2084},[273,10551,839],{"class":820},[273,10553,10554,10556,10558,10560,10562],{"class":275,"line":420},[273,10555,3451],{"class":860},[273,10557,1185],{"class":820},[273,10559,9482],{"class":860},[273,10561,1725],{"class":820},[273,10563,3658],{"class":1339},[273,10565,10566],{"class":275,"line":430},[273,10567,3471],{"class":820},[273,10569,10570],{"class":275,"line":440},[273,10571,999],{"class":820},[273,10573,10574],{"class":275,"line":450},[273,10575,556],{"emptyLinePlaceholder":555},[273,10577,10578,10581,10583,10586],{"class":275,"line":460},[273,10579,10580],{"class":641}," onMounted",[273,10582,1054],{"class":1333},[273,10584,10585],{"class":860},"fetchData",[273,10587,1064],{"class":1333},[273,10589,10590],{"class":275,"line":471},[273,10591,556],{"emptyLinePlaceholder":555},[273,10593,10594,10596,10598,10600,10602,10604,10606,10608,10610,10613,10615,10617],{"class":275,"line":743},[273,10595,9583],{"class":2084},[273,10597,2062],{"class":820},[273,10599,10396],{"class":860},[273,10601,1375],{"class":820},[273,10603,1519],{"class":860},[273,10605,1375],{"class":820},[273,10607,6438],{"class":860},[273,10609,1375],{"class":820},[273,10611,10612],{"class":1333}," refetch",[273,10614,836],{"class":820},[273,10616,10449],{"class":860},[273,10618,7461],{"class":820},[273,10620,10621],{"class":275,"line":749},[273,10622,1004],{"class":820},[99,10624,10626],{"id":10625},"performance-considerations","Performance Considerations",[127,10628,10630,10631,10634],{"id":10629},"_1-use-shallowref-for-large-objects","1. Use ",[270,10632,10633],{},"shallowRef"," for Large Objects",[104,10636,10637],{},"For large objects that don't need deep reactivity:",[263,10639,10641],{"className":1028,"code":10640,"language":1030,"meta":268,"style":268},"import { shallowRef } from \"vue\"\n\nconst largeDataSet = shallowRef({\n // Large object with many properties\n})\n",[270,10642,10643,10662,10666,10681,10686],{"__ignoreMap":268},[273,10644,10645,10647,10649,10652,10654,10656,10658,10660],{"class":275,"line":276},[273,10646,3312],{"class":2084},[273,10648,2062],{"class":820},[273,10650,10651],{"class":860}," shallowRef",[273,10653,1343],{"class":820},[273,10655,3355],{"class":2084},[273,10657,854],{"class":820},[273,10659,677],{"class":289},[273,10661,958],{"class":820},[273,10663,10664],{"class":275,"line":283},[273,10665,556],{"emptyLinePlaceholder":555},[273,10667,10668,10670,10673,10675,10677,10679],{"class":275,"line":16},[273,10669,1042],{"class":829},[273,10671,10672],{"class":860}," largeDataSet ",[273,10674,1048],{"class":820},[273,10676,10651],{"class":641},[273,10678,1054],{"class":860},[273,10680,821],{"class":820},[273,10682,10683],{"class":275,"line":308},[273,10684,10685],{"class":293}," // Large object with many properties\n",[273,10687,10688,10690],{"class":275,"line":321},[273,10689,1538],{"class":820},[273,10691,1064],{"class":860},[127,10693,10695,10696],{"id":10694},"_2-optimize-with-markraw","2. Optimize with ",[270,10697,10698],{},"markRaw",[104,10700,10701],{},"For objects that should never be reactive:",[263,10703,10705],{"className":1028,"code":10704,"language":1030,"meta":268,"style":268},"import { markRaw } from \"vue\"\n\nconst thirdPartyLibrary = markRaw(new SomeLibrary())\n",[270,10706,10707,10726,10730],{"__ignoreMap":268},[273,10708,10709,10711,10713,10716,10718,10720,10722,10724],{"class":275,"line":276},[273,10710,3312],{"class":2084},[273,10712,2062],{"class":820},[273,10714,10715],{"class":860}," markRaw",[273,10717,1343],{"class":820},[273,10719,3355],{"class":2084},[273,10721,854],{"class":820},[273,10723,677],{"class":289},[273,10725,958],{"class":820},[273,10727,10728],{"class":275,"line":283},[273,10729,556],{"emptyLinePlaceholder":555},[273,10731,10732,10734,10737,10739,10741,10743,10746,10749],{"class":275,"line":16},[273,10733,1042],{"class":829},[273,10735,10736],{"class":860}," thirdPartyLibrary ",[273,10738,1048],{"class":820},[273,10740,10715],{"class":641},[273,10742,1054],{"class":860},[273,10744,10745],{"class":820},"new",[273,10747,10748],{"class":641}," SomeLibrary",[273,10750,10751],{"class":860},"())\n",[99,10753,10755],{"id":10754},"testing-composables","Testing Composables",[104,10757,10758],{},"Composables are easy to test in isolation:",[263,10760,10762],{"className":1028,"code":10761,"language":1030,"meta":268,"style":268},"import { describe, it, expect } from \"vitest\"\nimport { useCounter } from \"@/composables/useCounter\"\n\ndescribe(\"useCounter\", () => {\n it(\"should increment count\", () => {\n const { count, increment } = useCounter(0)\n\n expect(count.value).toBe(0)\n increment()\n expect(count.value).toBe(1)\n })\n})\n",[270,10763,10764,10794,10813,10817,10838,10859,10883,10887,10911,10917,10941,10947],{"__ignoreMap":268},[273,10765,10766,10768,10770,10773,10775,10778,10780,10783,10785,10787,10789,10792],{"class":275,"line":276},[273,10767,3312],{"class":2084},[273,10769,2062],{"class":820},[273,10771,10772],{"class":860}," describe",[273,10774,1375],{"class":820},[273,10776,10777],{"class":860}," it",[273,10779,1375],{"class":820},[273,10781,10782],{"class":860}," expect",[273,10784,1343],{"class":820},[273,10786,3355],{"class":2084},[273,10788,854],{"class":820},[273,10790,10791],{"class":289},"vitest",[273,10793,958],{"class":820},[273,10795,10796,10798,10800,10802,10804,10806,10808,10811],{"class":275,"line":283},[273,10797,3312],{"class":2084},[273,10799,2062],{"class":820},[273,10801,9428],{"class":860},[273,10803,1343],{"class":820},[273,10805,3355],{"class":2084},[273,10807,854],{"class":820},[273,10809,10810],{"class":289},"@/composables/useCounter",[273,10812,958],{"class":820},[273,10814,10815],{"class":275,"line":16},[273,10816,556],{"emptyLinePlaceholder":555},[273,10818,10819,10821,10823,10825,10828,10830,10832,10834,10836],{"class":275,"line":308},[273,10820,7193],{"class":641},[273,10822,1054],{"class":860},[273,10824,833],{"class":820},[273,10826,10827],{"class":289},"useCounter",[273,10829,833],{"class":820},[273,10831,1375],{"class":820},[273,10833,1671],{"class":820},[273,10835,1466],{"class":829},[273,10837,839],{"class":820},[273,10839,10840,10842,10844,10846,10849,10851,10853,10855,10857],{"class":275,"line":321},[273,10841,7854],{"class":641},[273,10843,1054],{"class":1333},[273,10845,833],{"class":820},[273,10847,10848],{"class":289},"should increment count",[273,10850,833],{"class":820},[273,10852,1375],{"class":820},[273,10854,1671],{"class":820},[273,10856,1466],{"class":829},[273,10858,839],{"class":820},[273,10860,10861,10863,10865,10867,10869,10871,10873,10875,10877,10879,10881],{"class":275,"line":333},[273,10862,5148],{"class":829},[273,10864,2062],{"class":820},[273,10866,9448],{"class":860},[273,10868,1375],{"class":820},[273,10870,9469],{"class":860},[273,10872,1343],{"class":820},[273,10874,1725],{"class":820},[273,10876,9428],{"class":641},[273,10878,1054],{"class":1333},[273,10880,2981],{"class":1199},[273,10882,1064],{"class":1333},[273,10884,10885],{"class":275,"line":345},[273,10886,556],{"emptyLinePlaceholder":555},[273,10888,10889,10891,10893,10895,10897,10899,10901,10903,10905,10907,10909],{"class":275,"line":357},[273,10890,8000],{"class":641},[273,10892,1054],{"class":1333},[273,10894,9525],{"class":860},[273,10896,1185],{"class":820},[273,10898,9482],{"class":860},[273,10900,1463],{"class":1333},[273,10902,1185],{"class":820},[273,10904,7331],{"class":641},[273,10906,1054],{"class":1333},[273,10908,2981],{"class":1199},[273,10910,1064],{"class":1333},[273,10912,10913,10915],{"class":275,"line":370},[273,10914,9607],{"class":641},[273,10916,1170],{"class":1333},[273,10918,10919,10921,10923,10925,10927,10929,10931,10933,10935,10937,10939],{"class":275,"line":380},[273,10920,8000],{"class":641},[273,10922,1054],{"class":1333},[273,10924,9525],{"class":860},[273,10926,1185],{"class":820},[273,10928,9482],{"class":860},[273,10930,1463],{"class":1333},[273,10932,1185],{"class":820},[273,10934,7331],{"class":641},[273,10936,1054],{"class":1333},[273,10938,2517],{"class":1199},[273,10940,1064],{"class":1333},[273,10942,10943,10945],{"class":275,"line":390},[273,10944,2004],{"class":820},[273,10946,1064],{"class":1333},[273,10948,10949,10951],{"class":275,"line":401},[273,10950,1538],{"class":820},[273,10952,1064],{"class":860},[99,10954,9248],{"id":9247},[104,10956,10957],{},"The Composition API is a powerful tool that, when used correctly, can significantly improve your Vue.js applications. Focus on creating reusable composables, organizing code by feature, and leveraging TypeScript for better development experience.",[104,10959,10960],{},"The key is to think in terms of logical concerns rather than component options, leading to more maintainable and testable code.",[4136,10962,10963],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":268,"searchDepth":283,"depth":283,"links":10965},[10966,10967,10968,10974,10978,10984,10985],{"id":9342,"depth":283,"text":9337},{"id":9348,"depth":283,"text":9349},{"id":9375,"depth":283,"text":9376,"children":10969},[10970,10971,10972],{"id":9379,"depth":16,"text":9380},{"id":9641,"depth":16,"text":9642},{"id":9980,"depth":16,"text":10973},"3. Use readonly for Exposed State",{"id":10144,"depth":283,"text":10145,"children":10975},[10976,10977],{"id":10148,"depth":16,"text":10149},{"id":10363,"depth":16,"text":10364},{"id":10625,"depth":283,"text":10626,"children":10979},[10980,10982],{"id":10629,"depth":16,"text":10981},"1. Use shallowRef for Large Objects",{"id":10694,"depth":16,"text":10983},"2. Optimize with markRaw",{"id":10754,"depth":283,"text":10755},{"id":9247,"depth":283,"text":9248},"2024-12-12","Discover the most effective patterns and practices for using Vue 3's Composition API in production applications.","/img/blog/5-vue-3-composition-api-best-practices/banner.svg",{"readingTime":9305},"/blog/5-vue-3-composition-api-best-practices",{"title":9337,"description":10987},"Vue.js Mastery","Master Vue 3 from composition API to advanced patterns","blog/5-vue-3-composition-api-best-practices",[117,10996,10997,10998],"JavaScript","Frontend","Composition API","If36_A4yv7222uv3YFaQhROJ18zIrDPica8zaU43CdA",{"id":11001,"title":11002,"abstract":8799,"author":8815,"authorUrl":8799,"body":11003,"date":15118,"dateUpdated":15118,"description":15119,"excerpt":8799,"extension":8800,"featured":555,"headline":8799,"image":15120,"meta":15121,"navigation":555,"ogImage":8799,"path":15063,"seo":15123,"series":15044,"seriesDescription":15124,"seriesOrder":283,"socialImage":8799,"stem":15125,"tags":15126,"__hash__":15130},"blog/blog/6-typescript-nodejs-production-guide.md","TypeScript + Node.js in Production: A Complete Guide",{"type":96,"value":11004,"toc":15083},[11005,11008,11011,11015,11019,11238,11242,11611,11615,11619,11787,11789,11924,11928,11932,12248,12252,12609,12613,12617,12778,12782,13165,13169,13173,13354,13358,13870,13874,13878,13985,13989,14129,14133,14137,14513,14517,14521,15005,15007,15010,15013,15030,15033,15035,15039,15046,15080],[99,11006,11002],{"id":11007},"typescript-nodejs-in-production-a-complete-guide",[104,11009,11010],{},"Running TypeScript in production requires careful consideration of build processes, performance, and maintainability. After deploying dozens of TypeScript Node.js applications, here's everything you need to know.",[99,11012,11014],{"id":11013},"project-setup-and-configuration","Project Setup and Configuration",[127,11016,11018],{"id":11017},"essential-dependencies","Essential Dependencies",[263,11020,11022],{"className":811,"code":11021,"language":813,"meta":268,"style":268},"{\n \"devDependencies\": {\n \"@types/node\": \"^20.0.0\",\n \"typescript\": \"^5.0.0\",\n \"ts-node\": \"^10.9.0\",\n \"nodemon\": \"^3.0.0\",\n \"@typescript-eslint/eslint-plugin\": \"^6.0.0\",\n \"@typescript-eslint/parser\": \"^6.0.0\"\n },\n \"dependencies\": {\n \"dotenv\": \"^16.0.0\",\n \"express\": \"^4.18.0\",\n \"@types/express\": \"^4.17.0\"\n }\n}\n",[270,11023,11024,11028,11040,11060,11079,11099,11119,11139,11156,11160,11173,11193,11212,11230,11234],{"__ignoreMap":268},[273,11025,11026],{"class":275,"line":276},[273,11027,821],{"class":820},[273,11029,11030,11032,11034,11036,11038],{"class":275,"line":283},[273,11031,826],{"class":820},[273,11033,970],{"class":829},[273,11035,833],{"class":820},[273,11037,836],{"class":820},[273,11039,839],{"class":820},[273,11041,11042,11044,11047,11049,11051,11053,11056,11058],{"class":275,"line":16},[273,11043,844],{"class":820},[273,11045,11046],{"class":279},"@types/node",[273,11048,833],{"class":820},[273,11050,836],{"class":820},[273,11052,854],{"class":820},[273,11054,11055],{"class":289},"^20.0.0",[273,11057,833],{"class":820},[273,11059,879],{"class":820},[273,11061,11062,11064,11066,11068,11070,11072,11075,11077],{"class":275,"line":308},[273,11063,844],{"class":820},[273,11065,8889],{"class":279},[273,11067,833],{"class":820},[273,11069,836],{"class":820},[273,11071,854],{"class":820},[273,11073,11074],{"class":289},"^5.0.0",[273,11076,833],{"class":820},[273,11078,879],{"class":820},[273,11080,11081,11083,11086,11088,11090,11092,11095,11097],{"class":275,"line":321},[273,11082,844],{"class":820},[273,11084,11085],{"class":279},"ts-node",[273,11087,833],{"class":820},[273,11089,836],{"class":820},[273,11091,854],{"class":820},[273,11093,11094],{"class":289},"^10.9.0",[273,11096,833],{"class":820},[273,11098,879],{"class":820},[273,11100,11101,11103,11106,11108,11110,11112,11115,11117],{"class":275,"line":333},[273,11102,844],{"class":820},[273,11104,11105],{"class":279},"nodemon",[273,11107,833],{"class":820},[273,11109,836],{"class":820},[273,11111,854],{"class":820},[273,11113,11114],{"class":289},"^3.0.0",[273,11116,833],{"class":820},[273,11118,879],{"class":820},[273,11120,11121,11123,11126,11128,11130,11132,11135,11137],{"class":275,"line":345},[273,11122,844],{"class":820},[273,11124,11125],{"class":279},"@typescript-eslint/eslint-plugin",[273,11127,833],{"class":820},[273,11129,836],{"class":820},[273,11131,854],{"class":820},[273,11133,11134],{"class":289},"^6.0.0",[273,11136,833],{"class":820},[273,11138,879],{"class":820},[273,11140,11141,11143,11146,11148,11150,11152,11154],{"class":275,"line":357},[273,11142,844],{"class":820},[273,11144,11145],{"class":279},"@typescript-eslint/parser",[273,11147,833],{"class":820},[273,11149,836],{"class":820},[273,11151,854],{"class":820},[273,11153,11134],{"class":289},[273,11155,958],{"class":820},[273,11157,11158],{"class":275,"line":370},[273,11159,963],{"class":820},[273,11161,11162,11164,11167,11169,11171],{"class":275,"line":380},[273,11163,826],{"class":820},[273,11165,11166],{"class":829},"dependencies",[273,11168,833],{"class":820},[273,11170,836],{"class":820},[273,11172,839],{"class":820},[273,11174,11175,11177,11180,11182,11184,11186,11189,11191],{"class":275,"line":390},[273,11176,844],{"class":820},[273,11178,11179],{"class":279},"dotenv",[273,11181,833],{"class":820},[273,11183,836],{"class":820},[273,11185,854],{"class":820},[273,11187,11188],{"class":289},"^16.0.0",[273,11190,833],{"class":820},[273,11192,879],{"class":820},[273,11194,11195,11197,11199,11201,11203,11205,11208,11210],{"class":275,"line":401},[273,11196,844],{"class":820},[273,11198,1059],{"class":279},[273,11200,833],{"class":820},[273,11202,836],{"class":820},[273,11204,854],{"class":820},[273,11206,11207],{"class":289},"^4.18.0",[273,11209,833],{"class":820},[273,11211,879],{"class":820},[273,11213,11214,11216,11219,11221,11223,11225,11228],{"class":275,"line":29},[273,11215,844],{"class":820},[273,11217,11218],{"class":279},"@types/express",[273,11220,833],{"class":820},[273,11222,836],{"class":820},[273,11224,854],{"class":820},[273,11226,11227],{"class":289},"^4.17.0",[273,11229,958],{"class":820},[273,11231,11232],{"class":275,"line":420},[273,11233,999],{"class":820},[273,11235,11236],{"class":275,"line":430},[273,11237,1004],{"class":820},[127,11239,11241],{"id":11240},"typescript-configuration","TypeScript Configuration",[263,11243,11245],{"className":811,"code":11244,"language":813,"meta":268,"style":268},"// tsconfig.json\n{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"commonjs\",\n \"lib\": [\"ES2022\"],\n \"outDir\": \"./dist\",\n \"rootDir\": \"./src\",\n \"strict\": true,\n \"esModuleInterop\": true,\n \"skipLibCheck\": true,\n \"forceConsistentCasingInFileNames\": true,\n \"resolveJsonModule\": true,\n \"declaration\": true,\n \"declarationMap\": true,\n \"sourceMap\": true,\n \"removeComments\": true,\n \"noImplicitReturns\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUncheckedIndexedAccess\": true,\n \"exactOptionalPropertyTypes\": true\n },\n \"include\": [\"src/**/*\"],\n \"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\"]\n}\n",[270,11246,11247,11252,11256,11269,11289,11309,11331,11351,11371,11385,11398,11411,11424,11437,11450,11463,11476,11489,11502,11515,11528,11541,11545,11567,11607],{"__ignoreMap":268},[273,11248,11249],{"class":275,"line":276},[273,11250,11251],{"class":293},"// tsconfig.json\n",[273,11253,11254],{"class":275,"line":283},[273,11255,821],{"class":820},[273,11257,11258,11260,11263,11265,11267],{"class":275,"line":16},[273,11259,826],{"class":820},[273,11261,11262],{"class":829},"compilerOptions",[273,11264,833],{"class":820},[273,11266,836],{"class":820},[273,11268,839],{"class":820},[273,11270,11271,11273,11276,11278,11280,11282,11285,11287],{"class":275,"line":308},[273,11272,844],{"class":820},[273,11274,11275],{"class":279},"target",[273,11277,833],{"class":820},[273,11279,836],{"class":820},[273,11281,854],{"class":820},[273,11283,11284],{"class":289},"ES2022",[273,11286,833],{"class":820},[273,11288,879],{"class":820},[273,11290,11291,11293,11296,11298,11300,11302,11305,11307],{"class":275,"line":321},[273,11292,844],{"class":820},[273,11294,11295],{"class":279},"module",[273,11297,833],{"class":820},[273,11299,836],{"class":820},[273,11301,854],{"class":820},[273,11303,11304],{"class":289},"commonjs",[273,11306,833],{"class":820},[273,11308,879],{"class":820},[273,11310,11311,11313,11316,11318,11320,11322,11324,11326,11328],{"class":275,"line":333},[273,11312,844],{"class":820},[273,11314,11315],{"class":279},"lib",[273,11317,833],{"class":820},[273,11319,836],{"class":820},[273,11321,8605],{"class":820},[273,11323,833],{"class":820},[273,11325,11284],{"class":289},[273,11327,833],{"class":820},[273,11329,11330],{"class":820},"],\n",[273,11332,11333,11335,11338,11340,11342,11344,11347,11349],{"class":275,"line":345},[273,11334,844],{"class":820},[273,11336,11337],{"class":279},"outDir",[273,11339,833],{"class":820},[273,11341,836],{"class":820},[273,11343,854],{"class":820},[273,11345,11346],{"class":289},"./dist",[273,11348,833],{"class":820},[273,11350,879],{"class":820},[273,11352,11353,11355,11358,11360,11362,11364,11367,11369],{"class":275,"line":357},[273,11354,844],{"class":820},[273,11356,11357],{"class":279},"rootDir",[273,11359,833],{"class":820},[273,11361,836],{"class":820},[273,11363,854],{"class":820},[273,11365,11366],{"class":289},"./src",[273,11368,833],{"class":820},[273,11370,879],{"class":820},[273,11372,11373,11375,11378,11380,11382],{"class":275,"line":370},[273,11374,844],{"class":820},[273,11376,11377],{"class":279},"strict",[273,11379,833],{"class":820},[273,11381,836],{"class":820},[273,11383,11384],{"class":820}," true,\n",[273,11386,11387,11389,11392,11394,11396],{"class":275,"line":380},[273,11388,844],{"class":820},[273,11390,11391],{"class":279},"esModuleInterop",[273,11393,833],{"class":820},[273,11395,836],{"class":820},[273,11397,11384],{"class":820},[273,11399,11400,11402,11405,11407,11409],{"class":275,"line":390},[273,11401,844],{"class":820},[273,11403,11404],{"class":279},"skipLibCheck",[273,11406,833],{"class":820},[273,11408,836],{"class":820},[273,11410,11384],{"class":820},[273,11412,11413,11415,11418,11420,11422],{"class":275,"line":401},[273,11414,844],{"class":820},[273,11416,11417],{"class":279},"forceConsistentCasingInFileNames",[273,11419,833],{"class":820},[273,11421,836],{"class":820},[273,11423,11384],{"class":820},[273,11425,11426,11428,11431,11433,11435],{"class":275,"line":29},[273,11427,844],{"class":820},[273,11429,11430],{"class":279},"resolveJsonModule",[273,11432,833],{"class":820},[273,11434,836],{"class":820},[273,11436,11384],{"class":820},[273,11438,11439,11441,11444,11446,11448],{"class":275,"line":420},[273,11440,844],{"class":820},[273,11442,11443],{"class":279},"declaration",[273,11445,833],{"class":820},[273,11447,836],{"class":820},[273,11449,11384],{"class":820},[273,11451,11452,11454,11457,11459,11461],{"class":275,"line":430},[273,11453,844],{"class":820},[273,11455,11456],{"class":279},"declarationMap",[273,11458,833],{"class":820},[273,11460,836],{"class":820},[273,11462,11384],{"class":820},[273,11464,11465,11467,11470,11472,11474],{"class":275,"line":440},[273,11466,844],{"class":820},[273,11468,11469],{"class":279},"sourceMap",[273,11471,833],{"class":820},[273,11473,836],{"class":820},[273,11475,11384],{"class":820},[273,11477,11478,11480,11483,11485,11487],{"class":275,"line":450},[273,11479,844],{"class":820},[273,11481,11482],{"class":279},"removeComments",[273,11484,833],{"class":820},[273,11486,836],{"class":820},[273,11488,11384],{"class":820},[273,11490,11491,11493,11496,11498,11500],{"class":275,"line":460},[273,11492,844],{"class":820},[273,11494,11495],{"class":279},"noImplicitReturns",[273,11497,833],{"class":820},[273,11499,836],{"class":820},[273,11501,11384],{"class":820},[273,11503,11504,11506,11509,11511,11513],{"class":275,"line":471},[273,11505,844],{"class":820},[273,11507,11508],{"class":279},"noFallthroughCasesInSwitch",[273,11510,833],{"class":820},[273,11512,836],{"class":820},[273,11514,11384],{"class":820},[273,11516,11517,11519,11522,11524,11526],{"class":275,"line":743},[273,11518,844],{"class":820},[273,11520,11521],{"class":279},"noUncheckedIndexedAccess",[273,11523,833],{"class":820},[273,11525,836],{"class":820},[273,11527,11384],{"class":820},[273,11529,11530,11532,11535,11537,11539],{"class":275,"line":749},[273,11531,844],{"class":820},[273,11533,11534],{"class":279},"exactOptionalPropertyTypes",[273,11536,833],{"class":820},[273,11538,836],{"class":820},[273,11540,3538],{"class":820},[273,11542,11543],{"class":275,"line":771},[273,11544,963],{"class":820},[273,11546,11547,11549,11552,11554,11556,11558,11560,11563,11565],{"class":275,"line":784},[273,11548,826],{"class":820},[273,11550,11551],{"class":829},"include",[273,11553,833],{"class":820},[273,11555,836],{"class":820},[273,11557,8605],{"class":820},[273,11559,833],{"class":820},[273,11561,11562],{"class":289},"src/**/*",[273,11564,833],{"class":820},[273,11566,11330],{"class":820},[273,11568,11569,11571,11574,11576,11578,11580,11582,11585,11587,11589,11591,11594,11596,11598,11600,11603,11605],{"class":275,"line":789},[273,11570,826],{"class":820},[273,11572,11573],{"class":829},"exclude",[273,11575,833],{"class":820},[273,11577,836],{"class":820},[273,11579,8605],{"class":820},[273,11581,833],{"class":820},[273,11583,11584],{"class":289},"node_modules",[273,11586,833],{"class":820},[273,11588,1375],{"class":820},[273,11590,854],{"class":820},[273,11592,11593],{"class":289},"dist",[273,11595,833],{"class":820},[273,11597,1375],{"class":820},[273,11599,854],{"class":820},[273,11601,11602],{"class":289},"**/*.test.ts",[273,11604,833],{"class":820},[273,11606,1906],{"class":820},[273,11608,11609],{"class":275,"line":1471},[273,11610,1004],{"class":820},[99,11612,11614],{"id":11613},"build-and-deployment-strategies","Build and Deployment Strategies",[127,11616,11618],{"id":11617},"development-vs-production-builds","Development vs Production Builds",[263,11620,11622],{"className":811,"code":11621,"language":813,"meta":268,"style":268},"// package.json scripts\n{\n \"scripts\": {\n \"dev\": \"nodemon --exec ts-node src/index.ts\",\n \"build\": \"tsc\",\n \"start\": \"node dist/index.js\",\n \"start:prod\": \"NODE_ENV=production node dist/index.js\",\n \"type-check\": \"tsc --noEmit\",\n \"lint\": \"eslint src/**/*.ts\",\n \"test\": \"jest\"\n }\n}\n",[270,11623,11624,11629,11633,11645,11664,11683,11702,11721,11741,11761,11779,11783],{"__ignoreMap":268},[273,11625,11626],{"class":275,"line":276},[273,11627,11628],{"class":293},"// package.json scripts\n",[273,11630,11631],{"class":275,"line":283},[273,11632,821],{"class":820},[273,11634,11635,11637,11639,11641,11643],{"class":275,"line":16},[273,11636,826],{"class":820},[273,11638,830],{"class":829},[273,11640,833],{"class":820},[273,11642,836],{"class":820},[273,11644,839],{"class":820},[273,11646,11647,11649,11651,11653,11655,11657,11660,11662],{"class":275,"line":308},[273,11648,844],{"class":820},[273,11650,847],{"class":279},[273,11652,833],{"class":820},[273,11654,836],{"class":820},[273,11656,854],{"class":820},[273,11658,11659],{"class":289},"nodemon --exec ts-node src/index.ts",[273,11661,833],{"class":820},[273,11663,879],{"class":820},[273,11665,11666,11668,11670,11672,11674,11676,11679,11681],{"class":275,"line":321},[273,11667,844],{"class":820},[273,11669,926],{"class":279},[273,11671,833],{"class":820},[273,11673,836],{"class":820},[273,11675,854],{"class":820},[273,11677,11678],{"class":289},"tsc",[273,11680,833],{"class":820},[273,11682,879],{"class":820},[273,11684,11685,11687,11689,11691,11693,11695,11698,11700],{"class":275,"line":333},[273,11686,844],{"class":820},[273,11688,946],{"class":279},[273,11690,833],{"class":820},[273,11692,836],{"class":820},[273,11694,854],{"class":820},[273,11696,11697],{"class":289},"node dist/index.js",[273,11699,833],{"class":820},[273,11701,879],{"class":820},[273,11703,11704,11706,11708,11710,11712,11714,11717,11719],{"class":275,"line":345},[273,11705,844],{"class":820},[273,11707,8413],{"class":279},[273,11709,833],{"class":820},[273,11711,836],{"class":820},[273,11713,854],{"class":820},[273,11715,11716],{"class":289},"NODE_ENV=production node dist/index.js",[273,11718,833],{"class":820},[273,11720,879],{"class":820},[273,11722,11723,11725,11728,11730,11732,11734,11737,11739],{"class":275,"line":357},[273,11724,844],{"class":820},[273,11726,11727],{"class":279},"type-check",[273,11729,833],{"class":820},[273,11731,836],{"class":820},[273,11733,854],{"class":820},[273,11735,11736],{"class":289},"tsc --noEmit",[273,11738,833],{"class":820},[273,11740,879],{"class":820},[273,11742,11743,11745,11748,11750,11752,11754,11757,11759],{"class":275,"line":370},[273,11744,844],{"class":820},[273,11746,11747],{"class":279},"lint",[273,11749,833],{"class":820},[273,11751,836],{"class":820},[273,11753,854],{"class":820},[273,11755,11756],{"class":289},"eslint src/**/*.ts",[273,11758,833],{"class":820},[273,11760,879],{"class":820},[273,11762,11763,11765,11768,11770,11772,11774,11777],{"class":275,"line":380},[273,11764,844],{"class":820},[273,11766,11767],{"class":279},"test",[273,11769,833],{"class":820},[273,11771,836],{"class":820},[273,11773,854],{"class":820},[273,11775,11776],{"class":289},"jest",[273,11778,958],{"class":820},[273,11780,11781],{"class":275,"line":390},[273,11782,999],{"class":820},[273,11784,11785],{"class":275,"line":401},[273,11786,1004],{"class":820},[127,11788,8456],{"id":8455},[263,11790,11792],{"className":8459,"code":11791,"language":8461,"meta":268,"style":268},"# Multi-stage build for production\nFROM node:18-alpine AS builder\n\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --only=production\n\nCOPY . .\nRUN npm run build\n\nFROM node:18-alpine AS production\n\nWORKDIR /app\nCOPY --from=builder /app/dist ./dist\nCOPY --from=builder /app/node_modules ./node_modules\nCOPY --from=builder /app/package*.json ./\n\nEXPOSE 3000\nCMD [\"npm\", \"run\", \"start:prod\"]\n",[270,11793,11794,11799,11812,11816,11822,11828,11834,11838,11844,11850,11854,11865,11869,11875,11882,11889,11896,11900,11906],{"__ignoreMap":268},[273,11795,11796],{"class":275,"line":276},[273,11797,11798],{"class":293},"# Multi-stage build for production\n",[273,11800,11801,11803,11806,11809],{"class":275,"line":283},[273,11802,8473],{"class":1199},[273,11804,11805],{"class":860}," node:18-alpine ",[273,11807,11808],{"class":1199},"AS",[273,11810,11811],{"class":860}," builder\n",[273,11813,11814],{"class":275,"line":16},[273,11815,556],{"emptyLinePlaceholder":555},[273,11817,11818,11820],{"class":275,"line":308},[273,11819,8485],{"class":1199},[273,11821,8488],{"class":860},[273,11823,11824,11826],{"class":275,"line":321},[273,11825,8502],{"class":1199},[273,11827,8505],{"class":860},[273,11829,11830,11832],{"class":275,"line":333},[273,11831,8533],{"class":1199},[273,11833,8536],{"class":860},[273,11835,11836],{"class":275,"line":345},[273,11837,556],{"emptyLinePlaceholder":555},[273,11839,11840,11842],{"class":275,"line":357},[273,11841,8502],{"class":1199},[273,11843,8566],{"class":860},[273,11845,11846,11848],{"class":275,"line":370},[273,11847,8533],{"class":1199},[273,11849,8582],{"class":860},[273,11851,11852],{"class":275,"line":380},[273,11853,556],{"emptyLinePlaceholder":555},[273,11855,11856,11858,11860,11862],{"class":275,"line":390},[273,11857,8473],{"class":1199},[273,11859,11805],{"class":860},[273,11861,11808],{"class":1199},[273,11863,11864],{"class":860}," production\n",[273,11866,11867],{"class":275,"line":401},[273,11868,556],{"emptyLinePlaceholder":555},[273,11870,11871,11873],{"class":275,"line":29},[273,11872,8485],{"class":1199},[273,11874,8488],{"class":860},[273,11876,11877,11879],{"class":275,"line":420},[273,11878,8502],{"class":1199},[273,11880,11881],{"class":860}," --from=builder /app/dist ./dist\n",[273,11883,11884,11886],{"class":275,"line":430},[273,11885,8502],{"class":1199},[273,11887,11888],{"class":860}," --from=builder /app/node_modules ./node_modules\n",[273,11890,11891,11893],{"class":275,"line":440},[273,11892,8502],{"class":1199},[273,11894,11895],{"class":860}," --from=builder /app/package*.json ./\n",[273,11897,11898],{"class":275,"line":450},[273,11899,556],{"emptyLinePlaceholder":555},[273,11901,11902,11904],{"class":275,"line":460},[273,11903,8591],{"class":1199},[273,11905,1200],{"class":860},[273,11907,11908,11910,11912,11914,11916,11918,11920,11922],{"class":275,"line":471},[273,11909,8602],{"class":1199},[273,11911,8605],{"class":860},[273,11913,8608],{"class":289},[273,11915,8611],{"class":860},[273,11917,8614],{"class":289},[273,11919,8611],{"class":860},[273,11921,8619],{"class":289},[273,11923,1906],{"class":860},[99,11925,11927],{"id":11926},"error-handling-and-type-safety","Error Handling and Type Safety",[127,11929,11931],{"id":11930},"custom-error-classes","Custom Error Classes",[263,11933,11935],{"className":8887,"code":11934,"language":8889,"meta":268,"style":268},"// src/errors/AppError.ts\nexport class AppError extends Error {\n public readonly statusCode: number\n public readonly isOperational: boolean\n\n constructor(\n message: string,\n statusCode: number = 500,\n isOperational: boolean = true,\n ) {\n super(message)\n this.statusCode = statusCode\n this.isOperational = isOperational\n\n Error.captureStackTrace(this, this.constructor)\n }\n}\n\nexport class ValidationError extends AppError {\n constructor(message: string) {\n super(message, 400)\n }\n}\n\nexport class NotFoundError extends AppError {\n constructor(resource: string) {\n super(`${resource} not found`, 404)\n }\n}\n",[270,11936,11937,11942,11958,11973,11987,11991,11999,12011,12028,12044,12051,12062,12075,12087,12091,12113,12117,12121,12125,12140,12156,12171,12175,12179,12183,12198,12215,12240,12244],{"__ignoreMap":268},[273,11938,11939],{"class":275,"line":276},[273,11940,11941],{"class":293},"// src/errors/AppError.ts\n",[273,11943,11944,11946,11948,11951,11954,11956],{"class":275,"line":283},[273,11945,3371],{"class":2084},[273,11947,3112],{"class":829},[273,11949,11950],{"class":279}," AppError",[273,11952,11953],{"class":829}," extends",[273,11955,5303],{"class":279},[273,11957,839],{"class":820},[273,11959,11960,11963,11965,11968,11970],{"class":275,"line":16},[273,11961,11962],{"class":829}," public",[273,11964,9594],{"class":829},[273,11966,11967],{"class":1333}," statusCode",[273,11969,836],{"class":820},[273,11971,11972],{"class":279}," number\n",[273,11974,11975,11977,11979,11982,11984],{"class":275,"line":308},[273,11976,11962],{"class":829},[273,11978,9594],{"class":829},[273,11980,11981],{"class":1333}," isOperational",[273,11983,836],{"class":820},[273,11985,11986],{"class":279}," boolean\n",[273,11988,11989],{"class":275,"line":321},[273,11990,556],{"emptyLinePlaceholder":555},[273,11992,11993,11996],{"class":275,"line":333},[273,11994,11995],{"class":829}," constructor",[273,11997,11998],{"class":820},"(\n",[273,12000,12001,12004,12006,12009],{"class":275,"line":345},[273,12002,12003],{"class":1444}," message",[273,12005,836],{"class":820},[273,12007,12008],{"class":279}," string",[273,12010,879],{"class":820},[273,12012,12013,12016,12018,12021,12023,12026],{"class":275,"line":357},[273,12014,12015],{"class":1444}," statusCode",[273,12017,836],{"class":820},[273,12019,12020],{"class":279}," number",[273,12022,1725],{"class":820},[273,12024,12025],{"class":1199}," 500",[273,12027,879],{"class":820},[273,12029,12030,12033,12035,12038,12040,12042],{"class":275,"line":370},[273,12031,12032],{"class":1444}," isOperational",[273,12034,836],{"class":820},[273,12036,12037],{"class":279}," boolean",[273,12039,1725],{"class":820},[273,12041,1340],{"class":1339},[273,12043,879],{"class":820},[273,12045,12046,12049],{"class":275,"line":380},[273,12047,12048],{"class":820}," )",[273,12050,839],{"class":820},[273,12052,12053,12056,12058,12060],{"class":275,"line":390},[273,12054,12055],{"class":860}," super",[273,12057,1054],{"class":1333},[273,12059,6889],{"class":860},[273,12061,1064],{"class":1333},[273,12063,12064,12067,12070,12072],{"class":275,"line":401},[273,12065,12066],{"class":820}," this.",[273,12068,12069],{"class":860},"statusCode",[273,12071,1725],{"class":820},[273,12073,12074],{"class":860}," statusCode\n",[273,12076,12077,12079,12082,12084],{"class":275,"line":29},[273,12078,12066],{"class":820},[273,12080,12081],{"class":860},"isOperational",[273,12083,1725],{"class":820},[273,12085,12086],{"class":860}," isOperational\n",[273,12088,12089],{"class":275,"line":420},[273,12090,556],{"emptyLinePlaceholder":555},[273,12092,12093,12096,12098,12101,12103,12106,12108,12111],{"class":275,"line":430},[273,12094,12095],{"class":860}," Error",[273,12097,1185],{"class":820},[273,12099,12100],{"class":641},"captureStackTrace",[273,12102,1054],{"class":1333},[273,12104,12105],{"class":820},"this,",[273,12107,3495],{"class":820},[273,12109,12110],{"class":860},"constructor",[273,12112,1064],{"class":1333},[273,12114,12115],{"class":275,"line":440},[273,12116,999],{"class":820},[273,12118,12119],{"class":275,"line":450},[273,12120,1004],{"class":820},[273,12122,12123],{"class":275,"line":460},[273,12124,556],{"emptyLinePlaceholder":555},[273,12126,12127,12129,12131,12134,12136,12138],{"class":275,"line":471},[273,12128,3371],{"class":2084},[273,12130,3112],{"class":829},[273,12132,12133],{"class":279}," ValidationError",[273,12135,11953],{"class":829},[273,12137,11950],{"class":279},[273,12139,839],{"class":820},[273,12141,12142,12144,12146,12148,12150,12152,12154],{"class":275,"line":743},[273,12143,11995],{"class":829},[273,12145,1054],{"class":820},[273,12147,6889],{"class":1444},[273,12149,836],{"class":820},[273,12151,12008],{"class":279},[273,12153,1463],{"class":820},[273,12155,839],{"class":820},[273,12157,12158,12160,12162,12164,12166,12169],{"class":275,"line":749},[273,12159,12055],{"class":860},[273,12161,1054],{"class":1333},[273,12163,6889],{"class":860},[273,12165,1375],{"class":820},[273,12167,12168],{"class":1199}," 400",[273,12170,1064],{"class":1333},[273,12172,12173],{"class":275,"line":771},[273,12174,999],{"class":820},[273,12176,12177],{"class":275,"line":784},[273,12178,1004],{"class":820},[273,12180,12181],{"class":275,"line":789},[273,12182,556],{"emptyLinePlaceholder":555},[273,12184,12185,12187,12189,12192,12194,12196],{"class":275,"line":1471},[273,12186,3371],{"class":2084},[273,12188,3112],{"class":829},[273,12190,12191],{"class":279}," NotFoundError",[273,12193,11953],{"class":829},[273,12195,11950],{"class":279},[273,12197,839],{"class":820},[273,12199,12200,12202,12204,12207,12209,12211,12213],{"class":275,"line":1493},[273,12201,11995],{"class":829},[273,12203,1054],{"class":820},[273,12205,12206],{"class":1444},"resource",[273,12208,836],{"class":820},[273,12210,12008],{"class":279},[273,12212,1463],{"class":820},[273,12214,839],{"class":820},[273,12216,12217,12219,12221,12224,12226,12228,12231,12233,12235,12238],{"class":275,"line":1535},[273,12218,12055],{"class":860},[273,12220,1054],{"class":1333},[273,12222,12223],{"class":820},"`${",[273,12225,12206],{"class":860},[273,12227,1538],{"class":820},[273,12229,12230],{"class":289}," not found",[273,12232,1690],{"class":820},[273,12234,1375],{"class":820},[273,12236,12237],{"class":1199}," 404",[273,12239,1064],{"class":1333},[273,12241,12242],{"class":275,"line":1543},[273,12243,999],{"class":820},[273,12245,12246],{"class":275,"line":1548},[273,12247,1004],{"class":820},[127,12249,12251],{"id":12250},"global-error-handler","Global Error Handler",[263,12253,12255],{"className":8887,"code":12254,"language":8889,"meta":268,"style":268},"// src/middleware/errorHandler.ts\nimport { Request, Response, NextFunction } from \"express\"\nimport { AppError } from \"../errors/AppError\"\n\nexport const errorHandler = (\n error: Error,\n req: Request,\n res: Response,\n next: NextFunction,\n) => {\n if (error instanceof AppError) {\n return res.status(error.statusCode).json({\n status: \"error\",\n message: error.message,\n ...(process.env.NODE_ENV === \"development\" && { stack: error.stack }),\n })\n }\n\n // Log unexpected errors\n console.error(\"Unexpected error:\", error)\n\n res.status(500).json({\n status: \"error\",\n message: \"Internal server error\",\n })\n}\n",[270,12256,12257,12262,12291,12310,12314,12328,12338,12349,12359,12370,12379,12396,12424,12439,12454,12501,12507,12511,12515,12520,12543,12547,12569,12584,12599,12605],{"__ignoreMap":268},[273,12258,12259],{"class":275,"line":276},[273,12260,12261],{"class":293},"// src/middleware/errorHandler.ts\n",[273,12263,12264,12266,12268,12271,12273,12276,12278,12281,12283,12285,12287,12289],{"class":275,"line":283},[273,12265,3312],{"class":2084},[273,12267,2062],{"class":820},[273,12269,12270],{"class":860}," Request",[273,12272,1375],{"class":820},[273,12274,12275],{"class":860}," Response",[273,12277,1375],{"class":820},[273,12279,12280],{"class":860}," NextFunction",[273,12282,1343],{"class":820},[273,12284,3355],{"class":2084},[273,12286,854],{"class":820},[273,12288,1059],{"class":289},[273,12290,958],{"class":820},[273,12292,12293,12295,12297,12299,12301,12303,12305,12308],{"class":275,"line":16},[273,12294,3312],{"class":2084},[273,12296,2062],{"class":820},[273,12298,11950],{"class":860},[273,12300,1343],{"class":820},[273,12302,3355],{"class":2084},[273,12304,854],{"class":820},[273,12306,12307],{"class":289},"../errors/AppError",[273,12309,958],{"class":820},[273,12311,12312],{"class":275,"line":308},[273,12313,556],{"emptyLinePlaceholder":555},[273,12315,12316,12318,12320,12323,12325],{"class":275,"line":321},[273,12317,3371],{"class":2084},[273,12319,5647],{"class":829},[273,12321,12322],{"class":860}," errorHandler ",[273,12324,1048],{"class":820},[273,12326,12327],{"class":860}," (\n",[273,12329,12330,12332,12334,12336],{"class":275,"line":333},[273,12331,6400],{"class":1444},[273,12333,836],{"class":820},[273,12335,5303],{"class":279},[273,12337,879],{"class":820},[273,12339,12340,12343,12345,12347],{"class":275,"line":345},[273,12341,12342],{"class":1444}," req",[273,12344,836],{"class":820},[273,12346,12270],{"class":279},[273,12348,879],{"class":820},[273,12350,12351,12353,12355,12357],{"class":275,"line":357},[273,12352,1496],{"class":1444},[273,12354,836],{"class":820},[273,12356,12275],{"class":279},[273,12358,879],{"class":820},[273,12360,12361,12364,12366,12368],{"class":275,"line":370},[273,12362,12363],{"class":1444}," next",[273,12365,836],{"class":820},[273,12367,12280],{"class":279},[273,12369,879],{"class":820},[273,12371,12372,12374,12377],{"class":275,"line":380},[273,12373,2114],{"class":860},[273,12375,12376],{"class":829},"=>",[273,12378,839],{"class":820},[273,12380,12381,12383,12385,12387,12390,12392,12394],{"class":275,"line":390},[273,12382,2085],{"class":2084},[273,12384,1575],{"class":1333},[273,12386,1479],{"class":860},[273,12388,12389],{"class":820}," instanceof",[273,12391,11950],{"class":279},[273,12393,2114],{"class":1333},[273,12395,821],{"class":820},[273,12397,12398,12400,12402,12404,12406,12408,12410,12412,12414,12416,12418,12420,12422],{"class":275,"line":401},[273,12399,2121],{"class":2084},[273,12401,1455],{"class":860},[273,12403,1185],{"class":820},[273,12405,1501],{"class":641},[273,12407,1054],{"class":1333},[273,12409,1479],{"class":860},[273,12411,1185],{"class":820},[273,12413,12069],{"class":860},[273,12415,1463],{"class":1333},[273,12417,1185],{"class":820},[273,12419,813],{"class":641},[273,12421,1054],{"class":1333},[273,12423,821],{"class":820},[273,12425,12426,12429,12431,12433,12435,12437],{"class":275,"line":29},[273,12427,12428],{"class":1333}," status",[273,12430,836],{"class":820},[273,12432,854],{"class":820},[273,12434,1479],{"class":289},[273,12436,833],{"class":820},[273,12438,879],{"class":820},[273,12440,12441,12444,12446,12448,12450,12452],{"class":275,"line":420},[273,12442,12443],{"class":1333}," message",[273,12445,836],{"class":820},[273,12447,1519],{"class":860},[273,12449,1185],{"class":820},[273,12451,6889],{"class":860},[273,12453,879],{"class":820},[273,12455,12456,12458,12460,12463,12465,12467,12469,12471,12473,12475,12477,12479,12482,12484,12487,12489,12491,12493,12495,12497,12499],{"class":275,"line":430},[273,12457,5233],{"class":820},[273,12459,1054],{"class":1333},[273,12461,12462],{"class":860},"process",[273,12464,1185],{"class":820},[273,12466,1188],{"class":860},[273,12468,1185],{"class":820},[273,12470,8292],{"class":860},[273,12472,2490],{"class":820},[273,12474,854],{"class":820},[273,12476,5996],{"class":289},[273,12478,833],{"class":820},[273,12480,12481],{"class":820}," &&",[273,12483,2062],{"class":820},[273,12485,12486],{"class":1333}," stack",[273,12488,836],{"class":820},[273,12490,1519],{"class":860},[273,12492,1185],{"class":820},[273,12494,1488],{"class":860},[273,12496,1343],{"class":820},[273,12498,1463],{"class":1333},[273,12500,879],{"class":820},[273,12502,12503,12505],{"class":275,"line":440},[273,12504,2174],{"class":820},[273,12506,1064],{"class":1333},[273,12508,12509],{"class":275,"line":450},[273,12510,999],{"class":820},[273,12512,12513],{"class":275,"line":460},[273,12514,556],{"emptyLinePlaceholder":555},[273,12516,12517],{"class":275,"line":471},[273,12518,12519],{"class":293}," // Log unexpected errors\n",[273,12521,12522,12524,12526,12528,12530,12532,12535,12537,12539,12541],{"class":275,"line":743},[273,12523,1474],{"class":860},[273,12525,1185],{"class":820},[273,12527,1479],{"class":641},[273,12529,1054],{"class":1333},[273,12531,833],{"class":820},[273,12533,12534],{"class":289},"Unexpected error:",[273,12536,833],{"class":820},[273,12538,1375],{"class":820},[273,12540,1519],{"class":860},[273,12542,1064],{"class":1333},[273,12544,12545],{"class":275,"line":749},[273,12546,556],{"emptyLinePlaceholder":555},[273,12548,12549,12551,12553,12555,12557,12559,12561,12563,12565,12567],{"class":275,"line":771},[273,12550,1496],{"class":860},[273,12552,1185],{"class":820},[273,12554,1501],{"class":641},[273,12556,1054],{"class":1333},[273,12558,1506],{"class":1199},[273,12560,1463],{"class":1333},[273,12562,1185],{"class":820},[273,12564,813],{"class":641},[273,12566,1054],{"class":1333},[273,12568,821],{"class":820},[273,12570,12571,12574,12576,12578,12580,12582],{"class":275,"line":784},[273,12572,12573],{"class":1333}," status",[273,12575,836],{"class":820},[273,12577,854],{"class":820},[273,12579,1479],{"class":289},[273,12581,833],{"class":820},[273,12583,879],{"class":820},[273,12585,12586,12588,12590,12592,12595,12597],{"class":275,"line":789},[273,12587,12003],{"class":1333},[273,12589,836],{"class":820},[273,12591,854],{"class":820},[273,12593,12594],{"class":289},"Internal server error",[273,12596,833],{"class":820},[273,12598,879],{"class":820},[273,12600,12601,12603],{"class":275,"line":1471},[273,12602,2004],{"class":820},[273,12604,1064],{"class":1333},[273,12606,12607],{"class":275,"line":1493},[273,12608,1004],{"class":820},[99,12610,12612],{"id":12611},"database-integration-with-type-safety","Database Integration with Type Safety",[127,12614,12616],{"id":12615},"prisma-integration","Prisma Integration",[263,12618,12620],{"className":8887,"code":12619,"language":8889,"meta":268,"style":268},"// src/database/client.ts\nimport { PrismaClient } from \"@prisma/client\"\n\nconst globalForPrisma = globalThis as unknown as {\n prisma: PrismaClient | undefined\n}\n\nexport const prisma = globalForPrisma.prisma ?? new PrismaClient()\n\nif (process.env.NODE_ENV !== \"production\") {\n globalForPrisma.prisma = prisma\n}\n",[270,12621,12622,12627,12647,12651,12674,12689,12693,12697,12725,12729,12759,12774],{"__ignoreMap":268},[273,12623,12624],{"class":275,"line":276},[273,12625,12626],{"class":293},"// src/database/client.ts\n",[273,12628,12629,12631,12633,12636,12638,12640,12642,12645],{"class":275,"line":283},[273,12630,3312],{"class":2084},[273,12632,2062],{"class":820},[273,12634,12635],{"class":860}," PrismaClient",[273,12637,1343],{"class":820},[273,12639,3355],{"class":2084},[273,12641,854],{"class":820},[273,12643,12644],{"class":289},"@prisma/client",[273,12646,958],{"class":820},[273,12648,12649],{"class":275,"line":16},[273,12650,556],{"emptyLinePlaceholder":555},[273,12652,12653,12655,12658,12660,12663,12666,12669,12672],{"class":275,"line":308},[273,12654,1042],{"class":829},[273,12656,12657],{"class":860}," globalForPrisma ",[273,12659,1048],{"class":820},[273,12661,12662],{"class":860}," globalThis ",[273,12664,12665],{"class":2084},"as",[273,12667,12668],{"class":279}," unknown",[273,12670,12671],{"class":2084}," as",[273,12673,839],{"class":820},[273,12675,12676,12679,12681,12683,12686],{"class":275,"line":321},[273,12677,12678],{"class":1333}," prisma",[273,12680,836],{"class":820},[273,12682,12635],{"class":279},[273,12684,12685],{"class":820}," |",[273,12687,12688],{"class":279}," undefined\n",[273,12690,12691],{"class":275,"line":333},[273,12692,1004],{"class":820},[273,12694,12695],{"class":275,"line":345},[273,12696,556],{"emptyLinePlaceholder":555},[273,12698,12699,12701,12703,12706,12708,12711,12713,12716,12719,12721,12723],{"class":275,"line":357},[273,12700,3371],{"class":2084},[273,12702,5647],{"class":829},[273,12704,12705],{"class":860}," prisma ",[273,12707,1048],{"class":820},[273,12709,12710],{"class":860}," globalForPrisma",[273,12712,1185],{"class":820},[273,12714,12715],{"class":860},"prisma ",[273,12717,12718],{"class":820},"??",[273,12720,1623],{"class":820},[273,12722,12635],{"class":641},[273,12724,1170],{"class":860},[273,12726,12727],{"class":275,"line":370},[273,12728,556],{"emptyLinePlaceholder":555},[273,12730,12731,12734,12737,12739,12741,12743,12745,12748,12750,12753,12755,12757],{"class":275,"line":380},[273,12732,12733],{"class":2084},"if",[273,12735,12736],{"class":860}," (process",[273,12738,1185],{"class":820},[273,12740,1188],{"class":860},[273,12742,1185],{"class":820},[273,12744,5989],{"class":860},[273,12746,12747],{"class":820},"!==",[273,12749,854],{"class":820},[273,12751,12752],{"class":289},"production",[273,12754,833],{"class":820},[273,12756,2114],{"class":860},[273,12758,821],{"class":820},[273,12760,12761,12764,12766,12769,12771],{"class":275,"line":390},[273,12762,12763],{"class":860}," globalForPrisma",[273,12765,1185],{"class":820},[273,12767,12768],{"class":860},"prisma",[273,12770,1725],{"class":820},[273,12772,12773],{"class":860}," prisma\n",[273,12775,12776],{"class":275,"line":401},[273,12777,1004],{"class":820},[127,12779,12781],{"id":12780},"repository-pattern","Repository Pattern",[263,12783,12785],{"className":8887,"code":12784,"language":8889,"meta":268,"style":268},"// src/repositories/UserRepository.ts\nimport { User, Prisma } from \"@prisma/client\"\nimport { prisma } from \"../database/client\"\n\nexport class UserRepository {\n async findById(id: string): Promise {\n return prisma.user.findUnique({\n where: { id },\n })\n }\n\n async create(data: Prisma.UserCreateInput): Promise {\n return prisma.user.create({\n data,\n })\n }\n\n async update(id: string, data: Prisma.UserUpdateInput): Promise {\n return prisma.user.update({\n where: { id },\n data,\n })\n }\n\n async delete(id: string): Promise {\n await prisma.user.delete({\n where: { id },\n })\n }\n}\n",[270,12786,12787,12792,12816,12836,12840,12851,12886,12905,12918,12924,12928,12932,12963,12982,12988,12994,12998,13002,13042,13061,13073,13079,13085,13089,13093,13121,13139,13151,13157,13161],{"__ignoreMap":268},[273,12788,12789],{"class":275,"line":276},[273,12790,12791],{"class":293},"// src/repositories/UserRepository.ts\n",[273,12793,12794,12796,12798,12801,12803,12806,12808,12810,12812,12814],{"class":275,"line":283},[273,12795,3312],{"class":2084},[273,12797,2062],{"class":820},[273,12799,12800],{"class":860}," User",[273,12802,1375],{"class":820},[273,12804,12805],{"class":860}," Prisma",[273,12807,1343],{"class":820},[273,12809,3355],{"class":2084},[273,12811,854],{"class":820},[273,12813,12644],{"class":289},[273,12815,958],{"class":820},[273,12817,12818,12820,12822,12825,12827,12829,12831,12834],{"class":275,"line":16},[273,12819,3312],{"class":2084},[273,12821,2062],{"class":820},[273,12823,12824],{"class":860}," prisma",[273,12826,1343],{"class":820},[273,12828,3355],{"class":2084},[273,12830,854],{"class":820},[273,12832,12833],{"class":289},"../database/client",[273,12835,958],{"class":820},[273,12837,12838],{"class":275,"line":308},[273,12839,556],{"emptyLinePlaceholder":555},[273,12841,12842,12844,12846,12849],{"class":275,"line":321},[273,12843,3371],{"class":2084},[273,12845,3112],{"class":829},[273,12847,12848],{"class":279}," UserRepository",[273,12850,839],{"class":820},[273,12852,12853,12855,12858,12860,12862,12864,12866,12869,12872,12874,12877,12879,12882,12884],{"class":275,"line":333},[273,12854,3480],{"class":829},[273,12856,12857],{"class":1333}," findById",[273,12859,1054],{"class":820},[273,12861,2422],{"class":1444},[273,12863,836],{"class":820},[273,12865,12008],{"class":279},[273,12867,12868],{"class":820},"):",[273,12870,12871],{"class":279}," Promise",[273,12873,3075],{"class":820},[273,12875,12876],{"class":279},"User",[273,12878,12685],{"class":820},[273,12880,12881],{"class":279}," null",[273,12883,3134],{"class":820},[273,12885,839],{"class":820},[273,12887,12888,12890,12892,12894,12896,12898,12901,12903],{"class":275,"line":345},[273,12889,2121],{"class":2084},[273,12891,12824],{"class":860},[273,12893,1185],{"class":820},[273,12895,10053],{"class":860},[273,12897,1185],{"class":820},[273,12899,12900],{"class":641},"findUnique",[273,12902,1054],{"class":1333},[273,12904,821],{"class":820},[273,12906,12907,12910,12912,12914,12916],{"class":275,"line":357},[273,12908,12909],{"class":1333}," where",[273,12911,836],{"class":820},[273,12913,2062],{"class":820},[273,12915,1810],{"class":860},[273,12917,9020],{"class":820},[273,12919,12920,12922],{"class":275,"line":370},[273,12921,2174],{"class":820},[273,12923,1064],{"class":1333},[273,12925,12926],{"class":275,"line":380},[273,12927,999],{"class":820},[273,12929,12930],{"class":275,"line":390},[273,12931,556],{"emptyLinePlaceholder":555},[273,12933,12934,12936,12938,12940,12942,12944,12946,12948,12951,12953,12955,12957,12959,12961],{"class":275,"line":401},[273,12935,3480],{"class":829},[273,12937,680],{"class":1333},[273,12939,1054],{"class":820},[273,12941,3736],{"class":1444},[273,12943,836],{"class":820},[273,12945,12805],{"class":279},[273,12947,1185],{"class":820},[273,12949,12950],{"class":279},"UserCreateInput",[273,12952,12868],{"class":820},[273,12954,12871],{"class":279},[273,12956,3075],{"class":820},[273,12958,12876],{"class":279},[273,12960,3134],{"class":820},[273,12962,839],{"class":820},[273,12964,12965,12967,12969,12971,12973,12975,12978,12980],{"class":275,"line":29},[273,12966,2121],{"class":2084},[273,12968,12824],{"class":860},[273,12970,1185],{"class":820},[273,12972,10053],{"class":860},[273,12974,1185],{"class":820},[273,12976,12977],{"class":641},"create",[273,12979,1054],{"class":1333},[273,12981,821],{"class":820},[273,12983,12984,12986],{"class":275,"line":420},[273,12985,10500],{"class":860},[273,12987,879],{"class":820},[273,12989,12990,12992],{"class":275,"line":430},[273,12991,2174],{"class":820},[273,12993,1064],{"class":1333},[273,12995,12996],{"class":275,"line":440},[273,12997,999],{"class":820},[273,12999,13000],{"class":275,"line":450},[273,13001,556],{"emptyLinePlaceholder":555},[273,13003,13004,13006,13009,13011,13013,13015,13017,13019,13021,13023,13025,13027,13030,13032,13034,13036,13038,13040],{"class":275,"line":460},[273,13005,3480],{"class":829},[273,13007,13008],{"class":1333}," update",[273,13010,1054],{"class":820},[273,13012,2422],{"class":1444},[273,13014,836],{"class":820},[273,13016,12008],{"class":279},[273,13018,1375],{"class":820},[273,13020,10396],{"class":1444},[273,13022,836],{"class":820},[273,13024,12805],{"class":279},[273,13026,1185],{"class":820},[273,13028,13029],{"class":279},"UserUpdateInput",[273,13031,12868],{"class":820},[273,13033,12871],{"class":279},[273,13035,3075],{"class":820},[273,13037,12876],{"class":279},[273,13039,3134],{"class":820},[273,13041,839],{"class":820},[273,13043,13044,13046,13048,13050,13052,13054,13057,13059],{"class":275,"line":471},[273,13045,2121],{"class":2084},[273,13047,12824],{"class":860},[273,13049,1185],{"class":820},[273,13051,10053],{"class":860},[273,13053,1185],{"class":820},[273,13055,13056],{"class":641},"update",[273,13058,1054],{"class":1333},[273,13060,821],{"class":820},[273,13062,13063,13065,13067,13069,13071],{"class":275,"line":743},[273,13064,12909],{"class":1333},[273,13066,836],{"class":820},[273,13068,2062],{"class":820},[273,13070,1810],{"class":860},[273,13072,9020],{"class":820},[273,13074,13075,13077],{"class":275,"line":749},[273,13076,10500],{"class":860},[273,13078,879],{"class":820},[273,13080,13081,13083],{"class":275,"line":771},[273,13082,2174],{"class":820},[273,13084,1064],{"class":1333},[273,13086,13087],{"class":275,"line":784},[273,13088,999],{"class":820},[273,13090,13091],{"class":275,"line":789},[273,13092,556],{"emptyLinePlaceholder":555},[273,13094,13095,13097,13100,13102,13104,13106,13108,13110,13112,13114,13117,13119],{"class":275,"line":1471},[273,13096,3480],{"class":829},[273,13098,13099],{"class":1333}," delete",[273,13101,1054],{"class":820},[273,13103,2422],{"class":1444},[273,13105,836],{"class":820},[273,13107,12008],{"class":279},[273,13109,12868],{"class":820},[273,13111,12871],{"class":279},[273,13113,3075],{"class":820},[273,13115,13116],{"class":279},"void",[273,13118,3134],{"class":820},[273,13120,839],{"class":820},[273,13122,13123,13125,13127,13129,13131,13133,13135,13137],{"class":275,"line":1493},[273,13124,3492],{"class":2084},[273,13126,12824],{"class":860},[273,13128,1185],{"class":820},[273,13130,10053],{"class":860},[273,13132,1185],{"class":820},[273,13134,2772],{"class":641},[273,13136,1054],{"class":1333},[273,13138,821],{"class":820},[273,13140,13141,13143,13145,13147,13149],{"class":275,"line":1535},[273,13142,12909],{"class":1333},[273,13144,836],{"class":820},[273,13146,2062],{"class":820},[273,13148,1810],{"class":860},[273,13150,9020],{"class":820},[273,13152,13153,13155],{"class":275,"line":1543},[273,13154,2174],{"class":820},[273,13156,1064],{"class":1333},[273,13158,13159],{"class":275,"line":1548},[273,13160,999],{"class":820},[273,13162,13163],{"class":275,"line":1554},[273,13164,1004],{"class":820},[99,13166,13168],{"id":13167},"api-design-with-strong-types","API Design with Strong Types",[127,13170,13172],{"id":13171},"requestresponse-types","Request/Response Types",[263,13174,13176],{"className":8887,"code":13175,"language":8889,"meta":268,"style":268},"// src/types/api.ts\nexport interface CreateUserRequest {\n email: string\n name: string\n password: string\n}\n\nexport interface UserResponse {\n id: string\n email: string\n name: string\n createdAt: string\n updatedAt: string\n}\n\nexport interface ApiResponse {\n success: boolean\n data?: T\n error?: string\n message?: string\n}\n",[270,13177,13178,13183,13195,13205,13213,13222,13226,13230,13241,13250,13258,13266,13275,13284,13288,13292,13314,13323,13333,13341,13350],{"__ignoreMap":268},[273,13179,13180],{"class":275,"line":276},[273,13181,13182],{"class":293},"// src/types/api.ts\n",[273,13184,13185,13187,13190,13193],{"class":275,"line":283},[273,13186,3371],{"class":2084},[273,13188,13189],{"class":829}," interface",[273,13191,13192],{"class":279}," CreateUserRequest",[273,13194,839],{"class":820},[273,13196,13197,13200,13202],{"class":275,"line":16},[273,13198,13199],{"class":1333}," email",[273,13201,836],{"class":820},[273,13203,13204],{"class":279}," string\n",[273,13206,13207,13209,13211],{"class":275,"line":308},[273,13208,3381],{"class":1333},[273,13210,836],{"class":820},[273,13212,13204],{"class":279},[273,13214,13215,13218,13220],{"class":275,"line":321},[273,13216,13217],{"class":1333}," password",[273,13219,836],{"class":820},[273,13221,13204],{"class":279},[273,13223,13224],{"class":275,"line":333},[273,13225,1004],{"class":820},[273,13227,13228],{"class":275,"line":345},[273,13229,556],{"emptyLinePlaceholder":555},[273,13231,13232,13234,13236,13239],{"class":275,"line":357},[273,13233,3371],{"class":2084},[273,13235,13189],{"class":829},[273,13237,13238],{"class":279}," UserResponse",[273,13240,839],{"class":820},[273,13242,13243,13246,13248],{"class":275,"line":370},[273,13244,13245],{"class":1333}," id",[273,13247,836],{"class":820},[273,13249,13204],{"class":279},[273,13251,13252,13254,13256],{"class":275,"line":380},[273,13253,13199],{"class":1333},[273,13255,836],{"class":820},[273,13257,13204],{"class":279},[273,13259,13260,13262,13264],{"class":275,"line":390},[273,13261,3381],{"class":1333},[273,13263,836],{"class":820},[273,13265,13204],{"class":279},[273,13267,13268,13271,13273],{"class":275,"line":401},[273,13269,13270],{"class":1333}," createdAt",[273,13272,836],{"class":820},[273,13274,13204],{"class":279},[273,13276,13277,13280,13282],{"class":275,"line":29},[273,13278,13279],{"class":1333}," updatedAt",[273,13281,836],{"class":820},[273,13283,13204],{"class":279},[273,13285,13286],{"class":275,"line":420},[273,13287,1004],{"class":820},[273,13289,13290],{"class":275,"line":430},[273,13291,556],{"emptyLinePlaceholder":555},[273,13293,13294,13296,13298,13301,13303,13306,13308,13310,13312],{"class":275,"line":440},[273,13295,3371],{"class":2084},[273,13297,13189],{"class":829},[273,13299,13300],{"class":279}," ApiResponse",[273,13302,3075],{"class":820},[273,13304,13305],{"class":279},"T",[273,13307,1725],{"class":820},[273,13309,12668],{"class":279},[273,13311,3134],{"class":820},[273,13313,839],{"class":820},[273,13315,13316,13319,13321],{"class":275,"line":450},[273,13317,13318],{"class":1333}," success",[273,13320,836],{"class":820},[273,13322,11986],{"class":279},[273,13324,13325,13327,13330],{"class":275,"line":460},[273,13326,3424],{"class":1333},[273,13328,13329],{"class":820},"?:",[273,13331,13332],{"class":279}," T\n",[273,13334,13335,13337,13339],{"class":275,"line":471},[273,13336,6400],{"class":1333},[273,13338,13329],{"class":820},[273,13340,13204],{"class":279},[273,13342,13343,13346,13348],{"class":275,"line":743},[273,13344,13345],{"class":1333}," message",[273,13347,13329],{"class":820},[273,13349,13204],{"class":279},[273,13351,13352],{"class":275,"line":749},[273,13353,1004],{"class":820},[127,13355,13357],{"id":13356},"validated-controllers","Validated Controllers",[263,13359,13361],{"className":8887,"code":13360,"language":8889,"meta":268,"style":268},"// src/controllers/UserController.ts\nimport { Request, Response, NextFunction } from \"express\"\nimport { z } from \"zod\"\nimport { UserService } from \"../services/UserService\"\nimport { ValidationError } from \"../errors/AppError\"\n\nconst createUserSchema = z.object({\n email: z.string().email(),\n name: z.string().min(2),\n password: z.string().min(8),\n})\n\nexport class UserController {\n constructor(private userService: UserService) {}\n\n async createUser(req: Request, res: Response, next: NextFunction) {\n try {\n const validatedData = createUserSchema.parse(req.body)\n const user = await this.userService.createUser(validatedData)\n\n res.status(201).json({\n success: true,\n data: user,\n })\n } catch (error) {\n if (error instanceof z.ZodError) {\n next(\n new ValidationError(error.errors[0]?.message || \"Validation failed\"),\n )\n } else {\n next(error)\n }\n }\n }\n}\n",[270,13362,13363,13368,13394,13414,13434,13452,13456,13476,13500,13528,13555,13561,13565,13576,13596,13600,13635,13641,13668,13695,13699,13722,13732,13743,13749,13763,13784,13791,13830,13835,13844,13854,13858,13862,13866],{"__ignoreMap":268},[273,13364,13365],{"class":275,"line":276},[273,13366,13367],{"class":293},"// src/controllers/UserController.ts\n",[273,13369,13370,13372,13374,13376,13378,13380,13382,13384,13386,13388,13390,13392],{"class":275,"line":283},[273,13371,3312],{"class":2084},[273,13373,2062],{"class":820},[273,13375,12270],{"class":860},[273,13377,1375],{"class":820},[273,13379,12275],{"class":860},[273,13381,1375],{"class":820},[273,13383,12280],{"class":860},[273,13385,1343],{"class":820},[273,13387,3355],{"class":2084},[273,13389,854],{"class":820},[273,13391,1059],{"class":289},[273,13393,958],{"class":820},[273,13395,13396,13398,13400,13403,13405,13407,13409,13412],{"class":275,"line":16},[273,13397,3312],{"class":2084},[273,13399,2062],{"class":820},[273,13401,13402],{"class":860}," z",[273,13404,1343],{"class":820},[273,13406,3355],{"class":2084},[273,13408,854],{"class":820},[273,13410,13411],{"class":289},"zod",[273,13413,958],{"class":820},[273,13415,13416,13418,13420,13423,13425,13427,13429,13432],{"class":275,"line":308},[273,13417,3312],{"class":2084},[273,13419,2062],{"class":820},[273,13421,13422],{"class":860}," UserService",[273,13424,1343],{"class":820},[273,13426,3355],{"class":2084},[273,13428,854],{"class":820},[273,13430,13431],{"class":289},"../services/UserService",[273,13433,958],{"class":820},[273,13435,13436,13438,13440,13442,13444,13446,13448,13450],{"class":275,"line":321},[273,13437,3312],{"class":2084},[273,13439,2062],{"class":820},[273,13441,12133],{"class":860},[273,13443,1343],{"class":820},[273,13445,3355],{"class":2084},[273,13447,854],{"class":820},[273,13449,12307],{"class":289},[273,13451,958],{"class":820},[273,13453,13454],{"class":275,"line":333},[273,13455,556],{"emptyLinePlaceholder":555},[273,13457,13458,13460,13463,13465,13467,13469,13472,13474],{"class":275,"line":345},[273,13459,1042],{"class":829},[273,13461,13462],{"class":860}," createUserSchema ",[273,13464,1048],{"class":820},[273,13466,13402],{"class":860},[273,13468,1185],{"class":820},[273,13470,13471],{"class":641},"object",[273,13473,1054],{"class":860},[273,13475,821],{"class":820},[273,13477,13478,13480,13482,13484,13486,13489,13491,13493,13496,13498],{"class":275,"line":357},[273,13479,13199],{"class":1333},[273,13481,836],{"class":820},[273,13483,13402],{"class":860},[273,13485,1185],{"class":820},[273,13487,13488],{"class":641},"string",[273,13490,1629],{"class":860},[273,13492,1185],{"class":820},[273,13494,13495],{"class":641},"email",[273,13497,1629],{"class":860},[273,13499,879],{"class":820},[273,13501,13502,13504,13506,13508,13510,13512,13514,13516,13519,13521,13524,13526],{"class":275,"line":370},[273,13503,3381],{"class":1333},[273,13505,836],{"class":820},[273,13507,13402],{"class":860},[273,13509,1185],{"class":820},[273,13511,13488],{"class":641},[273,13513,1629],{"class":860},[273,13515,1185],{"class":820},[273,13517,13518],{"class":641},"min",[273,13520,1054],{"class":860},[273,13522,13523],{"class":1199},"2",[273,13525,1463],{"class":860},[273,13527,879],{"class":820},[273,13529,13530,13532,13534,13536,13538,13540,13542,13544,13546,13548,13551,13553],{"class":275,"line":380},[273,13531,13217],{"class":1333},[273,13533,836],{"class":820},[273,13535,13402],{"class":860},[273,13537,1185],{"class":820},[273,13539,13488],{"class":641},[273,13541,1629],{"class":860},[273,13543,1185],{"class":820},[273,13545,13518],{"class":641},[273,13547,1054],{"class":860},[273,13549,13550],{"class":1199},"8",[273,13552,1463],{"class":860},[273,13554,879],{"class":820},[273,13556,13557,13559],{"class":275,"line":390},[273,13558,1538],{"class":820},[273,13560,1064],{"class":860},[273,13562,13563],{"class":275,"line":401},[273,13564,556],{"emptyLinePlaceholder":555},[273,13566,13567,13569,13571,13574],{"class":275,"line":29},[273,13568,3371],{"class":2084},[273,13570,3112],{"class":829},[273,13572,13573],{"class":279}," UserController",[273,13575,839],{"class":820},[273,13577,13578,13580,13582,13585,13588,13590,13592,13594],{"class":275,"line":420},[273,13579,11995],{"class":829},[273,13581,1054],{"class":820},[273,13583,13584],{"class":829},"private",[273,13586,13587],{"class":1444}," userService",[273,13589,836],{"class":820},[273,13591,13422],{"class":279},[273,13593,1463],{"class":820},[273,13595,9794],{"class":820},[273,13597,13598],{"class":275,"line":430},[273,13599,556],{"emptyLinePlaceholder":555},[273,13601,13602,13604,13607,13609,13611,13613,13615,13617,13619,13621,13623,13625,13627,13629,13631,13633],{"class":275,"line":440},[273,13603,3480],{"class":829},[273,13605,13606],{"class":1333}," createUser",[273,13608,1054],{"class":820},[273,13610,1578],{"class":1444},[273,13612,836],{"class":820},[273,13614,12270],{"class":279},[273,13616,1375],{"class":820},[273,13618,1455],{"class":1444},[273,13620,836],{"class":820},[273,13622,12275],{"class":279},[273,13624,1375],{"class":820},[273,13626,1460],{"class":1444},[273,13628,836],{"class":820},[273,13630,12280],{"class":279},[273,13632,1463],{"class":820},[273,13634,839],{"class":820},[273,13636,13637,13639],{"class":275,"line":450},[273,13638,6808],{"class":2084},[273,13640,839],{"class":820},[273,13642,13643,13645,13648,13650,13653,13655,13658,13660,13662,13664,13666],{"class":275,"line":460},[273,13644,6815],{"class":829},[273,13646,13647],{"class":860}," validatedData",[273,13649,1725],{"class":820},[273,13651,13652],{"class":860}," createUserSchema",[273,13654,1185],{"class":820},[273,13656,13657],{"class":641},"parse",[273,13659,1054],{"class":1333},[273,13661,1578],{"class":860},[273,13663,1185],{"class":820},[273,13665,7319],{"class":860},[273,13667,1064],{"class":1333},[273,13669,13670,13672,13674,13676,13678,13680,13683,13685,13688,13690,13693],{"class":275,"line":471},[273,13671,6815],{"class":829},[273,13673,9684],{"class":860},[273,13675,1725],{"class":820},[273,13677,3558],{"class":2084},[273,13679,3495],{"class":820},[273,13681,13682],{"class":860},"userService",[273,13684,1185],{"class":820},[273,13686,13687],{"class":641},"createUser",[273,13689,1054],{"class":1333},[273,13691,13692],{"class":860},"validatedData",[273,13694,1064],{"class":1333},[273,13696,13697],{"class":275,"line":743},[273,13698,556],{"emptyLinePlaceholder":555},[273,13700,13701,13704,13706,13708,13710,13712,13714,13716,13718,13720],{"class":275,"line":749},[273,13702,13703],{"class":860}," res",[273,13705,1185],{"class":820},[273,13707,1501],{"class":641},[273,13709,1054],{"class":1333},[273,13711,2303],{"class":1199},[273,13713,1463],{"class":1333},[273,13715,1185],{"class":820},[273,13717,813],{"class":641},[273,13719,1054],{"class":1333},[273,13721,821],{"class":820},[273,13723,13724,13726,13728,13730],{"class":275,"line":771},[273,13725,6284],{"class":1333},[273,13727,836],{"class":820},[273,13729,1340],{"class":1339},[273,13731,879],{"class":820},[273,13733,13734,13737,13739,13741],{"class":275,"line":784},[273,13735,13736],{"class":1333}," data",[273,13738,836],{"class":820},[273,13740,9684],{"class":860},[273,13742,879],{"class":820},[273,13744,13745,13747],{"class":275,"line":789},[273,13746,3588],{"class":820},[273,13748,1064],{"class":1333},[273,13750,13751,13753,13755,13757,13759,13761],{"class":275,"line":1471},[273,13752,2174],{"class":820},[273,13754,3591],{"class":2084},[273,13756,1575],{"class":1333},[273,13758,1479],{"class":860},[273,13760,2114],{"class":1333},[273,13762,821],{"class":820},[273,13764,13765,13767,13769,13771,13773,13775,13777,13780,13782],{"class":275,"line":1493},[273,13766,4600],{"class":2084},[273,13768,1575],{"class":1333},[273,13770,1479],{"class":860},[273,13772,12389],{"class":820},[273,13774,13402],{"class":279},[273,13776,1185],{"class":820},[273,13778,13779],{"class":279},"ZodError",[273,13781,2114],{"class":1333},[273,13783,821],{"class":820},[273,13785,13786,13789],{"class":275,"line":1535},[273,13787,13788],{"class":641}," next",[273,13790,11998],{"class":1333},[273,13792,13793,13796,13798,13800,13802,13804,13806,13808,13810,13812,13815,13817,13819,13821,13824,13826,13828],{"class":275,"line":1543},[273,13794,13795],{"class":820}," new",[273,13797,12133],{"class":641},[273,13799,1054],{"class":1333},[273,13801,1479],{"class":860},[273,13803,1185],{"class":820},[273,13805,6245],{"class":860},[273,13807,2620],{"class":1333},[273,13809,2981],{"class":1199},[273,13811,2625],{"class":1333},[273,13813,13814],{"class":820},"?.",[273,13816,6889],{"class":860},[273,13818,2096],{"class":820},[273,13820,854],{"class":820},[273,13822,13823],{"class":289},"Validation failed",[273,13825,833],{"class":820},[273,13827,1463],{"class":1333},[273,13829,879],{"class":820},[273,13831,13832],{"class":275,"line":1548},[273,13833,13834],{"class":1333}," )\n",[273,13836,13837,13839,13842],{"class":275,"line":1554},[273,13838,3588],{"class":820},[273,13840,13841],{"class":2084}," else",[273,13843,839],{"class":820},[273,13845,13846,13848,13850,13852],{"class":275,"line":1591},[273,13847,13788],{"class":641},[273,13849,1054],{"class":1333},[273,13851,1479],{"class":860},[273,13853,1064],{"class":1333},[273,13855,13856],{"class":275,"line":1644},[273,13857,3663],{"class":820},[273,13859,13860],{"class":275,"line":1651},[273,13861,3471],{"class":820},[273,13863,13864],{"class":275,"line":1656},[273,13865,999],{"class":820},[273,13867,13868],{"class":275,"line":1678},[273,13869,1004],{"class":820},[99,13871,13873],{"id":13872},"performance-optimization","Performance Optimization",[127,13875,13877],{"id":13876},"compilation-speed","Compilation Speed",[263,13879,13881],{"className":811,"code":13880,"language":813,"meta":268,"style":268},"// tsconfig.json - for faster builds\n{\n \"compilerOptions\": {\n \"incremental\": true,\n \"tsBuildInfoFile\": \".tsbuildinfo\"\n },\n \"ts-node\": {\n \"transpileOnly\": true,\n \"files\": true\n }\n}\n",[270,13882,13883,13888,13892,13904,13917,13935,13939,13951,13964,13977,13981],{"__ignoreMap":268},[273,13884,13885],{"class":275,"line":276},[273,13886,13887],{"class":293},"// tsconfig.json - for faster builds\n",[273,13889,13890],{"class":275,"line":283},[273,13891,821],{"class":820},[273,13893,13894,13896,13898,13900,13902],{"class":275,"line":16},[273,13895,826],{"class":820},[273,13897,11262],{"class":829},[273,13899,833],{"class":820},[273,13901,836],{"class":820},[273,13903,839],{"class":820},[273,13905,13906,13908,13911,13913,13915],{"class":275,"line":308},[273,13907,844],{"class":820},[273,13909,13910],{"class":279},"incremental",[273,13912,833],{"class":820},[273,13914,836],{"class":820},[273,13916,11384],{"class":820},[273,13918,13919,13921,13924,13926,13928,13930,13933],{"class":275,"line":321},[273,13920,844],{"class":820},[273,13922,13923],{"class":279},"tsBuildInfoFile",[273,13925,833],{"class":820},[273,13927,836],{"class":820},[273,13929,854],{"class":820},[273,13931,13932],{"class":289},".tsbuildinfo",[273,13934,958],{"class":820},[273,13936,13937],{"class":275,"line":333},[273,13938,963],{"class":820},[273,13940,13941,13943,13945,13947,13949],{"class":275,"line":345},[273,13942,826],{"class":820},[273,13944,11085],{"class":829},[273,13946,833],{"class":820},[273,13948,836],{"class":820},[273,13950,839],{"class":820},[273,13952,13953,13955,13958,13960,13962],{"class":275,"line":357},[273,13954,844],{"class":820},[273,13956,13957],{"class":279},"transpileOnly",[273,13959,833],{"class":820},[273,13961,836],{"class":820},[273,13963,11384],{"class":820},[273,13965,13966,13968,13971,13973,13975],{"class":275,"line":370},[273,13967,844],{"class":820},[273,13969,13970],{"class":279},"files",[273,13972,833],{"class":820},[273,13974,836],{"class":820},[273,13976,3538],{"class":820},[273,13978,13979],{"class":275,"line":380},[273,13980,999],{"class":820},[273,13982,13983],{"class":275,"line":390},[273,13984,1004],{"class":820},[127,13986,13988],{"id":13987},"runtime-performance","Runtime Performance",[263,13990,13992],{"className":8887,"code":13991,"language":8889,"meta":268,"style":268},"// Use type assertions carefully\nconst user = data as User // Fast but unsafe\nconst user = data satisfies User // Type-safe validation\n\n// Prefer interfaces over types for objects\ninterface User {\n id: string\n name: string\n}\n\n// Use const assertions for immutable data\nconst config = {\n port: 3000,\n host: \"localhost\",\n} as const\n",[270,13993,13994,13999,14018,14036,14040,14045,14054,14062,14070,14074,14078,14083,14093,14105,14120],{"__ignoreMap":268},[273,13995,13996],{"class":275,"line":276},[273,13997,13998],{"class":293},"// Use type assertions carefully\n",[273,14000,14001,14003,14006,14008,14011,14013,14015],{"class":275,"line":283},[273,14002,1042],{"class":829},[273,14004,14005],{"class":860}," user ",[273,14007,1048],{"class":820},[273,14009,14010],{"class":860}," data ",[273,14012,12665],{"class":2084},[273,14014,12800],{"class":279},[273,14016,14017],{"class":293}," // Fast but unsafe\n",[273,14019,14020,14022,14024,14026,14028,14031,14033],{"class":275,"line":16},[273,14021,1042],{"class":829},[273,14023,14005],{"class":860},[273,14025,1048],{"class":820},[273,14027,14010],{"class":860},[273,14029,14030],{"class":2084},"satisfies",[273,14032,12800],{"class":279},[273,14034,14035],{"class":293}," // Type-safe validation\n",[273,14037,14038],{"class":275,"line":308},[273,14039,556],{"emptyLinePlaceholder":555},[273,14041,14042],{"class":275,"line":321},[273,14043,14044],{"class":293},"// Prefer interfaces over types for objects\n",[273,14046,14047,14050,14052],{"class":275,"line":333},[273,14048,14049],{"class":829},"interface",[273,14051,12800],{"class":279},[273,14053,839],{"class":820},[273,14055,14056,14058,14060],{"class":275,"line":345},[273,14057,13245],{"class":1333},[273,14059,836],{"class":820},[273,14061,13204],{"class":279},[273,14063,14064,14066,14068],{"class":275,"line":357},[273,14065,3381],{"class":1333},[273,14067,836],{"class":820},[273,14069,13204],{"class":279},[273,14071,14072],{"class":275,"line":370},[273,14073,1004],{"class":820},[273,14075,14076],{"class":275,"line":380},[273,14077,556],{"emptyLinePlaceholder":555},[273,14079,14080],{"class":275,"line":390},[273,14081,14082],{"class":293},"// Use const assertions for immutable data\n",[273,14084,14085,14087,14089,14091],{"class":275,"line":401},[273,14086,1042],{"class":829},[273,14088,5686],{"class":860},[273,14090,1048],{"class":820},[273,14092,839],{"class":820},[273,14094,14095,14098,14100,14103],{"class":275,"line":29},[273,14096,14097],{"class":1333}," port",[273,14099,836],{"class":820},[273,14101,14102],{"class":1199}," 3000",[273,14104,879],{"class":820},[273,14106,14107,14110,14112,14114,14116,14118],{"class":275,"line":420},[273,14108,14109],{"class":1333}," host",[273,14111,836],{"class":820},[273,14113,854],{"class":820},[273,14115,5724],{"class":289},[273,14117,833],{"class":820},[273,14119,879],{"class":820},[273,14121,14122,14124,14126],{"class":275,"line":430},[273,14123,1538],{"class":820},[273,14125,12671],{"class":2084},[273,14127,14128],{"class":829}," const\n",[99,14130,14132],{"id":14131},"monitoring-and-logging","Monitoring and Logging",[127,14134,14136],{"id":14135},"structured-logging","Structured Logging",[263,14138,14140],{"className":8887,"code":14139,"language":8889,"meta":268,"style":268},"// src/utils/logger.ts\nimport winston from \"winston\"\n\nexport const logger = winston.createLogger({\n level: process.env.LOG_LEVEL || \"info\",\n format: winston.format.combine(\n winston.format.timestamp(),\n winston.format.errors({ stack: true }),\n winston.format.json(),\n ),\n transports: [\n new winston.transports.File({ filename: \"error.log\", level: \"error\" }),\n new winston.transports.File({ filename: \"combined.log\" }),\n ...(process.env.NODE_ENV !== \"production\"\n ? [\n new winston.transports.Console({\n format: winston.format.simple(),\n }),\n ]\n : []),\n ],\n})\n",[270,14141,14142,14147,14163,14167,14190,14219,14240,14258,14286,14302,14308,14317,14369,14404,14428,14435,14454,14476,14485,14490,14500,14507],{"__ignoreMap":268},[273,14143,14144],{"class":275,"line":276},[273,14145,14146],{"class":293},"// src/utils/logger.ts\n",[273,14148,14149,14151,14154,14156,14158,14161],{"class":275,"line":283},[273,14150,3312],{"class":2084},[273,14152,14153],{"class":860}," winston ",[273,14155,3318],{"class":2084},[273,14157,854],{"class":820},[273,14159,14160],{"class":289},"winston",[273,14162,958],{"class":820},[273,14164,14165],{"class":275,"line":16},[273,14166,556],{"emptyLinePlaceholder":555},[273,14168,14169,14171,14173,14176,14178,14181,14183,14186,14188],{"class":275,"line":308},[273,14170,3371],{"class":2084},[273,14172,5647],{"class":829},[273,14174,14175],{"class":860}," logger ",[273,14177,1048],{"class":820},[273,14179,14180],{"class":860}," winston",[273,14182,1185],{"class":820},[273,14184,14185],{"class":641},"createLogger",[273,14187,1054],{"class":860},[273,14189,821],{"class":820},[273,14191,14192,14195,14197,14199,14201,14203,14205,14208,14210,14212,14215,14217],{"class":275,"line":321},[273,14193,14194],{"class":1333}," level",[273,14196,836],{"class":820},[273,14198,1182],{"class":860},[273,14200,1185],{"class":820},[273,14202,1188],{"class":860},[273,14204,1185],{"class":820},[273,14206,14207],{"class":860},"LOG_LEVEL ",[273,14209,1196],{"class":820},[273,14211,854],{"class":820},[273,14213,14214],{"class":289},"info",[273,14216,833],{"class":820},[273,14218,879],{"class":820},[273,14220,14221,14224,14226,14228,14230,14233,14235,14238],{"class":275,"line":333},[273,14222,14223],{"class":1333}," format",[273,14225,836],{"class":820},[273,14227,14180],{"class":860},[273,14229,1185],{"class":820},[273,14231,14232],{"class":860},"format",[273,14234,1185],{"class":820},[273,14236,14237],{"class":641},"combine",[273,14239,11998],{"class":860},[273,14241,14242,14245,14247,14249,14251,14254,14256],{"class":275,"line":345},[273,14243,14244],{"class":860}," winston",[273,14246,1185],{"class":820},[273,14248,14232],{"class":860},[273,14250,1185],{"class":820},[273,14252,14253],{"class":641},"timestamp",[273,14255,1629],{"class":860},[273,14257,879],{"class":820},[273,14259,14260,14262,14264,14266,14268,14270,14272,14274,14276,14278,14280,14282,14284],{"class":275,"line":357},[273,14261,14244],{"class":860},[273,14263,1185],{"class":820},[273,14265,14232],{"class":860},[273,14267,1185],{"class":820},[273,14269,6245],{"class":641},[273,14271,1054],{"class":860},[273,14273,1330],{"class":820},[273,14275,12486],{"class":1333},[273,14277,836],{"class":820},[273,14279,1340],{"class":1339},[273,14281,1343],{"class":820},[273,14283,1463],{"class":860},[273,14285,879],{"class":820},[273,14287,14288,14290,14292,14294,14296,14298,14300],{"class":275,"line":370},[273,14289,14244],{"class":860},[273,14291,1185],{"class":820},[273,14293,14232],{"class":860},[273,14295,1185],{"class":820},[273,14297,813],{"class":641},[273,14299,1629],{"class":860},[273,14301,879],{"class":820},[273,14303,14304,14306],{"class":275,"line":380},[273,14305,12048],{"class":860},[273,14307,879],{"class":820},[273,14309,14310,14313,14315],{"class":275,"line":390},[273,14311,14312],{"class":1333}," transports",[273,14314,836],{"class":820},[273,14316,1802],{"class":860},[273,14318,14319,14322,14324,14326,14329,14331,14334,14336,14338,14341,14343,14345,14348,14350,14352,14355,14357,14359,14361,14363,14365,14367],{"class":275,"line":401},[273,14320,14321],{"class":820}," new",[273,14323,14180],{"class":860},[273,14325,1185],{"class":820},[273,14327,14328],{"class":860},"transports",[273,14330,1185],{"class":820},[273,14332,14333],{"class":641},"File",[273,14335,1054],{"class":860},[273,14337,1330],{"class":820},[273,14339,14340],{"class":1333}," filename",[273,14342,836],{"class":820},[273,14344,854],{"class":820},[273,14346,14347],{"class":289},"error.log",[273,14349,833],{"class":820},[273,14351,1375],{"class":820},[273,14353,14354],{"class":1333}," level",[273,14356,836],{"class":820},[273,14358,854],{"class":820},[273,14360,1479],{"class":289},[273,14362,833],{"class":820},[273,14364,1343],{"class":820},[273,14366,1463],{"class":860},[273,14368,879],{"class":820},[273,14370,14371,14373,14375,14377,14379,14381,14383,14385,14387,14389,14391,14393,14396,14398,14400,14402],{"class":275,"line":29},[273,14372,14321],{"class":820},[273,14374,14180],{"class":860},[273,14376,1185],{"class":820},[273,14378,14328],{"class":860},[273,14380,1185],{"class":820},[273,14382,14333],{"class":641},[273,14384,1054],{"class":860},[273,14386,1330],{"class":820},[273,14388,14340],{"class":1333},[273,14390,836],{"class":820},[273,14392,854],{"class":820},[273,14394,14395],{"class":289},"combined.log",[273,14397,833],{"class":820},[273,14399,1343],{"class":820},[273,14401,1463],{"class":860},[273,14403,879],{"class":820},[273,14405,14406,14409,14412,14414,14416,14418,14420,14422,14424,14426],{"class":275,"line":420},[273,14407,14408],{"class":820}," ...",[273,14410,14411],{"class":860},"(process",[273,14413,1185],{"class":820},[273,14415,1188],{"class":860},[273,14417,1185],{"class":820},[273,14419,5989],{"class":860},[273,14421,12747],{"class":820},[273,14423,854],{"class":820},[273,14425,12752],{"class":289},[273,14427,958],{"class":820},[273,14429,14430,14433],{"class":275,"line":430},[273,14431,14432],{"class":820}," ?",[273,14434,1802],{"class":860},[273,14436,14437,14439,14441,14443,14445,14447,14450,14452],{"class":275,"line":440},[273,14438,13795],{"class":820},[273,14440,14180],{"class":860},[273,14442,1185],{"class":820},[273,14444,14328],{"class":860},[273,14446,1185],{"class":820},[273,14448,14449],{"class":641},"Console",[273,14451,1054],{"class":860},[273,14453,821],{"class":820},[273,14455,14456,14459,14461,14463,14465,14467,14469,14472,14474],{"class":275,"line":450},[273,14457,14458],{"class":1333}," format",[273,14460,836],{"class":820},[273,14462,14180],{"class":860},[273,14464,1185],{"class":820},[273,14466,14232],{"class":860},[273,14468,1185],{"class":820},[273,14470,14471],{"class":641},"simple",[273,14473,1629],{"class":860},[273,14475,879],{"class":820},[273,14477,14478,14481,14483],{"class":275,"line":460},[273,14479,14480],{"class":820}," }",[273,14482,1463],{"class":860},[273,14484,879],{"class":820},[273,14486,14487],{"class":275,"line":471},[273,14488,14489],{"class":860}," ]\n",[273,14491,14492,14495,14498],{"class":275,"line":743},[273,14493,14494],{"class":820}," :",[273,14496,14497],{"class":860}," [])",[273,14499,879],{"class":820},[273,14501,14502,14505],{"class":275,"line":749},[273,14503,14504],{"class":860}," ]",[273,14506,879],{"class":820},[273,14508,14509,14511],{"class":275,"line":771},[273,14510,1538],{"class":820},[273,14512,1064],{"class":860},[99,14514,14516],{"id":14515},"testing-strategy","Testing Strategy",[127,14518,14520],{"id":14519},"unit-tests-with-jest","Unit Tests with Jest",[263,14522,14524],{"className":8887,"code":14523,"language":8889,"meta":268,"style":268},"// src/services/__tests__/UserService.test.ts\nimport { UserService } from \"../UserService\"\nimport { UserRepository } from \"../../repositories/UserRepository\"\n\njest.mock(\"../../repositories/UserRepository\")\n\ndescribe(\"UserService\", () => {\n let userService: UserService\n let mockUserRepository: jest.Mocked\n\n beforeEach(() => {\n mockUserRepository = new UserRepository() as jest.Mocked\n userService = new UserService(mockUserRepository)\n })\n\n it(\"should create user successfully\", async () => {\n const userData = {\n email: \"test@example.com\",\n name: \"Test User\",\n password: \"password123\",\n }\n\n mockUserRepository.create.mockResolvedValue({\n id: \"1\",\n ...userData,\n createdAt: new Date(),\n updatedAt: new Date(),\n })\n\n const result = await userService.createUser(userData)\n\n expect(result).toBeDefined()\n expect(result.email).toBe(userData.email)\n expect(mockUserRepository.create).toHaveBeenCalledWith(userData)\n })\n})\n",[270,14525,14526,14531,14550,14569,14573,14592,14596,14617,14628,14652,14656,14669,14696,14714,14720,14724,14747,14758,14774,14790,14806,14810,14814,14831,14846,14855,14870,14885,14891,14895,14918,14922,14940,14968,14993,14999],{"__ignoreMap":268},[273,14527,14528],{"class":275,"line":276},[273,14529,14530],{"class":293},"// src/services/__tests__/UserService.test.ts\n",[273,14532,14533,14535,14537,14539,14541,14543,14545,14548],{"class":275,"line":283},[273,14534,3312],{"class":2084},[273,14536,2062],{"class":820},[273,14538,13422],{"class":860},[273,14540,1343],{"class":820},[273,14542,3355],{"class":2084},[273,14544,854],{"class":820},[273,14546,14547],{"class":289},"../UserService",[273,14549,958],{"class":820},[273,14551,14552,14554,14556,14558,14560,14562,14564,14567],{"class":275,"line":16},[273,14553,3312],{"class":2084},[273,14555,2062],{"class":820},[273,14557,12848],{"class":860},[273,14559,1343],{"class":820},[273,14561,3355],{"class":2084},[273,14563,854],{"class":820},[273,14565,14566],{"class":289},"../../repositories/UserRepository",[273,14568,958],{"class":820},[273,14570,14571],{"class":275,"line":308},[273,14572,556],{"emptyLinePlaceholder":555},[273,14574,14575,14577,14579,14582,14584,14586,14588,14590],{"class":275,"line":321},[273,14576,11776],{"class":860},[273,14578,1185],{"class":820},[273,14580,14581],{"class":641},"mock",[273,14583,1054],{"class":860},[273,14585,833],{"class":820},[273,14587,14566],{"class":289},[273,14589,833],{"class":820},[273,14591,1064],{"class":860},[273,14593,14594],{"class":275,"line":333},[273,14595,556],{"emptyLinePlaceholder":555},[273,14597,14598,14600,14602,14604,14607,14609,14611,14613,14615],{"class":275,"line":345},[273,14599,7193],{"class":641},[273,14601,1054],{"class":860},[273,14603,833],{"class":820},[273,14605,14606],{"class":289},"UserService",[273,14608,833],{"class":820},[273,14610,1375],{"class":820},[273,14612,1671],{"class":820},[273,14614,1466],{"class":829},[273,14616,839],{"class":820},[273,14618,14619,14621,14623,14625],{"class":275,"line":357},[273,14620,10211],{"class":829},[273,14622,13587],{"class":860},[273,14624,836],{"class":820},[273,14626,14627],{"class":279}," UserService\n",[273,14629,14630,14632,14635,14637,14640,14642,14645,14647,14650],{"class":275,"line":370},[273,14631,10211],{"class":829},[273,14633,14634],{"class":860}," mockUserRepository",[273,14636,836],{"class":820},[273,14638,14639],{"class":279}," jest",[273,14641,1185],{"class":820},[273,14643,14644],{"class":279},"Mocked",[273,14646,3075],{"class":820},[273,14648,14649],{"class":279},"UserRepository",[273,14651,3081],{"class":820},[273,14653,14654],{"class":275,"line":380},[273,14655,556],{"emptyLinePlaceholder":555},[273,14657,14658,14661,14663,14665,14667],{"class":275,"line":390},[273,14659,14660],{"class":641}," beforeEach",[273,14662,1054],{"class":1333},[273,14664,1629],{"class":820},[273,14666,1466],{"class":829},[273,14668,839],{"class":820},[273,14670,14671,14674,14676,14678,14680,14682,14684,14686,14688,14690,14692,14694],{"class":275,"line":401},[273,14672,14673],{"class":860}," mockUserRepository",[273,14675,1725],{"class":820},[273,14677,1623],{"class":820},[273,14679,12848],{"class":641},[273,14681,1637],{"class":1333},[273,14683,12665],{"class":2084},[273,14685,14639],{"class":279},[273,14687,1185],{"class":820},[273,14689,14644],{"class":279},[273,14691,3075],{"class":820},[273,14693,14649],{"class":279},[273,14695,3081],{"class":820},[273,14697,14698,14701,14703,14705,14707,14709,14712],{"class":275,"line":29},[273,14699,14700],{"class":860}," userService",[273,14702,1725],{"class":820},[273,14704,1623],{"class":820},[273,14706,13422],{"class":641},[273,14708,1054],{"class":1333},[273,14710,14711],{"class":860},"mockUserRepository",[273,14713,1064],{"class":1333},[273,14715,14716,14718],{"class":275,"line":420},[273,14717,2004],{"class":820},[273,14719,1064],{"class":1333},[273,14721,14722],{"class":275,"line":430},[273,14723,556],{"emptyLinePlaceholder":555},[273,14725,14726,14728,14730,14732,14735,14737,14739,14741,14743,14745],{"class":275,"line":440},[273,14727,7854],{"class":641},[273,14729,1054],{"class":1333},[273,14731,833],{"class":820},[273,14733,14734],{"class":289},"should create user successfully",[273,14736,833],{"class":820},[273,14738,1375],{"class":820},[273,14740,7251],{"class":829},[273,14742,1671],{"class":820},[273,14744,1466],{"class":829},[273,14746,839],{"class":820},[273,14748,14749,14751,14754,14756],{"class":275,"line":450},[273,14750,5148],{"class":829},[273,14752,14753],{"class":860}," userData",[273,14755,1725],{"class":820},[273,14757,839],{"class":820},[273,14759,14760,14763,14765,14767,14770,14772],{"class":275,"line":460},[273,14761,14762],{"class":1333}," email",[273,14764,836],{"class":820},[273,14766,854],{"class":820},[273,14768,14769],{"class":289},"test@example.com",[273,14771,833],{"class":820},[273,14773,879],{"class":820},[273,14775,14776,14779,14781,14783,14786,14788],{"class":275,"line":471},[273,14777,14778],{"class":1333}," name",[273,14780,836],{"class":820},[273,14782,854],{"class":820},[273,14784,14785],{"class":289},"Test User",[273,14787,833],{"class":820},[273,14789,879],{"class":820},[273,14791,14792,14795,14797,14799,14802,14804],{"class":275,"line":743},[273,14793,14794],{"class":1333}," password",[273,14796,836],{"class":820},[273,14798,854],{"class":820},[273,14800,14801],{"class":289},"password123",[273,14803,833],{"class":820},[273,14805,879],{"class":820},[273,14807,14808],{"class":275,"line":749},[273,14809,3471],{"class":820},[273,14811,14812],{"class":275,"line":771},[273,14813,556],{"emptyLinePlaceholder":555},[273,14815,14816,14818,14820,14822,14824,14827,14829],{"class":275,"line":784},[273,14817,14673],{"class":860},[273,14819,1185],{"class":820},[273,14821,12977],{"class":860},[273,14823,1185],{"class":820},[273,14825,14826],{"class":641},"mockResolvedValue",[273,14828,1054],{"class":1333},[273,14830,821],{"class":820},[273,14832,14833,14836,14838,14840,14842,14844],{"class":275,"line":789},[273,14834,14835],{"class":1333}," id",[273,14837,836],{"class":820},[273,14839,854],{"class":820},[273,14841,2517],{"class":289},[273,14843,833],{"class":820},[273,14845,879],{"class":820},[273,14847,14848,14850,14853],{"class":275,"line":1471},[273,14849,5233],{"class":820},[273,14851,14852],{"class":860},"userData",[273,14854,879],{"class":820},[273,14856,14857,14860,14862,14864,14866,14868],{"class":275,"line":1493},[273,14858,14859],{"class":1333}," createdAt",[273,14861,836],{"class":820},[273,14863,1623],{"class":820},[273,14865,1626],{"class":641},[273,14867,1629],{"class":1333},[273,14869,879],{"class":820},[273,14871,14872,14875,14877,14879,14881,14883],{"class":275,"line":1535},[273,14873,14874],{"class":1333}," updatedAt",[273,14876,836],{"class":820},[273,14878,1623],{"class":820},[273,14880,1626],{"class":641},[273,14882,1629],{"class":1333},[273,14884,879],{"class":820},[273,14886,14887,14889],{"class":275,"line":1543},[273,14888,2174],{"class":820},[273,14890,1064],{"class":1333},[273,14892,14893],{"class":275,"line":1548},[273,14894,556],{"emptyLinePlaceholder":555},[273,14896,14897,14899,14902,14904,14906,14908,14910,14912,14914,14916],{"class":275,"line":1554},[273,14898,5148],{"class":829},[273,14900,14901],{"class":860}," result",[273,14903,1725],{"class":820},[273,14905,3558],{"class":2084},[273,14907,13587],{"class":860},[273,14909,1185],{"class":820},[273,14911,13687],{"class":641},[273,14913,1054],{"class":1333},[273,14915,14852],{"class":860},[273,14917,1064],{"class":1333},[273,14919,14920],{"class":275,"line":1591},[273,14921,556],{"emptyLinePlaceholder":555},[273,14923,14924,14926,14928,14931,14933,14935,14938],{"class":275,"line":1644},[273,14925,8000],{"class":641},[273,14927,1054],{"class":1333},[273,14929,14930],{"class":860},"result",[273,14932,1463],{"class":1333},[273,14934,1185],{"class":820},[273,14936,14937],{"class":641},"toBeDefined",[273,14939,1170],{"class":1333},[273,14941,14942,14944,14946,14948,14950,14952,14954,14956,14958,14960,14962,14964,14966],{"class":275,"line":1651},[273,14943,8000],{"class":641},[273,14945,1054],{"class":1333},[273,14947,14930],{"class":860},[273,14949,1185],{"class":820},[273,14951,13495],{"class":860},[273,14953,1463],{"class":1333},[273,14955,1185],{"class":820},[273,14957,7331],{"class":641},[273,14959,1054],{"class":1333},[273,14961,14852],{"class":860},[273,14963,1185],{"class":820},[273,14965,13495],{"class":860},[273,14967,1064],{"class":1333},[273,14969,14970,14972,14974,14976,14978,14980,14982,14984,14987,14989,14991],{"class":275,"line":1656},[273,14971,8000],{"class":641},[273,14973,1054],{"class":1333},[273,14975,14711],{"class":860},[273,14977,1185],{"class":820},[273,14979,12977],{"class":860},[273,14981,1463],{"class":1333},[273,14983,1185],{"class":820},[273,14985,14986],{"class":641},"toHaveBeenCalledWith",[273,14988,1054],{"class":1333},[273,14990,14852],{"class":860},[273,14992,1064],{"class":1333},[273,14994,14995,14997],{"class":275,"line":1678},[273,14996,2004],{"class":820},[273,14998,1064],{"class":1333},[273,15000,15001,15003],{"class":275,"line":1707},[273,15002,1538],{"class":820},[273,15004,1064],{"class":860},[99,15006,9248],{"id":9247},[104,15008,15009],{},"TypeScript in Node.js production environments requires attention to build processes, error handling, and type safety. The investment in proper setup pays dividends in maintainability, developer experience, and runtime reliability.",[104,15011,15012],{},"Key takeaways:",[135,15014,15015,15018,15021,15024,15027],{},[138,15016,15017],{},"Use strict TypeScript configuration",[138,15019,15020],{},"Implement proper error handling with custom error classes",[138,15022,15023],{},"Leverage type-safe database access patterns",[138,15025,15026],{},"Optimize for both development and production builds",[138,15028,15029],{},"Maintain comprehensive testing coverage",[104,15031,15032],{},"With these practices, you'll build robust, scalable Node.js applications that are a joy to maintain and extend.",[9243,15034],{},[99,15036,15038],{"id":15037},"continue-your-typescript-journey","Continue Your TypeScript Journey",[104,15040,15041,15042,15045],{},"This post is part 2 of the ",[111,15043,15044],{},"TypeScript Mastery"," series. Continue learning:",[8634,15047,15048,15057,15071],{},[138,15049,15050,15056],{},[111,15051,15052],{},[8742,15053,15055],{"href":15054},"/blog/2-my-vscode-setup","My VSCode Setup"," - Development environment setup",[138,15058,15059,15065,15066,15070],{},[111,15060,15061],{},[8742,15062,15064],{"href":15063},"/blog/6-typescript-nodejs-production-guide","TypeScript + Node.js in Production"," ",[15067,15068,15069],"em",{},"(You are here)"," - Building production-ready TypeScript applications",[138,15072,15073,15079],{},[111,15074,15075],{},[8742,15076,15078],{"href":15077},"/blog/11-typescript-advanced-patterns","Advanced TypeScript Patterns"," - Enterprise-level TypeScript patterns and techniques",[4136,15081,15082],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":268,"searchDepth":283,"depth":283,"links":15084},[15085,15086,15090,15094,15098,15102,15106,15110,15113,15116,15117],{"id":11007,"depth":283,"text":11002},{"id":11013,"depth":283,"text":11014,"children":15087},[15088,15089],{"id":11017,"depth":16,"text":11018},{"id":11240,"depth":16,"text":11241},{"id":11613,"depth":283,"text":11614,"children":15091},[15092,15093],{"id":11617,"depth":16,"text":11618},{"id":8455,"depth":16,"text":8456},{"id":11926,"depth":283,"text":11927,"children":15095},[15096,15097],{"id":11930,"depth":16,"text":11931},{"id":12250,"depth":16,"text":12251},{"id":12611,"depth":283,"text":12612,"children":15099},[15100,15101],{"id":12615,"depth":16,"text":12616},{"id":12780,"depth":16,"text":12781},{"id":13167,"depth":283,"text":13168,"children":15103},[15104,15105],{"id":13171,"depth":16,"text":13172},{"id":13356,"depth":16,"text":13357},{"id":13872,"depth":283,"text":13873,"children":15107},[15108,15109],{"id":13876,"depth":16,"text":13877},{"id":13987,"depth":16,"text":13988},{"id":14131,"depth":283,"text":14132,"children":15111},[15112],{"id":14135,"depth":16,"text":14136},{"id":14515,"depth":283,"text":14516,"children":15114},[15115],{"id":14519,"depth":16,"text":14520},{"id":9247,"depth":283,"text":9248},{"id":15037,"depth":283,"text":15038},"2024-02-28","Learn how to build, deploy, and maintain TypeScript Node.js applications in production environments with best practices and real-world examples.","/img/blog/6-typescript-nodejs-production-guide/banner.svg",{"readingTime":15122},"18 min read",{"title":11002,"description":15119},"Master TypeScript from development setup to advanced enterprise patterns","blog/6-typescript-nodejs-production-guide",[15127,113,15128,15129],"TypeScript","Backend","Production","qfyychSTx6Wbs307TObROpK4RV8jhDc7CO-J4T2fblw",["Reactive",15132],{"$scolor-mode":15133,"$snuxt-seo-utils:routeRules":15135,"$stoasts":15136,"$ssite-config":15137},{"preference":15134,"value":15134,"unknown":555,"forced":8801},"system",{"head":-1,"seoMeta":-1},[],{"_priority":15138,"defaultLocale":15141,"description":15142,"env":12752,"indexable":555,"name":15143,"url":15144},{"env":15139,"indexable":15140,"url":15140,"name":15140,"description":15140,"defaultLocale":15140},-15,-3,"en","Senior Software Engineer specializing in modern web technologies. Crafting scalable web applications and browser extensions for startups and founders.","Muhammad Ubaid Raza - Full Stack Engineer","https://mubaidr.js.org",["Set"],["ShallowReactive",15147],{"blog-post-/blog/4-full-stack-development":-1,"profile-data":-1,"related-posts-/blog/4-full-stack-development":-1}]