Skip to content

Conversation

@yoannfleurydev
Copy link
Member

@yoannfleurydev yoannfleurydev commented Oct 12, 2025

Related to #66

Summary by CodeRabbit

  • Documentation

    • Updated homepage banner link and refreshed the tech stack list.
    • Overhauled the "Create your first CRUD" tutorial: clarified steps, updated examples and paths, improved seeding flow, added pagination/search guidance, refined API/router examples, and expanded permissions guidance.
    • Polished links, wording, and formatting across docs.
  • Chores

    • Added workspace configuration marking certain tools as built dependencies.
    • Added TypeScript path aliases to simplify imports.

@vercel
Copy link

vercel bot commented Oct 12, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
start-ui-web-docs Ready Ready Preview Comment Oct 12, 2025 8:49pm

@coderabbitai
Copy link

coderabbitai bot commented Oct 12, 2025

Walkthrough

Adds a pnpm workspace built-dependency entry for esbuild and sharp, introduces TypeScript path aliases, updates docs (homepage and CRUD tutorial) with import/path, schema, router, seed, and permissions changes, and aligns social link typing with Starlight config.

Changes

Cohort / File(s) Summary
Workspace configuration
pnpm-workspace.yaml
Adds onlyBuiltDependencies listing esbuild and sharp.
TypeScript config & aliases
tsconfig.json
Sets baseUrl and path aliases: @/components/*src/components/*, @/assets/*src/assets/*.
Docs: homepage updates
src/content/docs/index.mdx
Links “other contributors” to contributors page; revises and expands tech stack list and ordering.
Docs: CRUD tutorial
src/content/docs/tutorials/create-your-first-crud.mdx
Switches imports to aliases, updates step headings and Prisma seed/DB UI paths, moves examples from app/server to src/server, and adjusts router examples to new input/output shapes and handler signatures.
Server routers & feature schemas
src/server/routers/project.ts, src/server/router.ts, src/features/project/...
Relocates/exports zProject and Project types; updates project router export and getAll signature to accept { cursor?, limit?, searchTerm? } and return { items, nextCursor, total }; handler signature changed to (context, input).
Permissions & auth types
src/features/auth/permissions.ts
Adds project permissions and updates exported rolesNames, zRole, and Permission shapes to include project CRUD permissions.
Seeding & examples paths
src/prisma/seed*, src/server/* (examples)
Adjusts seed script and example paths to new locations; makes seed idempotent and updates referenced imports.
Starlight social links typing
src/content/links.tsx
Imports Starlight types and changes socials to use satisfies StarlightUserConfig["social"].

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant C as Client (UI)
  participant R as Project Router
  participant A as Auth/Permissions
  participant DB as Database

  U->>C: Request project list (page/search)
  rect rgba(200,220,255,0.25)
    C->>R: getAll({ cursor, limit, searchTerm })
    R->>A: Check permission: project.read
    A-->>R: Authorized / Denied
  end

  alt Authorized
    R->>DB: findMany({ cursor, take: limit, where: searchTerm })
    DB-->>R: items, total, nextCursor
    R-->>C: { items, nextCursor, total }
    C-->>U: Render list with pagination
  else Denied
    R-->>C: Error (403)
    C-->>U: Show access denied
  end
Loading
sequenceDiagram
  autonumber
  participant Dev as Developer
  participant CLI as pnpm CLI
  participant S as Seed Script
  participant DB as Database

  Dev->>CLI: pnpm db:seed
  CLI->>S: Run seed
  S->>DB: Read existing projects
  alt Missing data
    S->>DB: Create projects/users (idempotent)
  else Already present
    S-->>DB: No-op
  end
  DB-->>S: Success
  S-->>CLI: Log completion
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • ntatoud

Poem

I hop through code with tiny paws,
Aliases stitched and docs with claws.
Seeds that sprout and pages sing,
Paginated lists that neatly spring.
Typed links sparkle — merge, hooray! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “docs improvements - create your first crud step 1” uses a broad and generic “docs improvements” prefix and only partially describes the changes by referencing the tutorial step. It does not clearly or succinctly summarize the primary update, nor does it convey the additional documentation and configuration tweaks elsewhere in the PR. This makes it difficult to grasp the full scope of the change at a glance. Consider renaming the pull request to something more specific and concise, such as “Update ‘Create Your First CRUD’ tutorial step 1 and adjust documentation configs,” so that it clearly highlights the main tutorial changes and related doc updates.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch yoann/docs-improvements

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@yoannfleurydev yoannfleurydev self-assigned this Oct 12, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/content/docs/tutorials/create-your-first-crud.mdx (2)

637-650: Critical bug: Missing where clause in findMany.

The where clause is defined but not applied to the findMany query on line 646-650. This means the search will count filtered results but return unfiltered items, causing incorrect pagination and search behavior.

Apply this diff to fix:

         const [total, items] = await context.db.$transaction([
             context.db.project.count({ where }),
             context.db.project.findMany({
+            where,
             // Get an extra item at the end which we'll use as next cursor
             take: input.limit + 1,
             cursor: input.cursor ? { id: input.cursor } : undefined,
             }),
         ]);

708-723: Unify feature-folder and schema paths in tutorial

The MDX examples inconsistently reference:

  • Creating app/features/project/schema.ts
  • Referencing src/features/project/schemas.ts
  • Importing from @/features/projects/schema

Pick a single convention—e.g. use app/features/project/schema.ts throughout and import via @/features/project/schema.

🧹 Nitpick comments (1)
pnpm-workspace.yaml (1)

1-3: onlyBuiltDependencies is valid here; scope your workspace

  • PNPM supports the onlyBuiltDependencies (camelCase) field in git/pnpm-workspace.yaml; if preferred, use only-built-dependencies[] in ​.npmrc.
  • No top-level packages: globs defined—add your workspace package patterns or remove this file if unused.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a330a1 and 888b147.

⛔ Files ignored due to path filters (4)
  • src/assets/tutorials/create-your-first-crud/db-seed-result.png is excluded by !**/*.png
  • src/assets/tutorials/create-your-first-crud/db-ui-1.png is excluded by !**/*.png
  • src/assets/tutorials/create-your-first-crud/db-ui-2.png is excluded by !**/*.png
  • src/assets/tutorials/create-your-first-crud/db-ui-3.png is excluded by !**/*.png
📒 Files selected for processing (5)
  • pnpm-workspace.yaml (1 hunks)
  • src/content/docs/index.mdx (2 hunks)
  • src/content/docs/tutorials/create-your-first-crud.mdx (3 hunks)
  • src/content/links.tsx (2 hunks)
  • tsconfig.json (1 hunks)
🔇 Additional comments (9)
src/content/links.tsx (1)

1-1: LGTM! Modern TypeScript pattern.

The use of the satisfies operator with StarlightUserConfig["social"] provides better type safety while preserving the literal types of the array elements, compared to the previous as const approach. This ensures the socials array conforms to Starlight's expected structure.

Also applies to: 14-14

src/content/docs/index.mdx (2)

16-16: Good addition: Contributors link.

Adding the hyperlink to the contributors page improves discoverability and acknowledges community contributions.


29-29: Comprehensive tech stack documentation.

The expanded list of technologies provides users with a clear understanding of the starter's complete stack, which aids in decision-making and onboarding.

tsconfig.json (1)

4-10: LGTM! Standard path alias configuration.

The path aliases for @/components/* and @/assets/* will enable cleaner, more maintainable imports throughout the codebase. This is a common and recommended pattern for TypeScript projects.

src/content/docs/tutorials/create-your-first-crud.mdx (5)

5-5: LGTM! Consistent with path aliases.

The import path change from a relative path to the alias-based @/components/WorkInProgress.astro aligns with the TypeScript path mappings introduced in tsconfig.json.


26-33: LGTM! Standard Prisma model structure.

The Project model follows best practices with standard fields (id, createdAt, updatedAt) and a unique constraint on the name field.


83-119: LGTM! Idempotent seed implementation.

The seed function correctly checks for existing projects before creating new ones, making it safe to run multiple times. The filtering logic prevents duplicate entries.


165-220: LGTM! Comprehensive permission setup.

The BetterAuth permission configuration properly defines the project resource with all CRUD operations and assigns appropriate access levels to admin and user roles.


445-516: LGTM! Correct cursor-based pagination implementation.

The pagination logic correctly:

  • Fetches limit + 1 items
  • Uses the extra item as the next cursor
  • Pops the extra item before returning results

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/content/docs/tutorials/create-your-first-crud.mdx (1)

655-663: Search step: where missing in findMany (but present in count).

As written, total is filtered but items are not. Include where (and pagination fixes from earlier comment).

-            context.db.project.findMany({
+            context.db.project.findMany({
               // Get an extra item at the end which we'll use as next cursor
               take: input.limit + 1,
               cursor: input.cursor ? { id: input.cursor } : undefined,
+              skip: input.cursor ? 1 : 0,
+              orderBy: { id: "asc" },
+              where,
             }),
🧹 Nitpick comments (4)
src/content/docs/tutorials/create-your-first-crud.mdx (4)

359-364: Match the response to the output schema (select only needed fields).

You return full Project rows while the output schema lists only id, name, description. Select fields to avoid leaking extra properties and to keep validation strict.

-        .handler(async ({ context }) => {
-          context.logger.info("Getting projects from database");
-
-          return await context.db.project.findMany();
-        }),
+        .handler(async ({ context }) => {
+          context.logger.info("Getting projects from database");
+          return await context.db.project.findMany({
+            select: { id: true, name: true, description: true },
+          });
+        }),

318-324: Consistency: id and description constraints vary across steps.

Some snippets use z.string().cuid() vs z.string(), and description is optional() in some and nullish() in others. Pick one convention (suggest id: z.string() and description: z.string().nullish() or enforce cuid() consistently) to avoid reader confusion.

Also applies to: 351-358, 446-453, 496-506, 560-571, 633-644, 773-778


579-581: Optional: prefer skipDuplicates: true with createMany over manual filtering.

Your seed manually filters names before createMany. Prisma can handle duplicates.

-      const result = await db.project.createMany({
-          data: projects
-          .filter(
-              (project) =>
-              !existingProjects
-                  .map((existingProject) => existingProject.name)
-                  .includes(project.name)
-          )
-          .map(({ name }) => ({ name })),
-      });
+      const result = await db.project.createMany({
+        data: projects.map(({ name }) => ({ name })),
+        skipDuplicates: true,
+      });

Also applies to: 659-661


720-723: Minor: tRPC mention seems outdated; this guide uses oRPC.

Adjust wording to avoid mixing router tech.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7552fbe and 1af463c.

📒 Files selected for processing (1)
  • src/content/docs/tutorials/create-your-first-crud.mdx (10 hunks)

Comment on lines +392 to 398
z
.object({
cursor: z.string().cuid().optional(),
limit: z.number().min(1).max(100).default(20),
cursor: z.string().optional(),
limit: z.number().min(1).max(100).default(20),
})
.default({})
.prefault({})
)
Copy link

@coderabbitai coderabbitai bot Oct 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Typo: .prefault({}) is not a Zod method; use .default({}) (optionally with .partial()).

Without this fix, examples won’t compile. Replace all occurrences.

-            .prefault({})
+            .default({})

If you want to accept a fully omitted input object, consider:

-        z.object({ /* fields */ })
-            .default({})
+        z.object({ /* fields */ })
+            .partial()
+            .default({})

Also applies to: 438-444, 488-494, 552-558, 624-631

🤖 Prompt for AI Agents
In src/content/docs/tutorials/create-your-first-crud.mdx around lines 392-398
(and also apply the same change at 438-444, 488-494, 552-558, 624-631): the code
uses the non-existent Zod method `.prefault({})`; replace each `.prefault({})`
with `.default({})` (optionally chain `.partial()` on the object schema if you
want to accept an entirely omitted input object), ensuring you update every
occurrence in the listed ranges so the examples compile.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the Zod documentation about prefaults
curl -s "https://zod.dev/api" | grep -A 20 -B 5 "prefaults"

Length of output: 313867


🏁 Script executed:

#!/bin/bash
# Search for usage of prefault/prefaults in the codebase
rg -n "\.prefaults?\(" --type=ts --type=tsx -C 2

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Check package.json for zod version
cat package.json | jq '.dependencies.zod, .devDependencies.zod' 2>/dev/null || echo "Zod version not found in package.json"

Length of output: 9


@yoannfleurydev You’re right—Zod 4 adds .prefault(), and your usage is correct. Thanks for the link. Using .prefault({}) on the input object lets parsing continue so property defaults/transforms (e.g., limit.default(20)) still apply. Switching to .default({}) would short‑circuit and skip those, returning {} as-is.

I’ll retract that suggestion. If you want, I can resolve my earlier auto-comment on this thread—just say the word.

(=^‥^=)っ📝


✏️ Learnings added
Learnt from: yoannfleurydev
PR: BearStudio/start-ui-web-docs#67
File: src/content/docs/tutorials/create-your-first-crud.mdx:392-398
Timestamp: 2025-10-12T21:28:00.911Z
Learning: Zod v4 supports `.prefault()` (pre-parse default). In this repository’s docs/tutorials, `.prefault({})` is intentionally used on input object schemas so property-level defaults and transforms (e.g., `limit.default(20)`) are still applied during parsing. Do not suggest replacing with `.default({})`, which short-circuits parsing.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +457 to 462
return await context.db.project.findMany({
// Get an extra item at the end which we'll use as next cursor
take: input.limit + 1,
cursor: input.cursor ? { id: input.cursor } : undefined,
});
});
}),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Cursor pagination: add stable ordering and skip the cursor item; also include where in findMany (search step).

Current code may duplicate the cursor item and produce nondeterministic pages. In the search step, results aren’t filtered.

Apply these changes to each findMany call that uses cursor pagination:

-          return await context.db.project.findMany({
-            // Get an extra item at the end which we'll use as next cursor
-            take: input.limit + 1,
-            cursor: input.cursor ? { id: input.cursor } : undefined,
-          });
+          return await context.db.project.findMany({
+            // Get an extra item at the end which we'll use as next cursor
+            take: input.limit + 1,
+            cursor: input.cursor ? { id: input.cursor } : undefined,
+            skip: input.cursor ? 1 : 0,
+            orderBy: { id: "asc" },
+          });

And in the search-enabled handler, also pass where:

-            context.db.project.findMany({
+            context.db.project.findMany({
               // Get an extra item at the end which we'll use as next cursor
               take: input.limit + 1,
               cursor: input.cursor ? { id: input.cursor } : undefined,
+              skip: input.cursor ? 1 : 0,
+              orderBy: { id: "asc" },
+              where,
             }),

Also applies to: 510-515, 575-583, 655-663

🤖 Prompt for AI Agents
In src/content/docs/tutorials/create-your-first-crud.mdx around lines 457-462
(and also update the similar cursor-using findMany calls at 510-515, 575-583,
655-663): the cursor pagination is missing a stable order and does not skip the
cursor item (and the search handler is not passing the where filter). For each
findMany that uses cursor pagination, add a deterministic orderBy (e.g.,
orderBy: { id: 'asc' }), include skip: 1 so the cursor item is not duplicated,
and continue to take input.limit + 1 for next-cursor detection; in the
search-enabled handler(s) also pass the computed where clause into findMany so
results are filtered by the search criteria.

Comment on lines +722 to 735
Let's create the `src/features/project/schema.ts` file with the zod schema for one project.

```ts filename="app/features/project/schema.ts" showLineNumbers
```ts filename="src/features/project/schema.ts"
import { z } from "zod";

import { zu } from "@/lib/zod/zod-utils";

export const zProject = () =>
z.object({
id: z.string().cuid(),
z.object({
id: z.string(),
name: zu.string.nonEmpty(z.string()),
description: z.string().nullish(),
});
});
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Inconsistent feature paths and duplicated schema definitions. Consolidate and align imports.

You create both src/features/project/schema.ts and src/features/project/schemas.ts, then import from @/features/projects/schema (plural). This will confuse readers and break path-alias imports.

Recommended:

  • Use a single file: src/features/project/schema.ts.
  • Export both zProject and Project from that file.
  • Import from @/features/project/schema everywhere.

Example:

// src/features/project/schema.ts
import { z } from "zod";
import { zu } from "@/lib/zod/zod-utils";

export const zProject = () =>
  z.object({
    id: z.string(), // or z.string().cuid() if you want to enforce cuid
    name: zu.string.nonEmpty(z.string()),
    description: z.string().nullish(),
  });

export type Project = z.infer<ReturnType<typeof zProject>>;

And update the import in the router example:

- import { zProject } from "@/features/projects/schema";
+ import { zProject } from "@/features/project/schema";

Also applies to: 739-751, 755-761

🤖 Prompt for AI Agents
In src/content/docs/tutorials/create-your-first-crud.mdx around lines 722-735
(and also apply the same changes noted at 739-751 and 755-761), you have
inconsistent feature paths and duplicated schema files; consolidate into a
single file src/features/project/schema.ts that exports both zProject and the
Project type (inferred from ReturnType<typeof zProject>), remove or stop
referencing src/features/project/schemas.ts, and update all imports in the
document and code examples to import from '@/features/project/schema' (singular)
including the router example so all path-aliases are correct and readers won't
be confused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant