Skip to content

Conversation

@mikan3rd
Copy link
Owner

@mikan3rd mikan3rd commented Jan 7, 2026

fix #374

Summary by CodeRabbit

Release Notes

  • New Features

    • Added post edit history logging for better transparency
  • Improvements

    • Enhanced timestamp tracking to distinguish original post creation from updates
    • Fixed "Updated" badge to accurately reflect when posts have been edited
  • API Changes

    • Content field is now required when updating posts

✏️ Tip: You can customize this high-level summary in your review settings.

- Added a new post_logs table to track post creation details.
- Updated post creation logic to include a public_id and log the post details upon creation.
- Modified post update logic to ensure proper handling of public_id for querying and deletion.
- Added the post ID to the values inserted into the post_logs table to enhance tracking of post creation details.
- Introduced public_id generation during user creation in the userApp.
- Updated related tests to include public_id in user insertion for consistency.
- Modified the post update schema to require the content field.
- Removed the updated_at field assertions from tests to reflect schema changes.
- Updated the user_id assignment in post creation logic for consistency with the new structure.
@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

Walkthrough

This PR removes the updated_at column from the database schema and introduces postLogsTable for post history tracking. Posts now use first_created_at for original creation time, with the API transforming database fields to response fields (created_at from first_created_at, updated_at from created_at or null). Post creation and updates are now transactional with logging. User signup and post endpoints now generate public_id identifiers.

Changes

Cohort / File(s) Summary
Database Schema
packages/backend_app/src/db/schema.ts, packages/backend_app/src/db/field.ts
Removed updated_at from shared timestamps object; added first_created_at to postsTable; introduced new postLogsTable with id, public_id, user_id, content, and timestamps; removed defaultRandom() from public_id field generation; updated public field definitions for posts to include first_created_at and remove updated_at.
Post DTOs & Transformations
packages/backend_app/src/apps/posts/dto.ts
Introduced centralized postSchema defining post shape (public_id, content, created_at, updated_at); added transformPost function mapping first_created_at → created_at and created_at → updated_at; added transformPostWithUser combining post transform with user field; updated postWithUserResponseSchema and response schemas to use new structure; made content required in update request schema.
Post Routes & Logic
packages/backend_app/src/apps/posts/index.ts
Replaced post mapping with transformPostWithUser calls; introduced transactional create flow with postLogsTable insertion; rewrote update flow to delete and re-insert post while preserving first_created_at, with postLogs logging; updated fetch logic to use transformPostWithUser for responses.
Post Tests
packages/backend_app/src/apps/posts/index.test.ts
Added postLogsTable imports and validation; updated test data to include first_created_at on post inserts; expanded assertions to verify postLogs entries; added public_id to user inserts; updated response expectations to map first_created_at → created_at and created_at → updated_at.
OpenAPI Specification
packages/backend_app/openapi.json
Updated Post schema descriptions (created_at: "originally created", updated_at: "created or last updated in case of delete&insert"); removed additionalProperties: false constraints; added required: ["content"] to POST/PUT operations; removed additionalProperties from nested user schemas in responses.
User Routes & Tests
packages/backend_app/src/apps/user/index.ts, packages/backend_app/src/apps/user/index.test.ts, packages/backend_app/src/middlewares/user/index.test.ts
Added crypto.randomUUID() generation for user public_id field in signup route and test fixtures; updated user test setup to populate public_id field.
Frontend Client Types
packages/frontend_app/src/client/index.schemas.ts
Made PutPostsPublicIdBody.content field required (removed optional ?); updated associated documentation comments.
PostCard Component
packages/frontend_app/src/components/pages/post/components/PostCard/index.tsx, index.stories.tsx
Changed Post.updated_at type from string to string | null; updated isUpdated logic to check updated_at !== null instead of date comparison; updated badge and date row rendering to use null check; changed mock factory default for updated_at to null.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • PR #272: Modifies post/user identifiers from numeric id to public_id across database, DTOs, routes, tests, and frontend types—directly parallels this PR's public_id changes.
  • PR #51: Modifies timestamp field declarations in packages/backend_app/src/db/schema.ts, specifically around updated_at handling—overlaps with schema changes in this PR.
  • PR #138: Modifies the post update route logic in packages/backend_app/src/apps/posts/index.ts—intersects with this PR's rewritten transactional update flow.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR successfully addresses issue #374 by removing the updated_at column from the posts schema and implementing alternative tracking mechanisms.
Out of Scope Changes check ✅ Passed All changes are directly related to removing updated_at and implementing alternative tracking; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch delete_update_at_column

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.

… logic

- Cleaned up the user creation logic by directly generating public_id during insertion.
- Updated related test files to remove FIXME comments and ensure consistency in public_id handling.
- Replaced crypto.randomUUID() with faker.string.uuid() for public_id generation in user and post test files.
- Ensured consistency in public_id handling across user and post creation logic in tests.
…st cases

- Removed the updated_at field from the post schema and updated the required fields accordingly.
- Modified test cases to reflect the changes in the post schema, ensuring consistency in content field requirements.
- Replaced crypto.randomUUID() with faker.string.uuid() for public_id generation in post test files.
- Ensured consistency in public_id handling across post creation logic in tests.
- Enhanced the post update logic to insert relevant post details into the post_logs table after a successful update.
- This addition improves tracking and auditing of post updates by capturing essential information such as public_id and content.
- Updated post test cases to include assertions for the post_logs table, ensuring that post details are logged correctly upon post creation and updates.
- Enhanced the test logic to verify the presence and correctness of logged post information, including public_id, user_id, and content.
- Updated the post creation logic to generate public_id directly within the values insertion, ensuring consistency and clarity in the code.
- This change simplifies the transaction process by removing the separate declaration of public_id.
- Introduced the updated_at field in the post schema to track the last modification timestamp.
- Updated relevant test cases to include assertions for the updated_at field, ensuring accurate verification of post details.
- Adjusted post creation logic to set the updated_at field upon post creation.
- Introduced the updated_at field in the post schema to track the last modification timestamp.
- Updated the required fields to include updated_at, ensuring comprehensive post detail tracking.
- Added the updated_at field to the post response object to ensure that the last modification timestamp is included when retrieving post details.
- Modified test cases to set the updated_at field to null instead of expecting a string, ensuring alignment with the current post response structure.
…components

- Modified the updated_at field in the Post interface to accept null values, reflecting the possibility of no updates.
- Adjusted related components and mock response functions to accommodate the updated field type, ensuring consistent handling of post update timestamps.
- Updated the mock post creation function in PostCard stories to set the updated_at field to null, aligning with the recent schema changes that allow null values for this field.
…ted logic

- Added first_created_at field to the post schema to track the original creation timestamp.
- Updated post creation and response logic to handle first_created_at, transforming it to created_at for consistency.
- Adjusted tests to ensure first_created_at is correctly set and maintained during post operations.
…t and remove updated_at

- Refactored the post schema to include first_created_at and removed the updated_at field, aligning with recent changes.
- Updated mock data generation in response functions to reflect the new schema, ensuring consistency in post creation and retrieval.
- Adjusted related types to streamline the post structure and improve clarity in the codebase.
…ng structure

- Introduced a new "post" schema definition to encapsulate post properties, including public_id, content, first_created_at, and created_at.
- Updated the post response structure to utilize the new schema, ensuring a more organized and maintainable codebase.
- Removed redundant property definitions in favor of schema references, streamlining the overall schema design.
…_at field

- Enhanced the post schema to include the updated_at field, ensuring accurate tracking of post modifications.
- Adjusted response structures and mock data to reflect the new schema, maintaining consistency across the application.
- Removed the first_created_at field from the schema and updated required fields accordingly, streamlining the post data model.
…ions

- Removed the previous response schema definitions and replaced them with transformation functions to convert database post data into the API response format.
- Introduced `transformPost` and `transformPostWithUser` functions to enhance clarity and maintainability in response handling.
- Updated routes to utilize the new transformation functions, ensuring consistent response structures across post operations.
@mikan3rd mikan3rd marked this pull request as ready for review January 18, 2026 05:04
@coderabbitai coderabbitai bot changed the title @coderabbit Add post history tracking and restructure timestamps Jan 18, 2026
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

🤖 Fix all issues with AI agents
In `@packages/backend_app/src/apps/posts/dto.ts`:
- Around line 6-41: postSchema currently requires updated_at and transformPost
always sets it, preventing signaling "never updated"; make updated_at nullable
in postSchema (allow null in the OpenAPI schema) and update transformPost to
return updated_at as null when the post was never edited by comparing timestamps
(use millisecond-safe comparison like first_created_at.getTime() ===
created_at.getTime()) otherwise return the ISO string; reference postSchema and
transformPost when making these changes and ensure the OpenAPI metadata reflects
nullability.

In `@packages/backend_app/src/db/schema.ts`:
- Around line 37-43: The post_logs table defines id as integer().primaryKey()
but should be auto-generated like other tables; update postLogsTable's id column
to use generatedAlwaysAsIdentity() (i.e., change id to
integer().primaryKey().generatedAlwaysAsIdentity()) so inserts no longer require
manual id assignment and align schema behavior with other tables (refer to
postLogsTable, "post_logs", and the id column).

In
`@packages/frontend_app/src/components/pages/post/components/PostCard/index.tsx`:
- Around line 27-28: The code treats updated_at as nullable which makes
isUpdated truthy for every post; in the PostCard component keep updated_at as a
non-null string (remove the "| null" from the local type declaration or align
with generated client types) and change the updated check to compare timestamps
(set isUpdated = post.updated_at !== post.created_at) so the "Updated"
badge/date only renders when updated_at differs from created_at; update any
usages that assume nullable updated_at accordingly (symbols: updated_at,
created_at, isUpdated, PostCard).

Comment on lines +6 to 41
export const postSchema = z
.object({
public_id: z.uuid().openapi({
description: "Public ID",
example: "123e4567-e89b-12d3-a456-426614174000",
}),
content: (schema) =>
schema.openapi({
content: z.string().openapi({
description: "The content of the post",
example: "test",
}),
created_at: (schema) =>
schema.openapi({
description: "The date and time the post was created",
created_at: z.iso.datetime().openapi({
description: "The date and time the post was originally created",
example: "2025-01-01T00:00:00Z",
format: "date-time",
}),
updated_at: (schema) =>
schema.openapi({
description: "The date and time the post was updated",
updated_at: z.iso.datetime().openapi({
description:
"The date and time the post was created (or last updated in case of delete&insert)",
example: "2025-01-01T00:00:00Z",
format: "date-time",
}),
})
.pick(postPublicFields)
.strict()
})
.openapi("post");

export const postWithUserSelectSchema = postSelectSchema.extend({
user: userSelectSchema,
// DB データから API レスポンス形式への変換関数
export const transformPost = (post: {
public_id: string;
content: string;
first_created_at: Date;
created_at: Date;
}): z.infer<typeof postSchema> => ({
public_id: post.public_id,
content: post.content,
created_at: post.first_created_at.toISOString(), // first_created_at → created_at
updated_at: post.created_at.toISOString(), // created_at → updated_at
});
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the file exists and examine its content
if [ -f "packages/backend_app/src/apps/posts/dto.ts" ]; then
  wc -l packages/backend_app/src/apps/posts/dto.ts
  echo "---"
  cat -n packages/backend_app/src/apps/posts/dto.ts
else
  echo "File not found. Searching for dto files..."
  find . -name "dto.ts" -type f 2>/dev/null | grep -i post
fi

Repository: mikan3rd/hono-next-example

Length of output: 3473


🏁 Script executed:

#!/bin/bash
# Check if zod supports nullable chaining
# Also search for any tests or usages of transformPost
rg "transformPost" --type ts --type js -A 3 -B 3

Repository: mikan3rd/hono-next-example

Length of output: 3431


🏁 Script executed:

#!/bin/bash
# Look for database schema or models that define the post structure
fd -t f -e ts -e js | xargs grep -l "first_created_at\|created_at" 2>/dev/null | head -20

Repository: mikan3rd/hono-next-example

Length of output: 378


🏁 Script executed:

#!/bin/bash
# Check the database schema to understand first_created_at vs created_at
cat -n packages/backend_app/src/db/schema.ts | grep -A 30 "postsTable\|first_created_at\|created_at"

Repository: mikan3rd/hono-next-example

Length of output: 1160


🏁 Script executed:

#!/bin/bash
# Check test file to understand expected behavior
wc -l packages/backend_app/src/apps/posts/index.test.ts
head -100 packages/backend_app/src/apps/posts/index.test.ts

Repository: mikan3rd/hono-next-example

Length of output: 3348


🏁 Script executed:

#!/bin/bash
# Check if created_at is ever explicitly set to first_created_at or if timestamps differ in practice
rg "created_at" packages/backend_app/src/apps/posts/ -A 2 -B 2 | head -40

Repository: mikan3rd/hono-next-example

Length of output: 2687


🏁 Script executed:

#!/bin/bash
# Check the zod version and confirm nullable() support
cat packages/backend_app/package.json | grep -A 5 '"@hono/zod-openapi"'

Repository: mikan3rd/hono-next-example

Length of output: 263


updated_at always populated; consider nullable to distinguish unedited posts.

The API currently always sets updated_at to a value, making it impossible to signal whether a post has been edited. The database design supports this distinction via first_created_at (immutable) vs created_at (updates on delete&insert pattern), but the API doesn't expose it. If the intent is to indicate "never updated," make updated_at nullable in the schema and conditionally null it when first_created_at === created_at. Note: exact timestamp comparison may be unreliable due to microsecond differences; consider row timestamp precision or an explicit edit flag if strict equality checking is needed.

🤖 Prompt for AI Agents
In `@packages/backend_app/src/apps/posts/dto.ts` around lines 6 - 41, postSchema
currently requires updated_at and transformPost always sets it, preventing
signaling "never updated"; make updated_at nullable in postSchema (allow null in
the OpenAPI schema) and update transformPost to return updated_at as null when
the post was never edited by comparing timestamps (use millisecond-safe
comparison like first_created_at.getTime() === created_at.getTime()) otherwise
return the ISO string; reference postSchema and transformPost when making these
changes and ensure the OpenAPI metadata reflects nullability.

Comment on lines +37 to +43
export const postLogsTable = pgTable("post_logs", {
id: integer().primaryKey(),
public_id: uuid().notNull(),
user_id: integer().notNull(),
content: text().notNull(),
...timestamps,
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, examine the schema file to see the current state
cat -n packages/backend_app/src/db/schema.ts | head -60

Repository: mikan3rd/hono-next-example

Length of output: 1529


🏁 Script executed:

# Search for insert statements related to postLogsTable
rg "postLogsTable" --type ts --context 5

Repository: mikan3rd/hono-next-example

Length of output: 6302


🏁 Script executed:

# Search for any insert patterns into post_logs
rg "post_logs\|postLogsTable" --type ts -A 3 -B 3

Repository: mikan3rd/hono-next-example

Length of output: 52


🏁 Script executed:

# Check if there are other table definitions to compare id generation patterns
rg "primaryKey()" --type ts -B 2 -A 2 | head -50

Repository: mikan3rd/hono-next-example

Length of output: 1510


post_logs.id should be auto-generated for consistency.
Other tables use generatedAlwaysAsIdentity(), but postLogsTable requires manual ID assignment on insert. While current inserts work by copying IDs from posts, this creates inconsistency and fragility. Align with the rest of the schema.

🛠️ Suggested fix
 export const postLogsTable = pgTable("post_logs", {
-  id: integer().primaryKey(),
+  id: integer().primaryKey().generatedAlwaysAsIdentity(),
   public_id: uuid().notNull(),
   user_id: integer().notNull(),
   content: text().notNull(),
   ...timestamps,
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const postLogsTable = pgTable("post_logs", {
id: integer().primaryKey(),
public_id: uuid().notNull(),
user_id: integer().notNull(),
content: text().notNull(),
...timestamps,
});
export const postLogsTable = pgTable("post_logs", {
id: integer().primaryKey().generatedAlwaysAsIdentity(),
public_id: uuid().notNull(),
user_id: integer().notNull(),
content: text().notNull(),
...timestamps,
});
🤖 Prompt for AI Agents
In `@packages/backend_app/src/db/schema.ts` around lines 37 - 43, The post_logs
table defines id as integer().primaryKey() but should be auto-generated like
other tables; update postLogsTable's id column to use
generatedAlwaysAsIdentity() (i.e., change id to
integer().primaryKey().generatedAlwaysAsIdentity()) so inserts no longer require
manual id assignment and align schema behavior with other tables (refer to
postLogsTable, "post_logs", and the id column).

Comment on lines +27 to 28
updated_at: string | null;
user: {
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

Align updated_at handling with the API contract to avoid always showing “Updated”.

The OpenAPI schema and generated client types still define updated_at as a required string, and backend tests expect it to be present. With the null check, isUpdated will always be true and the badge/date will render for every post. Either make updated_at nullable end-to-end (API + schema + responses) or keep it non-null here and compare to created_at.

🛠️ Suggested fix (keep current API contract)
-  updated_at: string | null;
+  updated_at: string;
-  const isUpdated = post.updated_at !== null;
+  const isUpdated = post.updated_at !== post.created_at;
-            {post.updated_at !== null && (
+            {isUpdated && (
               <span suppressHydrationWarning>
                 Updated: {formatDate(post.updated_at)}
               </span>
             )}

Also applies to: 45-46, 186-189

🤖 Prompt for AI Agents
In
`@packages/frontend_app/src/components/pages/post/components/PostCard/index.tsx`
around lines 27 - 28, The code treats updated_at as nullable which makes
isUpdated truthy for every post; in the PostCard component keep updated_at as a
non-null string (remove the "| null" from the local type declaration or align
with generated client types) and change the updated check to compare timestamps
(set isUpdated = post.updated_at !== post.created_at) so the "Updated"
badge/date only renders when updated_at differs from created_at; update any
usages that assume nullable updated_at accordingly (symbols: updated_at,
created_at, isUpdated, PostCard).

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.

updated_atカラムを不要にしたい

2 participants