轻松搞定Next.js+Prisma全栈开发

今天的学习真是让我大开眼界啊!作为一个前端初学者,我一直觉得后端数据库操作是件很复杂的事情,但通过今天的实践,我发现用Next.js配合Prisma可以如此轻松地完成全栈开发。让我来好好回顾一下今天学到的内容,把这些知识牢牢地记在脑子里。

这是整体效果,我们所添加的数据,都是从数据库中操作的:

prisma是何方神圣

prisma是什么

一开始我接触到了Prisma这个神奇的工具。Prisma是什么呢?简单来说,它是一个ORM(对象关系映射)工具,让我们不需要直接写复杂的SQL语句,就能像操作普通JavaScript对象一样来操作数据库。这真是太方便了!想象一下,如果不用Prisma,我们要操作数据库就得写各种SQL查询语句,像什么SELECT、INSERT、UPDATE之类的,不仅容易出错,而且代码也很难维护。Prisma的出现让我们摆脱了这些烦恼,可以用更加直观的方式来处理数据。

prisma安装和初始化

安装Prisma很简单,只需要在项目里运行pnpm i prisma @prisma/client命令就可以了。这里安装了两个包:prisma是核心工具,提供了命令行功能;@prisma/client则是客户端库,让我们能在代码中实际使用Prisma的功能。

初始化Prisma是通过npx prisma init命令完成的。这个命令会创建一个prisma文件夹,里面包含一个schema.prisma文件,这是Prisma的核心配置文件。同时还会生成一个.env文件,用来存放环境变量,特别是数据库连接字符串。

当然连接数据库得先有数据库,这里我用得是小皮面板里面集成了数据库等各种环境,非常方便,一键式安装,不需要做任何配置:

说到数据库连接,我在.env文件中做了如下配置:

ini 复制代码
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

# The following `prisma+postgres` URL is similar to the URL produced by running a local Prisma Postgres 
# server with the `prisma dev` CLI command, when not choosing any non-default ports or settings. The API key, unlike the 
# one found in a remote Prisma Postgres URL, does not contain any sensitive information.

DATABASE_URL="mysql://用户:密码@localhost:3306/数据库名"

DATABASE_URL="mysql://用户:密码@localhost:3306/数据库名这个连接字符串告诉Prisma如何连接到我的MySQL数据库。它包含了用户名、密码、数据库服务器地址(localhost:3306)和数据库名。这样配置好后,Prisma就知道该往哪里读写数据了。

接下来是定义数据模型,这是在schema.prisma文件中完成的。我创建了一个Todo模型,用来表示待办事项:

kotlin 复制代码
model Todo {
  id        Int      @id @default(autoincrement())
  title     String
  completed Boolean  @default(false)
  createdAt DateTime @default(now())
}

这个模型定义了四个字段:id是主键,会自动递增;title是字符串类型,存储待办事项的内容;completed是布尔类型,表示是否已完成,默认是false;createdAt是日期时间类型,会自动设置为创建时间。这种定义方式非常直观,就像是在定义一个TypeScript接口一样,但实际上它背后会转换成数据库的表结构。

定义好模型后,需要运行npx prisma migrate dev --name init命令来创建迁移文件并执行迁移。迁移(Migration)是数据库结构变更的记录,比如建表、改字段等操作。Prisma会帮我们生成SQL语句并执行,同时记录下这些变更,这样团队协作时大家都能保持数据库结构的一致性。这一步真的很重要,它让数据库的版本控制变得像代码版本控制一样简单。

可以看到我们确实是成功建立todo这个表,里面的数据是我在后续的操作存入的

后端API开发

数据库层面准备好后,我开始着手API的开发。Next.js提供了App Router功能,让我们能很容易地创建API端点,不需要再像react-route-dom一样搭建路由。我在app/api/todos/route.ts文件中创建了处理待办事项相关请求的API。

获取待办事项

先来看看如何处理获取所有待办事项的GET请求:

javascript 复制代码
export async function GET() {
  const todos = await prisma.todo.findMany({
    orderBy: {
      createdAt: 'desc'
    }
  })
  return NextResponse.json(todos)
}

这段代码创建了一个Prisma客户端实例,然后使用findMany方法获取所有的待办事项。orderBy参数指定了按创建时间降序排列,这样最新的待办事项会显示在最前面。最后将结果以JSON格式返回给前端。

添加新待办事项

添加新待办事项的POST请求处理也很简单:

javascript 复制代码
export async function POST(req: Request) {
  const { title } = await req.json()
  const todo = await prisma.todo.create({
    data: {
      title,
    }
  })
  return NextResponse.json(todo)
}

这里从请求体中获取title字段,然后使用Prisma的create方法创建新的待办事项。Prisma会自动处理id、completed和createdAt字段的默认值,我们只需要提供title就可以了。创建成功后返回新创建的待办事项对象。

除了基本的获取和添加功能,我还实现了更新和删除待办事项的API。这些API放在app/api/todos/[id]/route.ts文件中,使用了动态路由来根据id操作特定的待办事项。

更新待办事项

更新待办事项(主要是切换完成状态)的PATCH请求处理:

javascript 复制代码
export async function PATCH(req: Request,
  { params }: { params: { id: string } }) {
  const { completed } = await req.json();
  const todo = await prisma.todo.update({
    where: { id: Number(params.id) },
    data: { completed }
  })
  return NextResponse.json(todo)
}

这里从路由参数中获取id,从请求体中获取completed状态,然后使用Prisma的update方法更新特定的待办事项。where条件指定了要更新哪条记录,data指定了要更新的字段。

删除待办事项

删除待办事项的DELETE请求处理:

javascript 复制代码
export async function DELETE(req: Request, 
  { params }: { params: { id: string } }) {
  await prisma.todo.delete({
    where: { id: Number(params.id) }
  })
  return NextResponse.json({ success: true })
}

同样从路由参数中获取id,然后使用Prisma的delete方法删除对应的记录。删除成功后返回一个简单的成功标志。

前端页面

前端页面部分,我创建了一个简单的待办事项列表页面。这个页面使用了React的useState和useEffect钩子来管理状态和副作用。

首先定义了Todo接口来规范数据类型:

typescript 复制代码
export interface Todo {
  id: number;
  title: string;
  completed: boolean;
  createdAt: string
}

获取数据

然后在组件中定义了状态和相关的处理函数:

scss 复制代码
export default function Home() {
  const [todos, setTodos] = useState<Todo[]>([]);
  const [input, setInput] = useState("");

  useEffect(() => {
    fetchTodos()
  }, [])

  // 其他函数...
}

todos状态用来存储待办事项列表,input状态用来存储输入框的内容。useEffect在组件挂载时调用fetchTodos函数获取数据。

fetchTodos函数通过调用我们之前创建的API来获取数据:

ini 复制代码
const fetchTodos = async () => {
  const res = await fetch("/api/todos");
  const data = await res.json();
  setTodos(data);
}

这里使用了原生的fetch API来发送请求,然后解析返回的JSON数据并更新状态。

添加数据

添加待办事项的函数稍微复杂一些:

javascript 复制代码
const addTodo = async () => {
  if (!input.trim()) return
  const res = await fetch('api/todos', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ title: input })
  })
  const newTodo = await res.json()
  setTodos([newTodo, ...todos])
  setInput('')
}

首先检查输入是否为空,然后发送POST请求到API,将新待办事项添加到数据库。成功后,将返回的新待办事项添加到列表的最前面,并清空输入框。

可以看到我们的数据是加入到了数据库的:

更新数据

切换待办事项完成状态的函数:

typescript 复制代码
const toogleTodo = async (id: number, completed: boolean) => {
  const res = await fetch(`api/todos/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ completed: !completed })
  })
  const updated = await res.json()
  setTodos(todos.map(todo => todo.id === id ? updated : todo))
}

发送PATCH请求更新特定待办事项的完成状态,然后更新本地状态以反映变化。

删除

删除待办事项的函数:

typescript 复制代码
const deleteTodo = async (id: number) => {
  await fetch(`api/todos/${id}`, {
    method: 'DELETE',
    headers: { 'Content-Type': 'application/json' }
  })
  setTodos(todos.filter(todo => todo.id !== id))
}

发送DELETE请求删除特定待办事项,然后从本地状态中移除该项。

Tailwind CSS

页面的UI部分使用了简单的HTML和Tailwind CSS类来样式化:

ini 复制代码
<main className=" max-w-xl max-auto p-6">
  <h1 className="text-2xl font-bold mb-4">Todos</h1>
  <div className="flex gap-2 mb-4">
    <input
      value={input}
      onChange={(e) => setInput(e.target.value)}
      placeholder="Add todo..."
      className="border p-2 rounded flex-1"
      type="text"
    />
    <button
      className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
      onClick={addTodo}
    >
      Add
    </button>
  </div>
  <ul className="space-y-2">
    {todos.map(todo => (
      <li key={todo.id} className="flex justify-between items-center p-2 border rounded">
        <span
          onClick={() => toogleTodo(todo.id, todo.completed)}
          className={`cursor-pointer select-none ${todo.completed ? 'line-through text-gray-500' : ''}`}
        >{todo.title}</span>
        <button
          onClick={() => deleteTodo(todo.id)}
          className="text-red-500 hover:text-red-700"
        >
          Delete
        </button>
      </li>
    ))}
  </ul>
</main>

有一个输入框和添加按钮用于添加新待办事项,还有一个列表显示所有待办事项。每个待办事项可以点击文本来切换完成状态(会有删除线效果),也可以点击删除按钮来删除。

总结

回顾整个开发过程,我发现Prisma+Next.js的组合真的很强大。Prisma让我们能够以类型安全的方式操作数据库,减少了出错的可能性,提高了开发效率。Next.js则提供了全栈开发的能力,前后端可以在同一个项目中协同工作,避免了跨项目协作的复杂性。

这种开发模式的好处是显而易见的:首先,数据类型前后一致,因为我们都使用TypeScript,前后端可以共享类型定义;其次,开发效率高,不需要频繁切换上下文;最后,部署简单,整个应用可以一起部署,减少了配置复杂度。

通过今天的学习,我对全栈开发有了更深入的理解。不再觉得后端开发是前端开发者的禁地,反而发现只要选对工具,后端开发也可以很直观、很高效。Prisma的模型定义、查询语法都非常直观,Next.js的API路由让创建后端接口变得简单易懂。

当然,这只是一个开始,还有很多高级特性等待我去探索,比如Prisma的关系查询、事务处理、聚合查询等,以及Next.js的服务器组件、中间件、缓存策略等。但今天的基础打得很扎实,让我有信心继续深入学习。

如果你也是前端开发者,想尝试全栈开发,我真的强烈推荐从Next.js+Prisma开始。这个组合入门门槛低,但能力强大,能够帮你快速构建完整的Web应用。今天的待办事项应用虽然简单,但涵盖了一个完整应用的核心功能:数据的增删改查。理解了这些基础,后续学习更复杂的功能就会容易得多。

好了,今天的回顾就到这里。希望通过这篇文章,不仅能帮自己巩固知识,也能给其他学习者一些启发。全栈开发并不难,关键在于选对工具和方法。Next.js+Prisma无疑是一个极佳的选择,值得每一个前端开发者学习和掌握。

相关推荐
pany15 分钟前
体验一款编程友好的显示器
前端·后端·程序员
Zuckjet20 分钟前
从零到百万:Notion如何用CRDT征服离线协作的终极挑战?
前端
ikonan25 分钟前
译:Chrome DevTools 实用技巧和窍门清单
前端·javascript
Juchecar25 分钟前
Vue3 v-if、v-show、v-for 详解及示例
前端·vue.js
ccc101829 分钟前
通过学长的分享,我学到了
前端
编辑胜编程29 分钟前
记录MCP开发表单
前端
可爱生存报告29 分钟前
vue3 vite quill-image-resize-module打包报错 Cannot set properties of undefined
前端·vite
__lll_29 分钟前
前端性能优化:Vue + Vite 全链路性能提升与打包体积压缩指南
前端·性能优化
weJee30 分钟前
pnpm原理
前端·前端工程化
小高00731 分钟前
⚡️ Vue 3.5 正式发布:10× 响应式性能、SSR 水合黑科技、告别 .value!
前端·javascript·vue.js