Docs/getting started/快速开始

快速开始

让我们构建一个简单的功能来理解 NextShip 的工作原理。

理解技术栈

NextShip 使用以下核心技术:

层级技术用途
前端React 19 + Next.js 15UI 和路由
样式Tailwind CSS v4 + shadcn/ui组件和样式
APItRPC + Server Actions类型安全的 API 调用
数据库Drizzle ORM + PostgreSQL数据持久化
认证Better Auth用户认证

添加新页面

  1. src/app/[locale]/(dashboard)/ 中创建新文件:
// src/app/[locale]/(dashboard)/my-page/page.tsx
import { setRequestLocale } from "next-intl/server";
 
export default async function MyPage({
  params,
}: {
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  setRequestLocale(locale);
 
  return (
    <div className="container py-8">
      <h1 className="text-2xl font-bold">我的新页面</h1>
    </div>
  );
}

添加数据库表

  1. src/lib/db/schema.ts 中定义表结构:
export const projects = pgTable("projects", {
  id: text("id").primaryKey().$defaultFn(() => crypto.randomUUID()),
  name: text("name").notNull(),
  userId: text("user_id").references(() => users.id),
  createdAt: timestamp("created_at").defaultNow(),
});
  1. 推送更改:
pnpm db:push

创建 tRPC 路由

  1. src/server/trpc/routers/ 中添加新路由:
// src/server/trpc/routers/project.ts
import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "../trpc";
import { projects } from "@/lib/db/schema";
 
export const projectRouter = createTRPCRouter({
  list: protectedProcedure.query(async ({ ctx }) => {
    return ctx.db
      .select()
      .from(projects)
      .where(eq(projects.userId, ctx.user.id));
  }),
 
  create: protectedProcedure
    .input(z.object({ name: z.string().min(1) }))
    .mutation(async ({ ctx, input }) => {
      return ctx.db.insert(projects).values({
        name: input.name,
        userId: ctx.user.id,
      });
    }),
});
  1. 在根路由(src/server/trpc/router.ts)中注册。

在组件中使用 API

"use client";
 
import { trpc } from "@/lib/trpc/client";
 
export function ProjectList() {
  const { data: projects, isLoading } = trpc.project.list.useQuery();
 
  if (isLoading) return <div>加载中...</div>;
 
  return (
    <ul>
      {projects?.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  );
}

下一步