Design Patterns in Nuxt
I’ve been looking to improve my code quality and stumbled upon design patterns, but I’m not entirely sure how to apply them in a Nuxt project.
Right now, I feel frustrated having everything mixed together in my .vue files: API calls, business logic, state management… sometimes even pushed down into child components, which just makes things harder to maintain.
My current idea is to use composables for the business logic.
The plan would be for a composable to handle $fetch and update the Pinia state, giving each part a single responsibility. Components would then only handle the presentation and call the composable, essentially acting as a “controller.”
That said, I can see some drawbacks in this approach, and I wonder if it conflicts with Nuxt’s conventions.
I’d love to get some ideas and see how others structure their Nuxt projects. I’m trying to follow SOLID principles—mainly single responsibility—and ideally even apply dependency inversion, though that seems a bit tricky in this setup.
2 Replies
Hi @FoxForGate. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
Your instinct to use composables for business logic and API calls, keeping components focused on presentation, aligns well with Nuxt’s best practices and the broader Vue ecosystem. This approach helps you achieve the single responsibility principle and makes your codebase more maintainable and testable.
Key Patterns and Recommendations:
1. Composables for Business Logic and API Calls
- Nuxt encourages the use of composables (in
Components then call
Your approach—using composables for business logic and API calls, Pinia for state, and keeping components focused on presentation—is well-aligned with Nuxt’s conventions and best practices. This structure supports SOLID principles and is widely used in the Nuxt community. For dependency inversion, leverage plugins and the Nuxt app context to inject dependencies. This modular, layered approach will help you maintain a clean, scalable codebase. If you want to see more concrete examples, check out the Nuxt composables documentation and the custom $fetch/repository pattern guide. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
app/composables/) to encapsulate logic, including API calls, state management, and reusable utilities. This keeps your .vue files clean and focused on UI concerns.
- You can auto-import composables, making them available throughout your app without manual imports. This supports modularity and code reuse Nuxt composables directory.
2. State Management
- For global state, Nuxt provides the useState composable, which is SSR-friendly and reactive. For more complex needs, Pinia is the recommended state management library and integrates seamlessly with Nuxt State management in Nuxt.
- You can update Pinia stores from within composables, further separating concerns.
3. API Abstraction and Repository Pattern
- Abstracting API calls into composables or using the repository pattern is a common and recommended approach. You can create a custom $fetch instance (e.g., for authentication, base URLs, error handling) and expose it via a Nuxt plugin. Then, use this instance in your composables or repositories to keep API logic decoupled from components Custom $fetch and repository pattern.
- Example:Components then call
useAPI or similar composables, keeping them free of direct API logic.
4. SOLID Principles and Dependency Inversion
- While Nuxt’s plugin and composable system doesn’t provide classic dependency injection, you can achieve a form of dependency inversion by injecting dependencies (like custom API clients) via plugins and accessing them through useNuxtApp() Custom $fetch via plugin.
- This allows you to swap implementations (e.g., for testing or different environments) without changing your business logic.
5. Avoiding Common Pitfalls
- Don’t put composable or state logic outside of the proper context (e.g., outside setup() or <script setup>), as this can cause cross-request state pollution in SSR Auto-imports and context.
- Avoid overusing plugins for logic that could be a composable, as plugins run during hydration and can impact performance Performance best practices.
---
Summary:Your approach—using composables for business logic and API calls, Pinia for state, and keeping components focused on presentation—is well-aligned with Nuxt’s conventions and best practices. This structure supports SOLID principles and is widely used in the Nuxt community. For dependency inversion, leverage plugins and the Nuxt app context to inject dependencies. This modular, layered approach will help you maintain a clean, scalable codebase. If you want to see more concrete examples, check out the Nuxt composables documentation and the custom $fetch/repository pattern guide. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
<@404016877623443456> Kapa.ai is still learning and improving, please let me know how I did by reacting below.