Skip to content

K1 24 make game data schema#10

Merged
emi-a-dinh merged 4 commits intodevelopfrom
K1-24-Make-Game-Data-Schema
Feb 25, 2026
Merged

K1 24 make game data schema#10
emi-a-dinh merged 4 commits intodevelopfrom
K1-24-Make-Game-Data-Schema

Conversation

@ShortVincentMan
Copy link
Copy Markdown
Collaborator

Developer: VINCENT LE

Closes #K-24

Pull Request Summary

Created the gameDataSchema alongside endpoint routes for the api, tested it in Postman and verified it works.

Modifications

/database/gameDataSchema.ts. - created
/api/gameData/[saveId]/route.ts - created
/api/gameData/route.ts - created

Testing Considerations

POST request - Requires "lastUpdated" otherwise error when testing with Postman

Pull Request Checklist

  • Code is neat, readable, and works
  • Comments are appropriate
  • The commit messages follows our guidelines
  • The developer name is specified
  • The summary is completed
  • Assign reviewers

Screenshots/Screencast

image image image image

Copilot AI review requested due to automatic review settings February 13, 2026 03:10
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a game data persistence system by creating a Mongoose schema and REST API endpoints for managing game save data. The implementation follows the existing patterns established by the users API but has several critical security vulnerabilities that must be addressed before merging.

Changes:

  • Created gameDataSchema with fields for save management (saveId, saveVersion, userId, gameId, completedLevels, lastUpdated)
  • Implemented POST endpoint for creating new game save data
  • Implemented GET and PATCH endpoints for retrieving and updating game data by saveId
  • Updated dependencies (Chakra UI, Clerk, emotion packages, framer-motion)
  • Removed .env.local.example file

Reviewed changes

Copilot reviewed 5 out of 6 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/database/gameDataSchema.ts Defines Mongoose schema for game save data with unique saveId identifier
src/app/api/gameData/route.ts POST endpoint for creating new game save records
src/app/api/gameData/[saveId]/route.ts GET and PATCH endpoints for retrieving and updating game saves by ID
package.json Dependency updates for Chakra UI, Clerk, and new UI packages
package-lock.json Lock file updates corresponding to package.json changes
.env.local.example Removed environment variable template file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +12
export async function GET(_req: Request, { params }: { params: { saveId: string } }) {
await connectDB();

const data = await GameData.findOne({ saveId: params.saveId }).lean();
if (!data) {
return NextResponse.json({ error: "Game data not found" }, { status: 404 });
}
return NextResponse.json(data, { status: 200 });
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Missing authentication/authorization check. This endpoint allows anyone to read any game data by saveId without verifying the user's identity or ownership. You should use auth() from '@clerk/nextjs/server' to verify the authenticated user and check that they own the game data being accessed (compare authenticated userId with the userId field in the retrieved data).

Copilot uses AI. Check for mistakes.
Comment on lines +15 to +31
export async function PATCH(req: NextRequest, { params }: { params: { saveId: string } }) {
try {
await connectDB();
const changes = await req.json();
if (!changes || typeof changes !== "object" || Array.isArray(changes)) {
return NextResponse.json({ error: "Invalid patch" }, { status: 400 });
}

const updated = await GameData.findOneAndUpdate(
{ saveId: params.saveId },
{ $set: changes },
{ new: true, runValidators: true },
).lean();
if (!updated) {
return NextResponse.json({ error: "Save not found" }, { status: 404 });
}
return NextResponse.json(updated, { status: 200 });
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Missing authentication/authorization check. This endpoint allows anyone to update any game data by saveId without verifying the user's identity or ownership. You should use auth() from '@clerk/nextjs/server' to verify the authenticated user and check that they own the game data being modified (compare authenticated userId with the userId field in the existing data).

Copilot uses AI. Check for mistakes.
gameId: { type: String, required: true },
completedLevels: { type: [Number], default: [] },
});

Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Consider adding an index on saveId field. Since saveId is marked as unique and is used for queries in the API endpoints, adding an explicit index will improve query performance. Add: gameDataSchema.index({ saveId: 1 }); after the schema definition.

Suggested change
gameDataSchema.index({ saveId: 1 });

Copilot uses AI. Check for mistakes.
"@clerk/nextjs": "^6.37.3",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"framer-motion": "^12.34.0",
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Added dependencies (@emotion/styled, framer-motion) that are not used in this PR. These packages add significant bundle size but aren't referenced in any of the new files. If these dependencies are for future work, consider adding them in the PR where they're actually used to keep PRs focused and make it easier to track when dependencies were introduced and why.

Suggested change
"framer-motion": "^12.34.0",

Copilot uses AI. Check for mistakes.
import mongoose, { Schema } from "mongoose";

const gameDataSchema = new Schema({
saveId: { type: String, required: true, unique: true },
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Missing validation for saveId format. The saveId field is used as a unique identifier but has no format validation. Consider adding validation (e.g., regex pattern, length constraints) to ensure saveIds follow a consistent format and prevent potential issues with malformed IDs.

Suggested change
saveId: { type: String, required: true, unique: true },
saveId: {
type: String,
required: true,
unique: true,
minlength: 1,
maxlength: 128,
match: [/^[A-Za-z0-9_-]+$/, "Invalid saveId format"],
},

Copilot uses AI. Check for mistakes.
try {
await connectDB();

const body = await req.json();
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Missing input validation for the request body. The endpoint should validate that required fields (saveId, saveVersion, userId, gameVersion, gameId) are present and have the correct types before attempting to create the document. This prevents unclear database validation errors from being exposed to the client.

Suggested change
const body = await req.json();
const body = await req.json();
// Basic input validation for required fields before hitting the database
if (body === null || typeof body !== "object" || Array.isArray(body)) {
return NextResponse.json(
{ error: "Invalid request body: expected a JSON object." },
{ status: 400 }
);
}
const {
saveId,
saveVersion,
userId,
gameVersion,
gameId,
} = body as Record<string, unknown>;
const isNonEmptyString = (value: unknown) =>
typeof value === "string" && value.trim().length > 0;
if (
!isNonEmptyString(saveId) ||
!isNonEmptyString(saveVersion) ||
!isNonEmptyString(userId) ||
!isNonEmptyString(gameVersion) ||
!isNonEmptyString(gameId)
) {
return NextResponse.json(
{
error:
"Invalid request body: required fields saveId, saveVersion, userId, gameVersion, and gameId must be non-empty strings.",
},
{ status: 400 }
);
}

Copilot uses AI. Check for mistakes.
Comment on lines +6 to +12
await connectDB();

const data = await GameData.findOne({ saveId: params.saveId }).lean();
if (!data) {
return NextResponse.json({ error: "Game data not found" }, { status: 404 });
}
return NextResponse.json(data, { status: 200 });
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Missing error handling. The GET handler lacks a try-catch block to handle potential database connection errors or query failures. If the database query throws an exception, it will result in an unhandled error. Wrap the function body in a try-catch block and return an appropriate error response with status 500, consistent with other GET endpoints in the codebase.

Suggested change
await connectDB();
const data = await GameData.findOne({ saveId: params.saveId }).lean();
if (!data) {
return NextResponse.json({ error: "Game data not found" }, { status: 404 });
}
return NextResponse.json(data, { status: 200 });
try {
await connectDB();
const data = await GameData.findOne({ saveId: params.saveId }).lean();
if (!data) {
return NextResponse.json({ error: "Game data not found" }, { status: 404 });
}
return NextResponse.json(data, { status: 200 });
} catch (err: any) {
console.error("Server error", err);
return NextResponse.json({ error: err.message }, { status: 500 });
}

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +26
const updated = await GameData.findOneAndUpdate(
{ saveId: params.saveId },
{ $set: changes },
{ new: true, runValidators: true },
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Security vulnerability: Users can modify protected fields. The PATCH endpoint allows modification of any field including userId, saveId, and other critical identifiers. You should either whitelist which fields can be updated (e.g., only completedLevels, lastUpdated), or blacklist critical fields that should never be modified by users (userId, saveId, gameId).

Copilot uses AI. Check for mistakes.
completedLevels: { type: [Number], default: [] },
});

const GameData = mongoose.models.GameData || mongoose.model("GameData", gameDataSchema);
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Schema should specify a collection name. Following the pattern in userSchema.ts which explicitly specifies the collection name as the third argument to mongoose.model(), this schema should also specify the collection name (e.g., "gamedata" or "gamesaves") to ensure it maps to the correct collection in MongoDB Atlas.

Suggested change
const GameData = mongoose.models.GameData || mongoose.model("GameData", gameDataSchema);
const GameData = mongoose.models.GameData || mongoose.model("GameData", gameDataSchema, "gamedata");

Copilot uses AI. Check for mistakes.
gameVersion: { type: String, required: true },
gameId: { type: String, required: true },
completedLevels: { type: [Number], default: [] },
});
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Consider adding timestamps option to schema. Adding { timestamps: true } as the second argument to the Schema constructor would automatically create createdAt and updatedAt fields, which is useful for tracking when game data was created versus when it was last modified. This is distinct from the lastUpdated field which appears to track game progress updates.

Suggested change
});
}, { timestamps: true });

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

@emi-a-dinh emi-a-dinh left a comment

Choose a reason for hiding this comment

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

Looks good, all api endpoints work.

@emi-a-dinh emi-a-dinh merged commit aa5d490 into develop Feb 25, 2026
10 checks passed
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.

3 participants