cd backend
npm run devBackend will start on http://localhost:3001
Test health check:
curl http://localhost:3001/api/healthIn a new terminal:
cd frontend
npm run devFrontend will start on http://localhost:3000
- Open browser:
http://localhost:3000 - Click "Check Backend Health" button
- You should see backend and database status
Use Neon MCP or create SQL migration:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);File: backend/src/data/repositories/user.repository.ts
import pool from '../db/pool';
export class UserRepository {
async findAll() {
const result = await pool.query('SELECT * FROM users');
return result.rows;
}
async create(data: { name: string; email: string }) {
const query = 'INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *';
const result = await pool.query(query, [data.name, data.email]);
return result.rows[0];
}
}
export default new UserRepository();File: backend/src/core/user.service.ts
import { z } from 'zod';
import userRepository from '../data/repositories/user.repository';
const createUserSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export class UserService {
async getAll() {
return await userRepository.findAll();
}
async create(data: any) {
const validated = createUserSchema.parse(data);
return await userRepository.create(validated);
}
}
export default new UserService();File: backend/src/app/routes/user.routes.ts
import { Router } from 'express';
import userService from '../../core/user.service';
const router = Router();
router.get('/users', async (req, res, next) => {
try {
const users = await userService.getAll();
res.json({ success: true, data: users });
} catch (error) {
next(error);
}
});
router.post('/users', async (req, res, next) => {
try {
const user = await userService.create(req.body);
res.status(201).json({ success: true, data: user });
} catch (error) {
next(error);
}
});
export default router;File: backend/src/app/routes/index.ts
import userRouter from './user.routes';
router.use('/api', userRouter);File: frontend/pages/users.vue
<template>
<div class="container">
<h1>Users</h1>
<ul>
<li v-for="user in users" :key="user.id">
{{ user.name }} - {{ user.email }}
</li>
</ul>
</div>
</template>
<script setup lang="ts">
const { fetchApi } = useApi()
const users = ref([])
onMounted(async () => {
const result = await fetchApi('/users')
if (result.success) {
users.value = result.data
}
})
</script>backend/
src/
app/routes/ ← HTTP endpoints (Presentation)
core/ ← Business logic (Business)
data/repositories/ ← Database queries (Data)
middleware/ ← Auth, validation, errors
types/ ← Shared types
frontend/
pages/ ← Nuxt pages (auto-routing)
components/ ← Vue components
composables/ ← Reusable logic (like useApi)
npm run dev # Development with hot reload
npm run build # Compile TypeScript
npm run start # Run productionnpm run dev # Development server
npm run build # Build for production
npm run preview # Preview production buildDATABASE_URL- PostgreSQL connection stringPORT- Server port (default: 3001)FRONTEND_URL- Frontend URL for CORS
NUXT_PUBLIC_API_BASE- Backend API URL
Backend won't start:
- Check if
.envfile exists - Verify
DATABASE_URLis correct - Check if port 3001 is available
Frontend won't connect:
- Ensure backend is running
- Check
NUXT_PUBLIC_API_BASEin frontend/.env - Verify CORS settings in backend
Database errors:
- Verify Neon project is active
- Check connection string has correct credentials
- Test connection with health check endpoint