Skip to content

Commit abe80b9

Browse files
committed
doc: developers composables
1 parent b25eb95 commit abe80b9

File tree

4 files changed

+244
-0
lines changed

4 files changed

+244
-0
lines changed

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export default defineConfig({
7272
{ text: 'Overview', link: '/developer-guide/index' },
7373
{ text: 'Development Setup', link: '/developer-guide/development-setup' },
7474
{ text: 'Architecture', link: '/developer-guide/architecture' },
75+
{ text: 'Composables', link: '/developer-guide/composables' },
7576
{ text: 'Database Internals', link: '/developer-guide/database-internals' },
7677
{ text: 'Server Utilities', link: '/developer-guide/server-utilities' },
7778
{ text: 'Testing', link: '/developer-guide/testing' },
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Composables Development Guide
2+
3+
This guide covers the development of composables in the Nuxt Users module, including important considerations for build-time compatibility and best practices.
4+
5+
## Overview
6+
7+
Composables in the Nuxt Users module provide reactive state management and API interactions. However, there are important considerations when developing composables that will be used in Vue components, especially regarding build-time execution.
8+
9+
## Build-Time Compatibility Issues
10+
11+
### The Problem
12+
13+
When Vue SFC (Single File Component) components are transformed during the module build process, any composables called at the module level (outside of lifecycle hooks) are executed during build time. This can cause issues if the composable uses Nuxt-specific functions like `useState()` when Nuxt is not yet available.
14+
15+
**Error Example:**
16+
```
17+
[Vue Router warn]: uncaught error during route navigation:
18+
ERROR [nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function.
19+
```
20+
21+
### Root Cause
22+
23+
The error occurs because:
24+
1. Vue SFC components are transformed during the module build process
25+
2. During this transformation, composables that use `useState()` or other Nuxt-specific functions are executed
26+
3. At build time, the Nuxt instance is not available, causing the error
27+
28+
### Which Composables Are Affected
29+
30+
**Composables that use `useState()` (need special handling):**
31+
- `useUsers()` - Uses `useState()` for global state management
32+
- `useAuthentication()` - Uses `useState()` for user state
33+
34+
**Composables that are safe (no special handling needed):**
35+
- `usePasswordValidation()` - Only uses Vue's `ref()` and `computed()`
36+
- `usePublicPaths()` - Only uses `useRuntimeConfig()` (Nuxt-provided)
37+
38+
**Nuxt-provided composables (always safe):**
39+
- `useRuntimeConfig()` - Designed to work during build process
40+
- `useRoute()` - Nuxt-provided
41+
- `useRouter()` - Nuxt-provided
42+
43+
## Solution: Lazy Initialization Pattern
44+
45+
For components that use composables with `useState()`, implement the lazy initialization pattern:
46+
47+
### Before (Problematic)
48+
```vue
49+
<script setup lang="ts">
50+
import { useUsers } from '../composables/useUsers'
51+
52+
// ❌ This causes build-time error
53+
const { users, pagination, loading, error, fetchUsers, removeUser } = useUsers()
54+
</script>
55+
```
56+
57+
### After (Fixed)
58+
```vue
59+
<script setup lang="ts">
60+
import { onMounted, computed } from 'vue'
61+
import { useUsers } from '../composables/useUsers'
62+
63+
// Initialize the composable state as null initially
64+
let usersComposable: ReturnType<typeof useUsers> | null = null
65+
66+
// Create computed properties that safely access the composable
67+
const users = computed(() => usersComposable?.users.value ?? [])
68+
const pagination = computed(() => usersComposable?.pagination.value ?? null)
69+
const loading = computed(() => usersComposable?.loading.value ?? false)
70+
const error = computed(() => usersComposable?.error.value ?? null)
71+
72+
onMounted(() => {
73+
// Initialize the composable only after the component is mounted
74+
usersComposable = useUsers()
75+
76+
// Fetch data if needed
77+
if (users.value.length === 0) {
78+
usersComposable.fetchUsers()
79+
}
80+
})
81+
82+
const handleFetchUsers = (page?: number, limit?: number) => {
83+
if (usersComposable) {
84+
usersComposable.fetchUsers(page, limit)
85+
}
86+
}
87+
</script>
88+
```
89+
90+
## Implementation Guidelines
91+
92+
### 1. Identify Affected Components
93+
94+
Check which components use composables that call `useState()`:
95+
96+
```bash
97+
grep -r "useUsers\|useAuthentication" src/runtime/components/
98+
```
99+
100+
### 2. Apply the Pattern
101+
102+
For each affected component:
103+
1. Move the composable call to `onMounted()`
104+
2. Create computed properties for reactive values
105+
3. Create wrapper functions for methods
106+
4. Add null checks where necessary
107+
108+
### 3. Keep Nuxt-Provided Composables at Module Level
109+
110+
Nuxt-provided composables like `useRuntimeConfig()` should remain at the module level:
111+
112+
```vue
113+
<script setup lang="ts">
114+
// ✅ Safe to keep at module level
115+
const { public: { nuxtUsers } } = useRuntimeConfig()
116+
117+
// ❌ Custom composable with useState needs lazy initialization
118+
let authComposable: ReturnType<typeof useAuthentication> | null = null
119+
120+
onMounted(() => {
121+
authComposable = useAuthentication()
122+
})
123+
</script>
124+
```
125+
126+
## Testing the Fix
127+
128+
After applying the fix, verify that:
129+
130+
1. **Build succeeds** - No more build-time errors
131+
2. **Components work correctly** - All functionality preserved
132+
3. **Reactivity maintained** - Computed properties update properly
133+
4. **Unit tests pass** - All existing tests continue to work
134+
135+
## Best Practices
136+
137+
### When Developing New Composables
138+
139+
1. **Prefer Vue primitives** - Use `ref()` and `computed()` when possible
140+
2. **Use `useState()` sparingly** - Only when you need global state
141+
3. **Document requirements** - Clearly indicate if a composable uses `useState()`
142+
4. **Test build compatibility** - Ensure composables work in both build and runtime
143+
144+
### When Using Composables in Components
145+
146+
1. **Check the source** - Look at what the composable uses internally
147+
2. **Apply lazy initialization** - For composables using `useState()`
148+
3. **Keep Nuxt composables at module level** - They're designed for it
149+
4. **Test thoroughly** - Ensure both build and runtime work correctly
150+
151+
## Common Patterns
152+
153+
### Pattern 1: Simple State Management
154+
```typescript
155+
// ✅ Safe - only uses Vue primitives
156+
export const useSimpleState = () => {
157+
const count = ref(0)
158+
const increment = () => count.value++
159+
return { count, increment }
160+
}
161+
```
162+
163+
### Pattern 2: Global State with useState
164+
```typescript
165+
// ⚠️ Requires lazy initialization in components
166+
export const useGlobalState = () => {
167+
const state = useState('global', () => ({ count: 0 }))
168+
const increment = () => state.value.count++
169+
return { state, increment }
170+
}
171+
```
172+
173+
### Pattern 3: Mixed Usage
174+
```typescript
175+
// ⚠️ Requires lazy initialization due to useState
176+
export const useMixedState = () => {
177+
const globalState = useState('global', () => ({ count: 0 }))
178+
const localState = ref({ loading: false })
179+
180+
return { globalState, localState }
181+
}
182+
```
183+
184+
## Troubleshooting
185+
186+
### Build Errors
187+
If you encounter build-time errors:
188+
1. Check which composables are being called at module level
189+
2. Identify which ones use `useState()`
190+
3. Apply the lazy initialization pattern
191+
4. Test the build again
192+
193+
### Runtime Errors
194+
If components don't work after the fix:
195+
1. Ensure computed properties are properly set up
196+
2. Check that null checks are in place
197+
3. Verify that methods are properly wrapped
198+
4. Test the component functionality
199+
200+
## Related Documentation
201+
202+
- [Architecture Guide](./architecture.md) - Overall module architecture
203+
- [Code Style Guide](./code-style.md) - Coding conventions
204+
- [Testing Guide](./testing.md) - Testing strategies
205+
- [User Guide - Composables](../user-guide/composables.md) - How to use composables

docs/developer-guide/index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ If you're looking to contribute to the Nuxt Users module, you'll find everything
2020
If you need to understand the module's internal architecture or extend its functionality:
2121

2222
- **[Architecture](./architecture.md)** - Comprehensive overview of the module's internal structure and design patterns
23+
- **[Composables](./composables.md)** - Guide to developing composables, including build-time compatibility issues and solutions
2324
- **[Database Internals](./database-internals.md)** - Deep dive into database utilities like `useDatabase()` and `useNuxtUsersDatabase()`
2425
- **[Server Utilities](./server-utilities.md)** - Documentation of internal server functions like `defineEventHandler()` and `getCookie()`
2526

@@ -34,13 +35,15 @@ This guide documents the internal APIs and utilities that power the Nuxt Users m
3435
- **API Handlers**: Internal event handlers and server-side logic
3536
- **Security Utilities**: Password hashing, token management, and security functions
3637
- **Migration System**: Database schema management and migration utilities
38+
- **Composables**: Reactive state management and API interactions
3739

3840
## Getting Started as a Developer
3941

4042
1. **Start with [Development Setup](./development-setup.md)** to configure your local environment
4143
2. **Read the [Architecture](./architecture.md)** documentation to understand the overall design
4244
3. **Review [Contributing Guidelines](./contributing.md)** before making changes
4345
4. **Check [Testing](./testing.md)** to learn how to validate your changes
46+
5. **Understand [Composables](./composables.md)** if you're working with reactive state management
4447

4548
## Difference from User Guide
4649

docs/user-guide/components.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,41 @@ The components automatically adapt to light and dark themes based on system pref
543543
</style>
544544
```
545545

546+
## Troubleshooting
547+
548+
### Build-Time Errors
549+
550+
If you encounter build-time errors like this:
551+
552+
```
553+
[Vue Router warn]: uncaught error during route navigation:
554+
ERROR [nuxt] A composable that requires access to the Nuxt instance was called outside of a plugin, Nuxt hook, Nuxt middleware, or Vue setup function.
555+
```
556+
557+
This is a known issue that has been fixed in the module. The error occurs when components use composables that require Nuxt to be available during the build process.
558+
559+
**Solution:** Update to the latest version of the module. If you're still experiencing issues, please report it as a bug.
560+
561+
**Note:** This issue only affects certain components (`NUsersList`, `NUsersUserCard`, `NUsersLogoutLink`, `NUsersProfileInfo`) and has been resolved by implementing lazy initialization of composables.
562+
563+
### Component Not Rendering
564+
565+
If a component is not rendering or appears empty:
566+
567+
1. **Check the console** for any JavaScript errors
568+
2. **Verify the component is properly imported** and registered
569+
3. **Ensure required props are provided** (check the component documentation)
570+
4. **Check that the API endpoints are accessible** and returning data
571+
572+
### Authentication Issues
573+
574+
If authentication components are not working:
575+
576+
1. **Verify your configuration** - Check that `apiBasePath` is correctly set
577+
2. **Check network requests** - Ensure API calls are reaching your server
578+
3. **Review server logs** - Look for any server-side errors
579+
4. **Test with the playground** - Try the components in the module's playground
580+
546581
## Next Steps
547582

548583
- **[Getting Started](/user-guide/getting-started)** - Learn how to set up authentication in your app

0 commit comments

Comments
 (0)