Conversation
- Modified the apikey table to drop the user_id column and replace it with reference_id, establishing a foreign key relationship with the user table. - Added config_id column with a default value to the apikey table. - Updated related code in the account schema and user service to reflect these changes. - Enhanced the journal and snapshot files to include the latest schema updates.
…nbale-to-generate-new-ones-after-upgrade-to-0287 feat: update apikey schema and relationships
| @@ -450,10 +450,9 @@ export const createApiKey = async ( | |||
| if (input.metadata) { | |||
| await db | |||
| .update(apikey) | |||
| .set({ | |||
| metadata: JSON.stringify(input.metadata), | |||
| }) | |||
| .where(eq(apikey.id, apiKey.id)); | |||
| .set({ metadata: JSON.stringify(input.metadata) }) | |||
| .where(eq(apikey.id, result.id)); | |||
| } | |||
| return apiKey; | |||
|
|
|||
| return result; | |||
There was a problem hiding this comment.
Non-atomic API key creation leaves orphaned unusable keys
The API key is created in one call, then metadata (containing organizationId) is set in a separate UPDATE. If the update on line 452–455 fails (e.g. due to a transient DB error or process crash), the key will persist in the database without the organizationId metadata.
In validateRequest in auth.ts (lines 457–465), any API key whose metadata lacks organizationId immediately returns { session: null, user: null }, so the key is permanently non-functional and there is no recovery path for the caller.
Consider passing the metadata during key creation or wrapping both operations in a transaction to guarantee atomicity:
const result = await auth.createApiKey({
body: {
name: input.name,
expiresIn: input.expiresIn,
prefix: input.prefix,
rateLimitEnabled: input.rateLimitEnabled,
rateLimitTimeWindow: input.rateLimitTimeWindow,
rateLimitMax: input.rateLimitMax,
remaining: input.remaining,
refillAmount: input.refillAmount,
refillInterval: input.refillInterval,
userId,
metadata: input.metadata ? JSON.stringify(input.metadata) : undefined,
},
});If the auth.createApiKey API does not accept metadata at creation time, wrap the two operations in a db.transaction so a failed metadata update rolls back the key creation as well.
| ALTER TABLE "apikey" DROP CONSTRAINT "apikey_user_id_user_id_fk"; | ||
| --> statement-breakpoint | ||
| ALTER TABLE "apikey" ADD CONSTRAINT "apikey_reference_id_user_id_fk" FOREIGN KEY ("reference_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint | ||
| ALTER TABLE "apikey" DROP COLUMN "user_id"; No newline at end of file |
There was a problem hiding this comment.
Missing newline at end of file
This file is missing a trailing newline (\ No newline at end of file in the diff). While this does not affect SQL execution, it is inconsistent with the other migration files in this repository and can cause noisy diffs in the future.
| ALTER TABLE "apikey" DROP COLUMN "user_id"; | |
| ALTER TABLE "apikey" DROP COLUMN "user_id"; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
- Changed the authorization check for deleting an API key to use referenceId instead of userId, ensuring proper validation against the current user's ID.
This PR promotes changes from
canarytomainfor version v0.28.8.🔍 Changes Include:
✅ Pre-merge Checklist:
Greptile Summary
This release (v0.28.8) migrates the
apikeytable to align with@better-auth/api-key's expected schema when usingreferences: "user", renaming theuser_idcolumn toreference_idand adding aconfig_idcolumn. The migration is split across two SQL files — the first safely backfills data before setting constraints, the second swaps the foreign key and drops the old column. The Drizzle schema and auth configuration are updated consistently.Key changes:
0150,0151) renameapikey.user_id→apikey.reference_idand addapikey.config_id.account.tsschema updated:referenceIdfield andapikeyRelationsupdated to join through the new column.auth.tsgainsreferences: "user"in theapiKeyplugin config so better-auth maps sessions toreferenceId.services/user.tscreateApiKeycreates the key then updatesmetadatain a separate, non-atomic DB call — if the metadata update fails, the key is orphaned and permanently unusable sincevalidateRequestrequiresorganizationIdin the metadata.0151_modern_sunfire.sqlis missing a trailing newline.Confidence Score: 3/5
user.ts— a failure between the two DB operations leaves a permanently unusable key with no recovery path, sincevalidateRequestrequiresorganizationIdin the metadata. This is a real reliability issue under transient DB failures. The missing EOF newline in the SQL file is trivial.packages/server/src/services/user.ts— the non-atomiccreateApiKeylogic deserves attention before the release ships to production.Last reviewed commit: "Merge pull request #..."