-
Notifications
You must be signed in to change notification settings - Fork 0
delete volunteers api #42
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,7 +16,8 @@ | |
| # next.js | ||
| /.next/ | ||
| /out/ | ||
|
|
||
| .claude | ||
| Documentation | ||
| # production | ||
| /build | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,71 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { createClient } from "../client/supabase/server"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export async function deleteVolunteer(id: number) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Validate input | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!id || typeof id !== "number" || id <= 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "Invalid volunteer ID. ID must be a positive number.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+8
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!id || typeof id !== "number" || id <= 0) { | |
| return { | |
| error: { | |
| message: "Invalid volunteer ID. ID must be a positive number.", | |
| if (typeof id !== "number" || id <= 0 || !Number.isInteger(id)) { | |
| return { | |
| error: { | |
| message: "Invalid volunteer ID. ID must be a positive integer.", |
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.
Copilot is right, no need for !id here because we are already checking typeof.
Copilot
AI
Nov 21, 2025
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.
Potential race condition: The check-then-delete pattern (lines 19-39) is vulnerable to race conditions. If another process deletes the volunteer between the existence check and the actual delete operation, the delete will succeed without error, but you've already verified the volunteer exists.
While this may not cause a critical issue in this specific case, it represents a Time-of-check to time-of-use (TOCTOU) problem. Consider either:
- Removing the existence check and relying on the delete operation's return data to determine if a row was affected
- If you need the existence check for business logic, document this known limitation
For option 1, Supabase delete operations don't return the deleted data by default, but you can check if the operation succeeded without errors as an indicator.
| // Check if volunteer exists before deleting | |
| const { data: existingVolunteer, error: fetchError } = await client | |
| .from("Volunteers") | |
| .select("id") | |
| .eq("id", id) | |
| .single(); | |
| if (fetchError || !existingVolunteer) { | |
| return { | |
| error: { | |
| message: "Volunteer not found.", | |
| status: 404, | |
| }, | |
| data: null, | |
| }; | |
| } | |
| // Delete the volunteer | |
| const { error: deleteError } = await client | |
| .from("Volunteers") | |
| .delete() | |
| .eq("id", id); | |
| if (deleteError) { | |
| return { | |
| error: { | |
| message: deleteError.message || "Failed to delete volunteer.", | |
| status: 500, | |
| // Attempt to delete the volunteer and return the deleted id | |
| const { data: deletedVolunteer, error: deleteError } = await client | |
| .from("Volunteers") | |
| .delete() | |
| .eq("id", id) | |
| .select("id") | |
| if (deleteError) { | |
| return { | |
| error: { | |
| message: deleteError.message || "Failed to delete volunteer.", | |
| status: 500, | |
| }, | |
| data: null, | |
| }; | |
| } | |
| // If no volunteer was deleted, it did not exist | |
| if (!deletedVolunteer || deletedVolunteer.length === 0) { | |
| return { | |
| error: { | |
| message: "Volunteer not found.", | |
| status: 404, |
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.
I agree with Copilot here. Can we rely on the client's return value to determine whether the volunteer was actually deleted?
One example of how this might be an issue is if the user spams the delete button.
Copilot
AI
Nov 21, 2025
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 return structure: The success response has status at the top level, but error responses have status nested inside the error object. This inconsistency makes it harder for API consumers to handle responses uniformly.
Consider either:
- Moving
statusinside thedataobject for success responses, or - Always placing
statusat the top level for both success and error cases
Example of consistent structure (option 2):
return {
status: 200,
error: null,
data: {
message: "Volunteer deleted successfully.",
id,
},
};| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| // Add any exports from your API files here | ||
|
|
||
| export { getExample } from "./getExample"; | ||
| export { deleteVolunteer } from "./deleteVolunteer"; |
Uh oh!
There was an error while loading. Please reload this page.
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.
You should change the branch name to from
remove_volunteer_apitobackend/remove-volunteer-api.