Frontend Development 13 min read

Building a Full‑Stack Todo List with Next.js, Prisma, and Server‑Side Rendering

This article explains how front‑end developers can expand into full‑stack development by using Next.js with server‑side rendering, Prisma ORM, and SQLite to create a functional Todo List application, covering installation, database schema, server actions, and client‑side form handling.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Building a Full‑Stack Todo List with Next.js, Prisma, and Server‑Side Rendering

In 2024 front‑end development is no longer limited to UI components; mastering TypeScript and server‑side rendering (SSR) frameworks like Next.js is becoming essential. The article argues that front‑end engineers can broaden their skill set by handling back‑end tasks with Node.js, especially for lightweight internal tools, blogs, and simple services.

It lists typical scenarios where a front‑end developer can apply SSR: corporate websites with CMS, blog systems, internal tool platforms, stable lightweight services (e.g., psychological tests, online utilities), and some outsourcing projects.

The advantages of modern SSR frameworks are highlighted:

Improved SEO and high‑performance page access.

Reduced front‑end bundle size by keeping libraries on the server.

Built‑in database connectivity for simple single‑database workloads.

Ability to ship a single full‑stack package to clients, lowering adoption cost.

Strong platform support (e.g., Vercel) even without dedicated servers.

To demonstrate these concepts, a Todo List project is built with Next.js and Prisma.

Installation of Next.js:

npx create-next-app@latest

What is your project named? todo
Would you like to use TypeScript? No / Yes #yes
Would you like to use ESLint? No / Yes # no
Would you like to use Tailwind CSS? No / Yes #yes
Would you like to use `src/` directory? No / Yes #yes
Would you like to use App Router? (recommended) No / Yes # yes
Would you like to customize the default import alias (@/*)? No / Yes # yes
What import alias would you like configured? @/*

Install Prisma and Day.js:

npm i prisma dayjs -D
npx prisma init --datasource-provider sqlite # you can choose other DBs

SQLite is used as a lightweight relational database. After initialization, the prisma/schema.prisma file defines the Todo model:

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Todo {
  id      Int @id @default(autoincrement())
  name    String
  time    String
  created DateTime @default(now())
}

Running prisma db push creates dev.db and installs @prisma/client for database access.

Next, a singleton Prisma client is exported ( src/app/prisma.ts ) to avoid multiple connections during development:

import { PrismaClient } from '@prisma/client'

const prismaClientSingleton = () => new PrismaClient()

type PrismaClientSingleton = ReturnType
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClientSingleton }
const prisma = globalForPrisma.prisma ?? prismaClientSingleton()
export default prisma
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

A client‑side form component ( src/app/form.tsx ) is created with 'use client' to enable React interactivity:

'use client'

import { useCallback } from "react"
import { addTodo } from "./data"

export function Form() {
  const submit = useCallback(async (e: React.MouseEvent
) => {
    e.preventDefault()
    const formData = new FormData(e.target as HTMLFormElement)
    await addTodo({
      name: formData.get('name') as string,
      time: formData.get('time') as string
    })
    location.reload()
  }, [])
  return (
添加
)
}

The server‑side action that writes to the database ( src/app/data.ts ) uses Next.js Server Action feature:

'use server'

import prisma from "./prisma"

export const addTodo = async (data: {name: string, time: string}) => {
  await prisma.todo.create({ data })
}

The main page ( src/app/page.tsx ) fetches todos on the server, formats dates with Day.js, and renders the list together with the Form component:

import dayjs from "dayjs"
import { Form } from "./form"
import prisma from "./prisma"

const getTodos = () => prisma.todo.findMany({ select: { id: true, name: true, time: true, created: true } })

export default async function Home() {
  const todos = await getTodos()
  return (
{todos.map(item => (
{item.time}
执行
{item.name}
创建时间:
{dayjs(item.created).format('YYYY-MM-DD HH:mm')}
))}
)
}

Because the page component is a server component by default, the database query runs on the server, keeping the client bundle small. The final UI shows a list of todos and a form to add new items, all rendered server‑side.

The article concludes that with tools like Next.js and Prisma, front‑end developers can quickly build full‑stack applications without needing heavyweight back‑end frameworks such as NestJS, Docker, or distributed databases, enabling rapid delivery of value to companies and clients.

TypeScriptNode.jsSQLiteServer-side RenderingNext.jsfull-stackPrisma
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.