From 4f37ac4e36b778f89ac3a66af74455cd8e5d4f9f Mon Sep 17 00:00:00 2001 From: Taesu <166604494+bytaesu@users.noreply.github.com> Date: Tue, 17 Mar 2026 19:00:55 +0900 Subject: [PATCH] docs: fix DatabaseTable type mapping and correct schema mismatches (#8611) Co-authored-by: Cursor Agent Co-authored-by: Taesu --- docs/content/docs/concepts/database.mdx | 1 + docs/content/docs/concepts/rate-limit.mdx | 19 ++-- .../docs/guides/next-auth-migration-guide.mdx | 1 + docs/content/docs/plugins/2fa.mdx | 4 +- .../docs/plugins/api-key/reference.mdx | 5 +- .../docs/plugins/device-authorization.mdx | 19 +--- docs/content/docs/plugins/oauth-provider.mdx | 42 ++++---- docs/content/docs/plugins/oidc-provider.mdx | 97 ++++++++++--------- docs/content/docs/plugins/organization.mdx | 5 + docs/content/docs/plugins/scim.mdx | 10 +- docs/content/docs/plugins/sso.mdx | 18 ++-- docs/content/docs/plugins/stripe.mdx | 6 +- docs/content/docs/plugins/username.mdx | 5 +- landing/components/docs/mdx-components.tsx | 33 ++++++- 14 files changed, 148 insertions(+), 117 deletions(-) diff --git a/docs/content/docs/concepts/database.mdx b/docs/content/docs/concepts/database.mdx index 068718f647..e8a51173bc 100644 --- a/docs/content/docs/concepts/database.mdx +++ b/docs/content/docs/concepts/database.mdx @@ -337,6 +337,7 @@ Table Name: `user` name: "email", type: "string", description: "User's email address for communication and login", + isUnique: true, }, { name: "emailVerified", diff --git a/docs/content/docs/concepts/rate-limit.mdx b/docs/content/docs/concepts/rate-limit.mdx index b7c950987f..663aacff98 100644 --- a/docs/content/docs/concepts/rate-limit.mdx +++ b/docs/content/docs/concepts/rate-limit.mdx @@ -291,18 +291,19 @@ Table Name: `rateLimit` isPrimaryKey: true }, { - name: "key", - type: "string", + name: "key", + type: "string", description: "Unique identifier for each rate limit key", + isUnique: true, }, { - name: "count", - type: "integer", - description: "Time window in seconds" + name: "count", + type: "integer", + description: "Number of requests made in the current window" }, - { - name: "lastRequest", - type: "bigint", - description: "Max requests in the window" + { + name: "lastRequest", + type: "bigint", + description: "Timestamp of the last request (epoch ms)" }]} /> diff --git a/docs/content/docs/guides/next-auth-migration-guide.mdx b/docs/content/docs/guides/next-auth-migration-guide.mdx index b03ac610d0..2d0bfd4364 100644 --- a/docs/content/docs/guides/next-auth-migration-guide.mdx +++ b/docs/content/docs/guides/next-auth-migration-guide.mdx @@ -382,6 +382,7 @@ Just like Auth.js has database models, Better Auth also has a core schema. In th name: "email", type: "string", description: "User's email address for communication and login", + isUnique: true, }, { name: "emailVerified", diff --git a/docs/content/docs/plugins/2fa.mdx b/docs/content/docs/plugins/2fa.mdx index dab2dfb073..5f66b4a7c2 100644 --- a/docs/content/docs/plugins/2fa.mdx +++ b/docs/content/docs/plugins/2fa.mdx @@ -482,8 +482,8 @@ Table: `twoFactor` fields={[ { name: "id", type: "string", description: "The ID of the two factor authentication.", isPrimaryKey: true }, { name: "userId", type: "string", description: "The ID of the user", isForeignKey: true }, - { name: "secret", type: "string", description: "The secret used to generate the TOTP code.", isOptional: true }, - { name: "backupCodes", type: "string", description: "The backup codes used to recover access to the account if the user loses access to their phone or email.", isOptional: true }, + { name: "secret", type: "string", description: "The secret used to generate the TOTP code." }, + { name: "backupCodes", type: "string", description: "The backup codes used to recover access to the account if the user loses access to their phone or email." }, ]} /> diff --git a/docs/content/docs/plugins/api-key/reference.mdx b/docs/content/docs/plugins/api-key/reference.mdx index af25739fc7..bf22a9a8da 100644 --- a/docs/content/docs/plugins/api-key/reference.mdx +++ b/docs/content/docs/plugins/api-key/reference.mdx @@ -497,11 +497,13 @@ Table: `apikey` name: "enabled", type: "boolean", description: "Whether the API key is enabled.", + isOptional: true, }, { name: "rateLimitEnabled", type: "boolean", description: "Whether the API key has rate limiting enabled.", + isOptional: true, }, { name: "rateLimitTimeWindow", @@ -521,6 +523,7 @@ Table: `apikey` type: "number", description: "The number of requests made within the rate limit time window.", + isOptional: true, }, { name: "remaining", @@ -558,7 +561,7 @@ Table: `apikey` }, { name: "metadata", - type: "Object", + type: "string", isOptional: true, description: "Any additional metadata you want to store with the key.", }, diff --git a/docs/content/docs/plugins/device-authorization.mdx b/docs/content/docs/plugins/device-authorization.mdx index f89d0a72be..b1643430a5 100644 --- a/docs/content/docs/plugins/device-authorization.mdx +++ b/docs/content/docs/plugins/device-authorization.mdx @@ -611,12 +611,11 @@ Table Name: `deviceCode` type: "string", description: "The user-friendly code for verification", }, - { - name: "userId", - type: "string", + { + name: "userId", + type: "string", description: "The ID of the user who approved/denied", - isOptional: true, - isForeignKey: true + isOptional: true }, { name: "clientId", @@ -652,15 +651,5 @@ Table Name: `deviceCode` description: "Minimum seconds between polls", isOptional: true }, - { - name: "createdAt", - type: "Date", - description: "When the request was created", - }, - { - name: "updatedAt", - type: "Date", - description: "When the request was last updated", - } ]} /> diff --git a/docs/content/docs/plugins/oauth-provider.mdx b/docs/content/docs/plugins/oauth-provider.mdx index ca152c0e34..2b0e2b021b 100644 --- a/docs/content/docs/plugins/oauth-provider.mdx +++ b/docs/content/docs/plugins/oauth-provider.mdx @@ -1601,7 +1601,7 @@ Table Name: `oauthClient` name: "clientId", type: "string", description: "Unique identifier for each OAuth client", - isPrimaryKey: true, + isUnique: true, }, { name: "clientSecret", @@ -1652,17 +1652,18 @@ Table Name: `oauthClient` type: "string", description: "ID of the reference of the client owner if not a user. (optional)", isOptional: true, - isForeignKey: true, }, { name: "createdAt", type: "Date", description: "Timestamp of when the OAuth client was created", + isOptional: true, }, { name: "updatedAt", type: "Date", description: "Timestamp of when the OAuth client was last updated", + isOptional: true, }, { name: "name", @@ -1722,7 +1723,12 @@ Table Name: `oauthClient` name: "redirectUris", type: "string[]", description: "Array of of redirect uris", - isRequired: true, + }, + { + name: "postLogoutRedirectUris", + type: "string[]", + description: "Array of post-logout redirect URIs", + isOptional: true, }, { name: "tokenEndpointAuthMethod", @@ -1754,6 +1760,12 @@ Table Name: `oauthClient` description: "Type of OAuth client. Supports: ['web', 'native', 'user-agent-based']", isOptional: true, }, + { + name: "requirePKCE", + type: "boolean", + description: "Whether PKCE is required for this client", + isOptional: true, + }, { name: "metadata", type: "json", @@ -1779,14 +1791,12 @@ Table Name: `oauthRefreshToken` name: "token", type: "string", description: "Hashed/encrypted refresh token", - isRequired: true, }, { name: "clientId", type: "string", description: "ID of the OAuth client", isForeignKey: true, - isRequired: true, references: { model: "oauthClient", field: "clientId" }, }, { @@ -1794,7 +1804,7 @@ Table Name: `oauthRefreshToken` type: "string", description: "ID of the session used at issuance of the token (and still active)", isForeignKey: true, - isRequired: false, + isOptional: true, references: { model: "session", field: "id" }, }, { @@ -1802,7 +1812,6 @@ Table Name: `oauthRefreshToken` type: "string", description: "ID of the user associated with the token", isForeignKey: true, - isRequired: true, references: { model: "user", field: "id" }, }, { @@ -1810,13 +1819,11 @@ Table Name: `oauthRefreshToken` type: "string", description: "ID of the consented reference", isOptional: true, - isForeignKey: true, }, { name: "scopes", type: "string[]", description: "Array of granted scopes", - isRequired: true, }, { name: "revoked", @@ -1859,14 +1866,13 @@ Table Name: `oauthAccessToken` name: "token", type: "string", description: "Hashed/encrypted access token", - isRequired: true, + isUnique: true, }, { name: "clientId", type: "string", description: "ID of the OAuth client", isForeignKey: true, - isRequired: true, references: { model: "oauthClient", field: "clientId" } }, { @@ -1898,18 +1904,16 @@ Table Name: `oauthAccessToken` type: "string", description: "ID of the consented reference", isOptional: true, - isForeignKey: true, }, { name: "scopes", type: "string[]", description: "Array of granted scopes", - isRequired: true, }, { name: "createdAt", type: "Date", - description: "Timestamp when the token was created" + description: "Timestamp when the token was created", }, { name: "expiresAt", @@ -1950,23 +1954,21 @@ Table Name: `oauthConsent` type: "string", description: "ID of the consented reference", isOptional: true, - isForeignKey: true, }, { name: "scopes", - type: "string", - description: "Comma-separated list of scopes consented to", - isRequired: true + type: "string[]", + description: "Array of scopes consented to", }, { name: "createdAt", type: "Date", - description: "Timestamp of when the consent was given" + description: "Timestamp of when the consent was given", }, { name: "updatedAt", type: "Date", - description: "Timestamp of when the consent was last updated" + description: "Timestamp of when the consent was last updated", }, ]} /> diff --git a/docs/content/docs/plugins/oidc-provider.mdx b/docs/content/docs/plugins/oidc-provider.mdx index a0079b3622..019caae91a 100644 --- a/docs/content/docs/plugins/oidc-provider.mdx +++ b/docs/content/docs/plugins/oidc-provider.mdx @@ -453,11 +453,11 @@ Table Name: `oauthApplication` description: "Database ID of the OAuth client", isPrimaryKey: true }, - { - name: "clientId", - type: "string", + { + name: "clientId", + type: "string", description: "Unique identifier for each OAuth client", - isPrimaryKey: true + isUnique: true }, { name: "clientSecret", @@ -465,17 +465,21 @@ Table Name: `oauthApplication` description: "Secret key for the OAuth client. Optional for public clients using PKCE.", isOptional: true }, - { - name: "name", - type: "string", - description: "Name of the OAuth client", - isRequired: true + { + name: "icon", + type: "string", + description: "Icon of the OAuth client", + isOptional: true + }, + { + name: "name", + type: "string", + description: "Name of the OAuth client" }, { name: "redirectUrls", type: "string", - description: "Comma-separated list of redirect URLs", - isRequired: true + description: "Comma-separated list of redirect URLs" }, { name: "metadata", @@ -486,26 +490,26 @@ Table Name: `oauthApplication` { name: "type", type: "string", - description: "Type of OAuth client (e.g., web, mobile)", - isRequired: true + description: "Type of OAuth client (e.g., web, mobile)" }, - { - name: "disabled", - type: "boolean", + { + name: "disabled", + type: "boolean", description: "Indicates if the client is disabled", - isRequired: true + isOptional: true }, { - name: "userId", - type: "string", + name: "userId", + type: "string", description: "ID of the user who owns the client. (optional)", isOptional: true, + isForeignKey: true, references: { model: "user", field: "id" } }, - { - name: "createdAt", - type: "Date", - description: "Timestamp of when the OAuth client was created" + { + name: "createdAt", + type: "Date", + description: "Timestamp of when the OAuth client was created" }, { name: "updatedAt", @@ -528,27 +532,26 @@ Table Name: `oauthAccessToken` isPrimaryKey: true }, { - name: "accessToken", - type: "string", + name: "accessToken", + type: "string", description: "Access token issued to the client", + isUnique: true }, { - name: "refreshToken", - type: "string", + name: "refreshToken", + type: "string", description: "Refresh token issued to the client", - isRequired: true + isUnique: true }, { name: "accessTokenExpiresAt", type: "Date", - description: "Expiration date of the access token", - isRequired: true + description: "Expiration date of the access token" }, { name: "refreshTokenExpiresAt", type: "Date", - description: "Expiration date of the refresh token", - isRequired: true + description: "Expiration date of the refresh token" }, { name: "clientId", @@ -558,22 +561,22 @@ Table Name: `oauthAccessToken` references: { model: "oauthApplication", field: "clientId" } }, { - name: "userId", - type: "string", + name: "userId", + type: "string", description: "ID of the user associated with the token", + isOptional: true, isForeignKey: true, references: { model: "user", field: "id" } }, { name: "scopes", type: "string", - description: "Comma-separated list of scopes granted", - isRequired: true + description: "Comma-separated list of scopes granted" }, - { - name: "createdAt", - type: "Date", - description: "Timestamp of when the access token was created" + { + name: "createdAt", + type: "Date", + description: "Timestamp of when the access token was created" }, { name: "updatedAt", @@ -612,19 +615,17 @@ Table Name: `oauthConsent` { name: "scopes", type: "string", - description: "Comma-separated list of scopes consented to", - isRequired: true + description: "Comma-separated list of scopes consented to" }, { name: "consentGiven", type: "boolean", - description: "Indicates if consent was given", - isRequired: true + description: "Indicates if consent was given" }, - { - name: "createdAt", - type: "Date", - description: "Timestamp of when the consent was given" + { + name: "createdAt", + type: "Date", + description: "Timestamp of when the consent was given" }, { name: "updatedAt", diff --git a/docs/content/docs/plugins/organization.mdx b/docs/content/docs/plugins/organization.mdx index b287d40e25..d7aef44f62 100644 --- a/docs/content/docs/plugins/organization.mdx +++ b/docs/content/docs/plugins/organization.mdx @@ -2138,6 +2138,7 @@ Table Name: `teamMember` name: "createdAt", type: "Date", description: "Timestamp of when the team member was created", + isOptional: true, }, ]} /> @@ -2167,6 +2168,7 @@ Table Name: `organization` name: "slug", type: "string", description: "The slug of the organization", + isUnique: true, }, { name: "logo", @@ -2258,6 +2260,7 @@ Table Name: `invitation` name: "role", type: "string", description: "The role of the user in the organization", + isOptional: true, }, { name: "status", @@ -2349,6 +2352,7 @@ Table Name: `organizationRole` name: "updatedAt", type: "Date", description: "Timestamp of when the organization role was updated", + isOptional: true, }, ]} /> @@ -2416,6 +2420,7 @@ Table Name: `teamMember` name: "createdAt", type: "Date", description: "Timestamp of when the team member was created", + isOptional: true, }, ]} /> diff --git a/docs/content/docs/plugins/scim.mdx b/docs/content/docs/plugins/scim.mdx index cbcb51a635..d19ae9f7e4 100644 --- a/docs/content/docs/plugins/scim.mdx +++ b/docs/content/docs/plugins/scim.mdx @@ -455,11 +455,11 @@ The plugin requires additional fields in the `scimProvider` table to store the p @@ -469,7 +469,7 @@ The `scimProvider` schema is extended as follows: diff --git a/docs/content/docs/plugins/sso.mdx b/docs/content/docs/plugins/sso.mdx index dfa325e9fb..b044672421 100644 --- a/docs/content/docs/plugins/sso.mdx +++ b/docs/content/docs/plugins/sso.mdx @@ -1406,15 +1406,15 @@ The plugin requires additional fields in the `ssoProvider` table to store the pr @@ -1424,7 +1424,7 @@ The `ssoProvider` schema is extended as follows: diff --git a/docs/content/docs/plugins/stripe.mdx b/docs/content/docs/plugins/stripe.mdx index e474b589ab..5f4e70aea3 100644 --- a/docs/content/docs/plugins/stripe.mdx +++ b/docs/content/docs/plugins/stripe.mdx @@ -739,9 +739,9 @@ Table Name: `subscription` description: "The Stripe subscription ID", isOptional: true }, - { - name: "status", - type: "string", + { + name: "status", + type: "string", description: "The status of the subscription (active, canceled, etc.)", defaultValue: "incomplete" }, diff --git a/docs/content/docs/plugins/username.mdx b/docs/content/docs/plugins/username.mdx index ae2c274ccc..507ce82394 100644 --- a/docs/content/docs/plugins/username.mdx +++ b/docs/content/docs/plugins/username.mdx @@ -345,13 +345,14 @@ The plugin requires 2 fields to be added to the user table: name: "username", type: "string", description: "The username of the user", - isUnique: true + isUnique: true, + isOptional: true }, { name: "displayUsername", type: "string", description: "Non normalized username of the user", - isUnique: true + isOptional: true }, ]} /> diff --git a/landing/components/docs/mdx-components.tsx b/landing/components/docs/mdx-components.tsx index 71596a8d03..069dce7344 100644 --- a/landing/components/docs/mdx-components.tsx +++ b/landing/components/docs/mdx-components.tsx @@ -95,10 +95,26 @@ interface Field { isPrimaryKey?: boolean; isForeignKey?: boolean; isOptional?: boolean; + isUnique?: boolean; } +const typeAliases: Record = { + text: "string", + integer: "number", + int: "number", + bigint: "number", + float: "number", + double: "number", + decimal: "number", + bool: "boolean", + object: "json", + timestamp: "date", + datetime: "date", +}; + function TypeIcon({ type }: { type: string }) { - const t = type.toLowerCase().replace("[]", ""); + const raw = type.toLowerCase().replace("[]", ""); + const t = typeAliases[raw] ?? raw; const className = "size-3 shrink-0"; if (t === "string" || t === "text") { @@ -202,7 +218,17 @@ type DrizzleProvider = "pg" | "mysql" | "sqlite"; function fieldToDBField(field: Field): DBFieldAttribute { const t = field.type.toLowerCase(); - const type = (t === "text" ? "string" : t) as DBFieldAttribute["type"]; + const isArray = t.endsWith("[]"); + const raw = isArray ? t.slice(0, -2) : t; + const aliased = typeAliases[raw] ?? raw; + const type = ( + isArray && (aliased === "string" || aliased === "number") + ? `${aliased}[]` + : isArray + ? t + : aliased + ) as DBFieldAttribute["type"]; + const bigint = raw === "bigint"; let references: DBFieldAttribute["references"] | undefined; if (field.isForeignKey && field.name.endsWith("Id")) { @@ -218,7 +244,8 @@ function fieldToDBField(field: Field): DBFieldAttribute { type, required: field.isPrimaryKey ? true : !field.isOptional, references, - unique: false, + unique: field.isUnique ?? false, + bigint, }; }