• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / storage / 29397915687

15 Jul 2026 07:36AM UTC coverage: 58.511% (-20.9%) from 79.363%
29397915687

Pull #1230

github

web-flow
Merge 9b3616757 into 5e9d4227f
Pull Request #1230: feat: Added separate Database application.

3563 of 6966 branches covered (51.15%)

Branch coverage included in aggregate %.

11 of 127 new or added lines in 4 files covered. (8.66%)

2373 existing lines in 100 files now uncovered.

7519 of 11974 relevant lines covered (62.79%)

383.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

64.65
/src/http/plugins/db.ts
1
import { createSingleFlightByKey } from '@internal/concurrency'
2
import {
3
  getPostgresConnection,
4
  getServiceKeyUser,
5
  getTenantConfig,
6
  PgTenantConnection,
7
} from '@internal/database'
8
import {
9
  areMigrationsUpToDate,
10
  DBMigration,
11
  lastLocalMigrationName,
12
  progressiveMigrations,
13
  runMigrationsOnTenant,
14
  updateTenantMigrationsState,
15
} from '@internal/database/migrations'
16
import { ERRORS } from '@internal/errors'
17
import { logSchema } from '@internal/monitoring'
18
import fastifyPlugin from 'fastify-plugin'
19
import { getConfig, MultitenantMigrationStrategy } from '../../config'
20

21
declare module 'fastify' {
22
  interface FastifyRequest {
23
    db: PgTenantConnection
24
    latestMigration?: keyof typeof DBMigration
25
  }
26
}
27

28
const { databaseEnableQueryCancellation, dbMigrationStrategy, isMultitenant, dbMigrationFreezeAt } =
28✔
29
  getConfig()
30

31
const migrationSingleFlight = createSingleFlightByKey<void>()
28✔
32

33
export const db = fastifyPlugin(
28✔
34
  async function db(fastify) {
35
    fastify.register(migrations)
4,560✔
36

37
    fastify.decorateRequest('db')
4,560✔
38

39
    fastify.addHook('preHandler', async (request) => {
4,560✔
40
      const adminUser = await getServiceKeyUser(request.tenantId)
1,048✔
41
      const userPayload = request.jwtPayload
1,048✔
42

43
      if (!userPayload) {
1,048!
44
        throw ERRORS.AccessDenied('JWT payload is missing')
×
45
      }
46

47
      request.db = await getPostgresConnection({
1,048✔
48
        user: {
49
          payload: userPayload,
50
          jwt: request.jwt,
51
        },
52
        superUser: adminUser,
53
        tenantId: request.tenantId,
54
        host: request.headers['x-forwarded-host'] as string,
55
        headers: request.headers,
56
        path: request.url,
57
        method: request.method,
58
        operation: () => request.operation,
2,313✔
59
      })
60

61
      // Connect abort signal to DB connection for query cancellation
62
      if (databaseEnableQueryCancellation && request.signals) {
1,046!
UNCOV
63
        request.db.setAbortSignal(request.signals.disconnect.signal)
×
64
      }
65
    })
66

67
    fastify.addHook('onSend', async (request, reply, payload) => {
4,560✔
68
      disposeRequestConnections(request)
1,049✔
69
      return payload
1,049✔
70
    })
71

72
    fastify.addHook('onTimeout', async (request) => {
4,560✔
73
      disposeRequestConnections(request)
×
74
    })
75

76
    fastify.addHook('onRequestAbort', async (request) => {
4,560✔
77
      disposeRequestConnections(request)
×
78
    })
79
  },
80
  { name: 'db-init' }
81
)
82

83
interface DbSuperUserPluginOptions {
84
  disableHostCheck?: boolean
85
}
86

87
export const dbSuperUser = fastifyPlugin<DbSuperUserPluginOptions>(
28✔
88
  async function dbSuperUser(fastify, opts) {
89
    fastify.register(migrations)
1,731✔
90
    fastify.decorateRequest('db')
1,731✔
91

92
    fastify.addHook('preHandler', async (request) => {
1,731✔
93
      const adminUser = await getServiceKeyUser(request.tenantId)
200✔
94

95
      request.db = await getPostgresConnection({
200✔
96
        user: adminUser,
97
        superUser: adminUser,
98
        tenantId: request.tenantId,
99
        host: request.headers['x-forwarded-host'] as string,
100
        path: request.url,
101
        method: request.method,
102
        headers: request.headers,
103
        disableHostCheck: opts.disableHostCheck,
104
        operation: () => request.operation,
65✔
105
      })
106

107
      // Connect abort signal to DB connection for query cancellation
108
      if (databaseEnableQueryCancellation && request.signals) {
200!
UNCOV
109
        request.db.setAbortSignal(request.signals.disconnect.signal)
×
110
      }
111
    })
112

113
    fastify.addHook('onSend', async (request, reply, payload) => {
1,731✔
114
      disposeRequestConnections(request)
261✔
115
      return payload
261✔
116
    })
117

118
    fastify.addHook('onTimeout', async (request) => {
1,731✔
119
      disposeRequestConnections(request)
×
120
    })
121

122
    fastify.addHook('onRequestAbort', async (request) => {
1,731✔
123
      disposeRequestConnections(request)
1✔
124
    })
125
  },
126
  { name: 'db-superuser-init' }
127
)
128

129
function disposeRequestConnections(request: {
130
  db?: PgTenantConnection
131
  log: Parameters<typeof logSchema.error>[0]
132
  tenantId: string
133
  id: string
134
  sbReqId?: string
135
}) {
136
  request.db?.dispose().catch((e) => {
1,311✔
137
    logSchema.error(request.log, 'Error disposing db connection', {
×
138
      type: 'db-connection',
139
      tenantId: request.tenantId,
140
      project: request.tenantId,
141
      reqId: request.id,
142
      sbReqId: request.sbReqId,
143
      error: e,
144
    })
145
  })
146
}
147

148
/**
149
 * Handle database migration for multitenant applications when a request is made
150
 */
151
export const migrations = fastifyPlugin(
28✔
152
  async function migrations(fastify) {
153
    fastify.addHook('preHandler', async (req) => {
6,291✔
154
      if (isMultitenant) {
1,248✔
155
        const { migrationVersion } = await getTenantConfig(req.tenantId)
3✔
156
        req.latestMigration = migrationVersion
3✔
157
        return
3✔
158
      }
159

160
      req.latestMigration = await lastLocalMigrationName()
1,245✔
161
    })
162

163
    if (dbMigrationStrategy === MultitenantMigrationStrategy.ON_REQUEST) {
6,291!
164
      fastify.addHook('preHandler', async (request) => {
6,291✔
165
        // migrations are handled via async migrations
166
        if (!isMultitenant) {
1,248✔
167
          return
1,245✔
168
        }
169

170
        const tenant = await getTenantConfig(request.tenantId)
3✔
171
        if (tenant.syncMigrationsDone) {
3!
UNCOV
172
          return
×
173
        }
174

175
        await migrationSingleFlight(request.tenantId, async () => {
3✔
176
          if (await areMigrationsUpToDate(request.tenantId)) {
3!
UNCOV
177
            tenant.syncMigrationsDone = true
×
UNCOV
178
            return
×
179
          }
180

181
          await runMigrationsOnTenant({
3✔
182
            databaseUrl: tenant.databaseUrl,
183
            tenantId: request.tenantId,
184
            upToMigration: dbMigrationFreezeAt,
185
          })
186
          await updateTenantMigrationsState(request.tenantId)
3✔
187
          tenant.syncMigrationsDone = true
3✔
188
        })
189
      })
190
    }
191

192
    if (dbMigrationStrategy === MultitenantMigrationStrategy.PROGRESSIVE) {
6,291!
UNCOV
193
      fastify.addHook('preHandler', async (request) => {
×
UNCOV
194
        if (!isMultitenant) {
×
UNCOV
195
          return
×
196
        }
197

198
        const tenant = await getTenantConfig(request.tenantId)
×
199
        if (tenant.syncMigrationsDone) {
×
200
          return
×
201
        }
202

203
        // migrations are up to date
204
        if (await areMigrationsUpToDate(request.tenantId)) {
×
205
          tenant.syncMigrationsDone = true
×
206
          return
×
207
        }
208

209
        progressiveMigrations.addTenant(request.tenantId)
×
210
      })
211
    }
212
  },
213
  { name: 'db-migrations' }
214
)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc