agent-rules-kit
Version:
Bootstrap of **Cursor** rules (`.mdc`) and mirror documentation (`.md`) for AI agent-guided projects.
420 lines (335 loc) • 9.89 kB
Markdown
---
description: Standard Architecture for Nuxt applications
globs: <root>/nuxt.config.js,<root>/nuxt.config.ts,<root>/pages/**/*.vue,<root>/pages/**/*.js,<root>/pages/**/*.ts,<root>/components/**/*.vue,<root>/components/**/*.js,<root>/components/**/*.ts
alwaysApply: false
---
This document outlines the standard architecture pattern for Nuxt.js applications in {projectPath}.
The standard Nuxt architecture follows this directory structure:
```
{projectPath}/
├── assets/
│ ├── css/
│ └── images/
├── components/
│ ├── common/
│ ├── ui/
│ └── features/
├── composables/
├── content/
├── layouts/
│ ├── default.vue
│ └── error.vue
├── middleware/
├── pages/
├── plugins/
├── public/
├── server/
│ ├── api/
│ └── middleware/
├── stores/
├── types/
├── utils/
├── app.vue
├── error.vue
├── nuxt.config.js
└── package.json
```
In the standard architecture, components are organized as follows:
```
components/
├── common/
│ ├── AppHeader.vue
│ ├── AppFooter.vue
│ └── AppSidebar.vue
├── ui/
│ ├── BaseButton.vue
│ ├── BaseCard.vue
│ ├── BaseInput.vue
│ └── BaseModal.vue
└── features/
├── auth/
│ ├── LoginForm.vue
│ └── SignupForm.vue
├── products/
│ ├── ProductCard.vue
│ ├── ProductList.vue
│ └── ProductDetail.vue
└── checkout/
├── CheckoutForm.vue
└── PaymentOptions.vue
```
The standard data flow in a Nuxt application follows this pattern:
1. **API Layer**: Server routes or external APIs
2. **State Management**: Pinia/Vuex stores or composables
3. **Component Logic**: Page or component setup/data
4. **UI Layer**: Templates and rendered HTML
For Nuxt 3:
```ts
// pages/products/[id].vue
<script setup>
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)
</script>
```
For Nuxt 2:
```js
// pages/products/_id.vue
export default {
async asyncData({ params, $axios }) {
const product = await $axios.$get(`/api/products/${params.id}`);
return { product };
},
};
```
```ts
// stores/products.js
export const useProductStore = defineStore('products', {
state: () => ({
items: [],
loading: false,
}),
actions: {
async fetchProducts() {
this.loading = true;
try {
this.items = await $fetch('/api/products');
} finally {
this.loading = false;
}
},
},
getters: {
featuredProducts: (state) => state.items.filter((p) => p.featured),
},
});
```
```js
// store/products.js
export const state = () => ({
items: [],
loading: false,
});
export const mutations = {
SET_PRODUCTS(state, products) {
state.items = products;
},
SET_LOADING(state, status) {
state.loading = status;
},
};
export const actions = {
async fetchProducts({ commit }) {
commit('SET_LOADING', true);
try {
const products = await this.$axios.$get('/api/products');
commit('SET_PRODUCTS', products);
} finally {
commit('SET_LOADING', false);
}
},
};
```
```ts
// server/api/products/index.get.ts
export default defineEventHandler(async (event) => {
// Connect to database or external API
const products = await db.products.findMany();
return products;
});
// server/api/products/[id].get.ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id');
const product = await db.products.findUnique({ where: { id } });
if (!product) {
throw createError({
statusCode: 404,
message: `Product with ID ${id} not found`,
});
}
return product;
});
```
```js
// server-middleware/api/products.js
const express = require('express');
const app = express();
app.use(express.json());
app.get('/products', async (req, res) => {
// Handle request
res.json([
/* products */
]);
});
app.get('/products/:id', async (req, res) => {
// Handle request
res.json({ id: req.params.id, name: 'Product' });
});
module.exports = app;
```
1. User submits credentials
2. Credentials validated by server
3. JWT/session stored
4. Protected routes check auth status
5. Redirect if unauthenticated
```ts
// composables/useAuth.js
export const useAuth = () => {
const user = useState('user', () => null);
const isAuthenticated = computed(() => !!user.value);
async function login(credentials) {
const response = await $fetch('/api/auth/login', {
method: 'POST',
body: credentials,
});
user.value = response.user;
// You might store token in cookie/localStorage
// Or use auth.js/NuxtAuthHandler
return navigateTo('/dashboard');
}
function logout() {
user.value = null;
// Clear stored tokens
return navigateTo('/login');
}
return {
user,
isAuthenticated,
login,
logout,
};
};
```
```ts
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const user = useState('user');
if (!user.value && to.path !== '/login') {
return navigateTo('/login');
}
});
```
```js
// middleware/auth.js
export default function ({ store, redirect, route }) {
if (!store.state.auth.user && route.path !== '/login') {
return redirect('/login');
}
}
```
Organize composables by feature or functionality:
```
composables/
├── auth/
│ ├── useAuth.js
│ └── usePermissions.js
├── products/
│ ├── useProducts.js
│ └── useCategories.js
└── ui/
├── useBreakpoints.js
├── useToast.js
└── useForm.js
```
```
tests/
├── components/
│ ├── BaseButton.test.js
│ └── ProductCard.test.js
├── composables/
│ └── useAuth.test.js
├── pages/
│ └── index.test.js
└── server/
└── products.test.js
```
Example component test:
```js
import { describe, it, expect } from 'vitest';
import { mount } from '@vue/test-utils';
import BaseButton from '~/components/ui/BaseButton.vue';
describe('BaseButton', () => {
it('renders the button with the correct text', () => {
const wrapper = mount(BaseButton, {
slots: {
default: 'Click me',
},
});
expect(wrapper.text()).toContain('Click me');
});
it('emits click event when clicked', async () => {
const wrapper = mount(BaseButton);
await wrapper.trigger('click');
expect(wrapper.emitted('click')).toBeTruthy();
});
});
```
Organize your Nuxt configuration by logical groups:
```js
// nuxt.config.js
export default defineNuxtConfig({
// Core configuration
app: {
head: {
title: 'My Nuxt App',
meta: [{ name: 'description', content: 'My Nuxt App Description' }],
},
},
// Module configuration
modules: ['@pinia/nuxt', '@nuxt/content', '@nuxtjs/tailwindcss'],
// Feature configuration
content: {
// Content module options
},
// Environment configuration
runtimeConfig: {
apiSecret: process.env.API_SECRET,
public: {
apiBase: process.env.API_BASE || '/api',
},
},
// Build configuration
nitro: {
// Server options
},
vite: {
// Vite options
},
});
```
1. **Consistent Component Organization**
- Group by feature or type depending on project size
- Use consistent naming conventions
2. **State Management Modularity**
- Divide stores by domain/feature
- Avoid a single monolithic store
3. **Minimalist Component Design**
- Keep components focused on single responsibilities
- Extract reusable logic to composables
4. **Server/Client Code Separation**
- Clearly distinguish server-only and client-only code
- Use appropriate server utilities for sensitive operations
5. **Typed Interfaces**
- Define and use TypeScript interfaces for data structures
- Enforce consistent data shapes across the application