+
-
-
Error while fetching feed 💔
-
- {{ article.title }} ({{ article.published ? 'Published' : 'Draft' }})
- By {{ article.author.name }}
- Unknown author
-
-
-
Publish
-
Delete
+
Loading post...
+
+
+
+
😔 Post not found
+
Back to Feed
+
+
+
+
+
+
+ {{ data.content }}
+
+
-
+
+
+
+ {{ isPublishing ? 'Publishing...' : '✓ Publish' }}
+
+
+
+ {{ isDeleting ? 'Deleting...' : '🗑 Delete' }}
+
+
+
-
diff --git a/orm/nuxt/app/pages/signup.vue b/orm/nuxt/app/pages/signup.vue
index dbe69c9bf73e..b726880ec7f2 100644
--- a/orm/nuxt/app/pages/signup.vue
+++ b/orm/nuxt/app/pages/signup.vue
@@ -1,68 +1,155 @@
-
+
diff --git a/orm/nuxt/app/types/index.ts b/orm/nuxt/app/types/index.ts
new file mode 100644
index 000000000000..506195e51828
--- /dev/null
+++ b/orm/nuxt/app/types/index.ts
@@ -0,0 +1,18 @@
+export interface User {
+ id: number
+ email: string
+ name: string | null
+}
+
+export interface Post {
+ id: number
+ createdAt: string
+ updatedAt: string
+ title: string
+ content: string | null
+ published: boolean
+ viewCount: number
+ author: User | null
+ authorId: number | null
+}
+
diff --git a/orm/nuxt/nuxt.config.ts b/orm/nuxt/nuxt.config.ts
index 0539dbbf8d93..fd0aed37d432 100644
--- a/orm/nuxt/nuxt.config.ts
+++ b/orm/nuxt/nuxt.config.ts
@@ -1,16 +1,24 @@
export default defineNuxtConfig({
- compatibilityDate: '2025-07-15',
+ compatibilityDate: '2024-11-01',
devtools: { enabled: true },
- modules: ['@nuxt/eslint'],
+ modules: ['@nuxt/eslint', '@nuxthub/core'],
+
app: {
head: {
- title: 'Nuxt-Prisma Example (TypeScript)',
+ title: 'Prisma Blog - Nuxt + Prisma Example',
+ htmlAttrs: {
+ lang: 'en'
+ },
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
- { name: 'description', content: '' },
+ { name: 'description', content: 'A modern blog built with Nuxt 4 and Prisma ORM' }
],
- link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
+ link: [
+ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
+ { rel: 'preconnect', href: 'https://fonts.googleapis.com' },
+ { rel: 'preconnect', href: 'https://fonts.gstatic.com', crossorigin: '' }
+ ]
}
}
-});
+})
\ No newline at end of file
diff --git a/orm/nuxt/package.json b/orm/nuxt/package.json
index 9b69803eda54..651bd01c9ff6 100644
--- a/orm/nuxt/package.json
+++ b/orm/nuxt/package.json
@@ -10,20 +10,21 @@
"postinstall": "nuxt prepare"
},
"dependencies": {
- "@nuxt/eslint": "1.9.0",
- "@prisma/client": "7.0.0",
- "@prisma/adapter-pg": "7.0.0",
- "eslint": "^9.0.0",
- "nuxt": "^4.1.2",
- "vue": "^3.5.22",
- "vue-router": "^4.5.1",
- "pg": "^8.16.3"
+ "@nuxt/eslint": "1.12.1",
+ "@nuxthub/core": "^0.10.1",
+ "@prisma/adapter-pg": "7.1.0",
+ "@prisma/client": "7.1.0",
+ "eslint": "^9.39.2",
+ "nuxt": "^4.2.2",
+ "pg": "^8.16.3",
+ "vue": "^3.5.25",
+ "vue-router": "^4.6.4"
},
"devDependencies": {
- "@types/node": "^24.6.1",
- "dotenv": "^17.2.1",
- "prisma": "7.0.0",
- "tsx": "^4.20.6",
- "@types/pg": "^8.15.6"
+ "@types/node": "^25.0.1",
+ "@types/pg": "^8.16.0",
+ "dotenv": "^17.2.3",
+ "prisma": "7.1.0",
+ "tsx": "^4.21.0"
}
}
diff --git a/orm/nuxt/server/api/drafts.get.ts b/orm/nuxt/server/api/drafts.get.ts
new file mode 100644
index 000000000000..e7d261f3a314
--- /dev/null
+++ b/orm/nuxt/server/api/drafts.get.ts
@@ -0,0 +1,16 @@
+export default defineEventHandler(async () => {
+ const drafts = await prisma.post.findMany({
+ where: {
+ published: false
+ },
+ include: {
+ author: true
+ },
+ orderBy: {
+ createdAt: 'desc'
+ }
+ })
+
+ return drafts
+})
+
diff --git a/orm/nuxt/server/api/feed.get.ts b/orm/nuxt/server/api/feed.get.ts
new file mode 100644
index 000000000000..a32525fbe0cf
--- /dev/null
+++ b/orm/nuxt/server/api/feed.get.ts
@@ -0,0 +1,16 @@
+export default defineEventHandler(async () => {
+ const posts = await prisma.post.findMany({
+ where: {
+ published: true
+ },
+ include: {
+ author: true
+ },
+ orderBy: {
+ createdAt: 'desc'
+ }
+ })
+
+ return posts
+})
+
diff --git a/orm/nuxt/server/api/posts/[id].delete.ts b/orm/nuxt/server/api/posts/[id].delete.ts
new file mode 100644
index 000000000000..acc311f6d710
--- /dev/null
+++ b/orm/nuxt/server/api/posts/[id].delete.ts
@@ -0,0 +1,19 @@
+export default defineEventHandler(async (event) => {
+ const id = getRouterParam(event, 'id')
+
+ if (!id) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: 'Post ID is required'
+ })
+ }
+
+ const post = await prisma.post.delete({
+ where: {
+ id: parseInt(id)
+ }
+ })
+
+ return post
+})
+
diff --git a/orm/nuxt/server/api/posts/[id].get.ts b/orm/nuxt/server/api/posts/[id].get.ts
new file mode 100644
index 000000000000..f33be5cd3e7f
--- /dev/null
+++ b/orm/nuxt/server/api/posts/[id].get.ts
@@ -0,0 +1,29 @@
+export default defineEventHandler(async (event) => {
+ const id = getRouterParam(event, 'id')
+
+ if (!id) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: 'Post ID is required'
+ })
+ }
+
+ const post = await prisma.post.findUnique({
+ where: {
+ id: parseInt(id)
+ },
+ include: {
+ author: true
+ }
+ })
+
+ if (!post) {
+ throw createError({
+ statusCode: 404,
+ statusMessage: 'Post not found'
+ })
+ }
+
+ return post
+})
+
diff --git a/orm/nuxt/server/api/posts/[id]/publish.put.ts b/orm/nuxt/server/api/posts/[id]/publish.put.ts
new file mode 100644
index 000000000000..b98ab5fb61f9
--- /dev/null
+++ b/orm/nuxt/server/api/posts/[id]/publish.put.ts
@@ -0,0 +1,25 @@
+export default defineEventHandler(async (event) => {
+ const id = getRouterParam(event, 'id')
+
+ if (!id) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: 'Post ID is required'
+ })
+ }
+
+ const post = await prisma.post.update({
+ where: {
+ id: parseInt(id)
+ },
+ data: {
+ published: true
+ },
+ include: {
+ author: true
+ }
+ })
+
+ return post
+})
+
diff --git a/orm/nuxt/server/api/posts/index.post.ts b/orm/nuxt/server/api/posts/index.post.ts
new file mode 100644
index 000000000000..92df6225f533
--- /dev/null
+++ b/orm/nuxt/server/api/posts/index.post.ts
@@ -0,0 +1,33 @@
+export default defineEventHandler(async (event) => {
+ const body = await readBody<{
+ title: string
+ content: string
+ authorEmail: string
+ }>(event)
+
+ if (!body.title || !body.authorEmail) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: 'Title and author email are required'
+ })
+ }
+
+ const post = await prisma.post.create({
+ data: {
+ title: body.title,
+ content: body.content ?? '',
+ published: false,
+ author: {
+ connect: {
+ email: body.authorEmail
+ }
+ }
+ },
+ include: {
+ author: true
+ }
+ })
+
+ return post
+})
+
diff --git a/orm/nuxt/server/api/users/check.post.ts b/orm/nuxt/server/api/users/check.post.ts
new file mode 100644
index 000000000000..e1e88a4311e5
--- /dev/null
+++ b/orm/nuxt/server/api/users/check.post.ts
@@ -0,0 +1,21 @@
+export default defineEventHandler(async (event) => {
+ const body = await readBody<{
+ email: string
+ }>(event)
+
+ if (!body.email) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: 'Email is required'
+ })
+ }
+
+ const user = await prisma.user.findUnique({
+ where: {
+ email: body.email
+ }
+ })
+
+ return { exists: !!user }
+})
+
diff --git a/orm/nuxt/server/api/users/index.post.ts b/orm/nuxt/server/api/users/index.post.ts
new file mode 100644
index 000000000000..f85301351198
--- /dev/null
+++ b/orm/nuxt/server/api/users/index.post.ts
@@ -0,0 +1,34 @@
+export default defineEventHandler(async (event) => {
+ const body = await readBody<{
+ name: string
+ email: string
+ }>(event)
+
+ if (!body.name || !body.email) {
+ throw createError({
+ statusCode: 400,
+ statusMessage: 'Name and email are required'
+ })
+ }
+
+ const existingUser = await prisma.user.findUnique({
+ where: { email: body.email }
+ })
+
+ if (existingUser) {
+ throw createError({
+ statusCode: 409,
+ statusMessage: 'User with this email already exists'
+ })
+ }
+
+ const user = await prisma.user.create({
+ data: {
+ name: body.name,
+ email: body.email
+ }
+ })
+
+ return user
+})
+
diff --git a/orm/nuxt/server/routes/author.ts b/orm/nuxt/server/routes/author.ts
deleted file mode 100644
index 1ecbd7e71c9d..000000000000
--- a/orm/nuxt/server/routes/author.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { prisma } from '../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- // https://nuxt.com/docs/guide/directory-structure/server#handling-requests-with-body
- const { email } = await readBody(event);
-
- try {
- const user = await prisma.user.findMany({
- where: {
- email: email
- }
- });
-
- return user;
- }
- catch(error) {
- console.error(error);
- }
-});
\ No newline at end of file
diff --git a/orm/nuxt/server/routes/draft-list.get.ts b/orm/nuxt/server/routes/draft-list.get.ts
deleted file mode 100644
index 3fea4effb8dc..000000000000
--- a/orm/nuxt/server/routes/draft-list.get.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { prisma } from '../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- const posts = await prisma.post.findMany({
- where: {
- published: false
- },
- include: {
- author: true
- }
- })
- .catch((error) => {
- console.error(error);
- });
-
- return posts;
-});
diff --git a/orm/nuxt/server/routes/feed.get.ts b/orm/nuxt/server/routes/feed.get.ts
deleted file mode 100644
index c189322a5bf8..000000000000
--- a/orm/nuxt/server/routes/feed.get.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { prisma } from '../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- const feed = await prisma.post.findMany({
- where: {
- published: true
- },
- include: {
- author: true
- }
- })
- .catch((error) => {
- console.error(error);
- });
-
- return feed;
-});
diff --git a/orm/nuxt/server/routes/filterPosts.get.ts b/orm/nuxt/server/routes/filterPosts.get.ts
deleted file mode 100644
index 22a1a269fb9d..000000000000
--- a/orm/nuxt/server/routes/filterPosts.get.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { prisma } from '../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- const { searchString } = getQuery(event);
-
- const draftPosts = await prisma.post.findMany({
- where: {
- OR: [
- {
- title: {
- //@ts-ignore
- contains: searchString,
- },
- },
- {
- content: {
- //@ts-ignore
- contains: searchString,
- },
- },
- ],
- },
- })
- .catch((error) => {
- console.error(error);
- });
-
- return draftPosts;
-});
diff --git a/orm/nuxt/server/routes/post/[id].delete.ts b/orm/nuxt/server/routes/post/[id].delete.ts
deleted file mode 100644
index 706e2a9e14de..000000000000
--- a/orm/nuxt/server/routes/post/[id].delete.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { prisma } from '../../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- const id = event.context.params.id;
-
- const deletePost = await prisma.post.delete({
- where: {
- //@ts-ignore
- id: parseInt(id)
- }
- })
- .catch((error) => {
- console.error(error);
- });
-
- return deletePost;
-});
\ No newline at end of file
diff --git a/orm/nuxt/server/routes/post/[id].get.ts b/orm/nuxt/server/routes/post/[id].get.ts
deleted file mode 100644
index 290a8ddd47e1..000000000000
--- a/orm/nuxt/server/routes/post/[id].get.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { prisma } from '../../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- const { context: { params: { id } } } = event;
-
- const getPost = await prisma.post.findUnique({
- where: {
- //@ts-ignore
- id: parseInt(id)
- },
- include: {
- author: true
- }
- })
- .catch((error) => {
- console.error(error);
- });
-
- return getPost;
-});
\ No newline at end of file
diff --git a/orm/nuxt/server/routes/post/index.ts b/orm/nuxt/server/routes/post/index.ts
deleted file mode 100644
index c84ddd5ce1a0..000000000000
--- a/orm/nuxt/server/routes/post/index.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { prisma } from '../../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- // https://nuxt.com/docs/guide/directory-structure/server#handling-requests-with-body
- const { title, content, authorEmail } = await readBody(event);
-
- const createPost = await prisma.post.create({
- data: {
- title,
- content,
- published: false,
- author: {
- connect: {
- email: authorEmail
- }
- }
- }
- })
- .catch((error) => {
- console.error(error);
- });
-
- return createPost;
-});
\ No newline at end of file
diff --git a/orm/nuxt/server/routes/publish/[id].put.ts b/orm/nuxt/server/routes/publish/[id].put.ts
deleted file mode 100644
index 63924f13ed2f..000000000000
--- a/orm/nuxt/server/routes/publish/[id].put.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { prisma } from '../../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- const id = event.context.params.id;
-
- const updatePost = await prisma.post.update({
- where: {
- id: parseInt(id)
- },
- data: {
- published: true
- }
- })
- .catch((error) => {
- console.error(error);
- });
-
- return updatePost;
-});
\ No newline at end of file
diff --git a/orm/nuxt/server/routes/user.ts b/orm/nuxt/server/routes/user.ts
deleted file mode 100644
index 0fb658878d7f..000000000000
--- a/orm/nuxt/server/routes/user.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { prisma } from '../../prisma/db'
-
-// https://nuxt.com/docs/guide/directory-structure/server
-export default defineEventHandler(async (event) => {
- // https://nuxt.com/docs/guide/directory-structure/server#handling-requests-with-body
- const { name, email } = await readBody(event);
-
- const createUser = await prisma.user.create({
- data: {
- name,
- email
- }
- })
- .catch((error) => {
- console.error(error);
- });
-
- return createUser;
-});
\ No newline at end of file
diff --git a/orm/nuxt/prisma/db.ts b/orm/nuxt/server/utils/db.ts
similarity index 76%
rename from orm/nuxt/prisma/db.ts
rename to orm/nuxt/server/utils/db.ts
index 37503712d093..b7e3fb3a4bba 100644
--- a/orm/nuxt/prisma/db.ts
+++ b/orm/nuxt/server/utils/db.ts
@@ -1,6 +1,5 @@
-// https://www.prisma.io/docs/guides/database/troubleshooting-orm/help-articles/nextjs-prisma-client-dev-practices
import { PrismaPg } from '@prisma/adapter-pg'
-import { PrismaClient } from './generated/client'
+import { PrismaClient } from '../../prisma/generated/client'
const prismaClientSingleton = () => {
const pool = new PrismaPg({ connectionString: process.env.DATABASE_URL! })