-
Notifications
You must be signed in to change notification settings - Fork 3
docs improvements - create your first crud step 1 #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds 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
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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this 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: Missingwhereclause in findMany.The
whereclause is defined but not applied to thefindManyquery 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 tutorialThe MDX examples inconsistently reference:
- Creating
app/features/project/schema.ts- Referencing
src/features/project/schemas.ts- Importing from
@/features/projects/schemaPick a single convention—e.g. use
app/features/project/schema.tsthroughout 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, useonly-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
⛔ Files ignored due to path filters (4)
src/assets/tutorials/create-your-first-crud/db-seed-result.pngis excluded by!**/*.pngsrc/assets/tutorials/create-your-first-crud/db-ui-1.pngis excluded by!**/*.pngsrc/assets/tutorials/create-your-first-crud/db-ui-2.pngis excluded by!**/*.pngsrc/assets/tutorials/create-your-first-crud/db-ui-3.pngis 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
satisfiesoperator withStarlightUserConfig["social"]provides better type safety while preserving the literal types of the array elements, compared to the previousas constapproach. 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.astroaligns with the TypeScript path mappings introduced intsconfig.json.
26-33: LGTM! Standard Prisma model structure.The
Projectmodel follows best practices with standard fields (id,createdAt,updatedAt) and a unique constraint on thenamefield.
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
projectresource 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 + 1items- Uses the extra item as the next cursor
- Pops the extra item before returning results
There was a problem hiding this 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:wheremissing infindMany(but present incount).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()vsz.string(), anddescriptionisoptional()in some andnullish()in others. Pick one convention (suggestid: z.string()anddescription: z.string().nullish()or enforcecuid()consistently) to avoid reader confusion.Also applies to: 351-358, 446-453, 496-506, 560-571, 633-644, 773-778
579-581: Optional: preferskipDuplicates: truewithcreateManyover 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.
| 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({}) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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.
| 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, | ||
| }); | ||
| }); | ||
| }), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| 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(), | ||
| }); | ||
| }); | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
zProjectandProjectfrom that file. - Import from
@/features/project/schemaeverywhere.
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.
Related to #66
Summary by CodeRabbit
Documentation
Chores