Database
The @pagebridge/db package provides the PostgreSQL schema, Drizzle ORM client, and helper functions.
Creating a client
import { createDb } from '@pagebridge/db'
const { db, close } = createDb(process.env.DATABASE_URL!)
// Use the db client for queries
await db.select().from(searchAnalytics)
// Close the connection when done
await close()Table exports
All table definitions are exported for use in custom queries:
import {
searchAnalytics,
queryAnalytics,
syncLog,
pageIndexStatus,
unmatchDiagnostics
} from '@pagebridge/db'searchAnalytics
Page-level search metrics per day.
// Columns
{
id: string // Primary key: `${siteId}:${page}:${date}`
siteId: string
page: string
date: date
clicks: number
impressions: number
ctr: number
position: number
fetchedAt: timestamp
}
// Indexes: (siteId, page), (siteId, date)queryAnalytics
Query-level search metrics per page per day.
// Columns
{
id: string // Primary key: `${siteId}:${page}:${query}:${date}`
siteId: string
page: string
query: string
date: date
clicks: number
impressions: number
ctr: number
position: number
}
// Index: (siteId, page, query)syncLog
Tracks sync execution history.
// Columns
{
id: uuid
siteId: string
status: 'running' | 'completed' | 'failed'
startedAt: timestamp
completedAt: timestamp | null
rowsProcessed: number | null
error: text | null
}pageIndexStatus
Cached Google index inspection results with 24-hour TTL.
// Columns
{
id: string // Primary key: `${siteId}:${page}`
siteId: string
page: string
verdict: string // 'PASS' | 'FAIL' | 'NEUTRAL'
coverageState: string | null
indexingState: string | null
pageFetchState: string | null
lastCrawlTime: timestamp | null
robotsTxtState: string | null
fetchedAt: timestamp
}
// Index: (siteId, page)unmatchDiagnostics
Stores diagnostic information for unmatched URLs.
// Columns
{
id: string // Primary key: `${siteId}:${gscUrl}`
siteId: string
gscUrl: string
extractedSlug: string | null
unmatchReason: string // 'no_slug_extracted' | 'no_matching_document' | 'outside_path_prefix'
normalizedUrl: string | null
pathAfterPrefix: string | null
configuredPrefix: string | null
similarSlugs: string | null // JSON array of similar slug suggestions
availableSlugsCount: number | null
lastSeenAt: timestamp
firstSeenAt: timestamp
}
// Indexes: (siteId), (siteId, unmatchReason)Drizzle utilities
Common Drizzle ORM utilities are re-exported for convenience:
import { eq, desc, asc, and, or, sql, gte, lte } from '@pagebridge/db'
// Example: query recent analytics
const recentData = await db
.select()
.from(searchAnalytics)
.where(
and(
eq(searchAnalytics.siteId, 'sc-domain:example.com'),
gte(searchAnalytics.date, startDate)
)
)
.orderBy(desc(searchAnalytics.date))