You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS+Prisma项目中后端概念是否消失?如何实现MVC架构?

NextJS + Prisma 场景下的后端概念与MVC架构实现

后端概念并未消失

NextJS是全栈框架,并非消除了后端概念,只是将前后端逻辑整合在同一个项目中。你依然需要处理数据校验、业务逻辑、数据库交互等后端工作,NextJS提供了多种承载后端逻辑的方式:

  • API路由:独立的后端接口,和传统RESTful API逻辑一致
  • Server Components:在服务器端运行的组件,可直接执行数据库查询等后端操作
  • Server Actions:App Router中推荐的异步数据处理方式,用于表单提交、数据修改等场景

这些方式本质都是后端逻辑的实现,只是不需要单独搭建和部署后端服务,由NextJS统一处理服务器端运行。

基于NextJS + Prisma 实现MVC架构

下面结合SQL/NoSQL数据库(Prisma支持PostgreSQL、MySQL、MongoDB等),拆解MVC各层的实现方式:

1. Model层:由Prisma定义与实现

Model层负责数据结构定义和数据库交互,完全由Prisma承担:

  • 首先在schema.prisma中定义数据模型,对应数据库表/集合:
// schema.prisma
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql" // 可替换为mysql、mongodb等
  url      = env("DATABASE_URL")
}

model Post {
  id        Int      @id @default(autoincrement())
  title     String   @db.VarChar(255)
  content   String?
  author    String   @db.VarChar(100)
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}
  • 运行npx prisma generate生成Prisma Client,这是Model层的核心工具,通过它执行CRUD操作,无需手动编写SQL/NoSQL语句。

2. Controller层:处理业务逻辑

Controller层负责接收请求、校验参数、调用Model层、处理业务规则,可根据场景选择以下实现方式:

方式一:API路由(Pages/App Router均支持)

适合需要对外提供RESTful接口的场景,比如供客户端组件调用或第三方系统集成:

// app/api/posts/route.js
import prisma from '@/lib/prisma';

// 获取文章列表
export async function GET() {
  const posts = await prisma.post.findMany({
    orderBy: { createdAt: 'desc' },
    select: { id: true, title: true, author: true, createdAt: true }
  });
  return Response.json(posts);
}

// 创建文章
export async function POST(request) {
  try {
    const { title, content, author } = await request.json();
    // 参数校验
    if (!title || !author) {
      return Response.json({ error: '标题和作者不能为空' }, { status: 400 });
    }
    const post = await prisma.post.create({
      data: { title, content, author }
    });
    return Response.json(post, { status: 201 });
  } catch (error) {
    return Response.json({ error: '创建失败' }, { status: 500 });
  }
}

方式二:Server Actions(App Router推荐)

适合处理表单提交、页面内数据操作等场景,无需单独编写API接口:

// app/actions/postActions.js
'use server';

import prisma from '@/lib/prisma';

export async function createPost(formData) {
  const title = formData.get('title');
  const content = formData.get('content');
  const author = formData.get('author');

  if (!title || !author) {
    throw new Error('标题和作者为必填项');
  }

  return await prisma.post.create({
    data: { title, content, author }
  });
}

export async function deletePost(postId) {
  return await prisma.post.delete({
    where: { id: parseInt(postId) }
  });
}

方式三:Service层(逻辑抽离)

如果业务逻辑复杂,可将Controller逻辑抽离为独立的service文件,保持代码整洁:

// app/services/postService.js
import prisma from '@/lib/prisma';

export async function getPostById(postId) {
  return await prisma.post.findUnique({
    where: { id: parseInt(postId) }
  });
}

export async function updatePost(postId, data) {
  return await prisma.post.update({
    where: { id: parseInt(postId) },
    data: { ...data }
  });
}

3. View层:NextJS页面与组件

View层负责渲染UI,展示Controller层返回的数据,分为两种类型:

  • Server Components:直接在服务器端获取数据并渲染,无需客户端请求,适合静态内容或数据敏感页面:
// app/posts/page.js
import { getAllPosts } from '@/services/postService';

export default async function PostsPage() {
  const posts = await getAllPosts();

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">博客文章</h1>
      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
        {posts.map(post => (
          <div key={post.id} className="border p-4 rounded">
            <h2 className="text-xl font-semibold">{post.title}</h2>
            <p className="text-gray-600 mt-1">作者:{post.author}</p>
            <p className="text-sm text-gray-500 mt-2">{post.createdAt.toLocaleString()}</p>
          </div>
        ))}
      </div>
    </div>
  );
}
  • Client Components:在客户端运行,可通过API请求或调用Server Actions与后端交互,适合需要交互的动态页面:
// app/posts/create/page.js
'use client';

import { createPost } from '@/actions/postActions';
import { useRouter } from 'next/navigation';

export default function CreatePostPage() {
  const router = useRouter();

  async function handleSubmit(formData) {
    try {
      await createPost(formData);
      router.push('/posts');
    } catch (error) {
      alert(error.message);
    }
  }

  return (
    <div className="container mx-auto p-4 max-w-md">
      <h1 className="text-2xl font-bold mb-4">发布新文章</h1>
      <form action={handleSubmit} className="flex flex-col gap-3">
        <input
          type="text"
          name="title"
          placeholder="文章标题"
          className="border p-2 rounded"
          required
        />
        <input
          type="text"
          name="author"
          placeholder="作者"
          className="border p-2 rounded"
          required
        />
        <textarea
          name="content"
          placeholder="文章内容"
          rows={5}
          className="border p-2 rounded"
        />
        <button type="submit" className="bg-blue-500 text-white p-2 rounded hover:bg-blue-600">
          发布
        </button>
      </form>
    </div>
  );
}

内容的提问来源于stack exchange,提问作者JustDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 10:35:39