applySchema method
Applies schemaSql to the project identified by projectRef.
accessToken may be either a Supabase personal access token (PAT) or
an organization access token — both use the same Bearer format. The
token is NOT stored anywhere; it is used once for this request and then
falls out of scope.
Throws ProvisioningException with a user-friendly message on any failure. The token is never included in exception messages or logs.
Implementation
Future<void> applySchema({
required String projectRef,
required String accessToken,
required String schemaSql,
}) async {
final url = '$_baseUrl/v1/projects/$projectRef/database/query';
try {
final response = await _dio.post<dynamic>(
url,
data: {'query': schemaSql},
options: Options(
headers: {
'Authorization': 'Bearer $accessToken',
'Content-Type': 'application/json',
'User-Agent': 'dutato-setup',
},
sendTimeout: _timeout,
receiveTimeout: _timeout,
// Don't throw on non-2xx; we handle status mapping below so we can
// surface a useful error to the user.
validateStatus: (_) => true,
),
);
final status = response.statusCode ?? 0;
if (status >= 200 && status < 300) {
dev.log(
'Schema applied to project $projectRef (HTTP $status).',
name: 'SupabaseProvisioner',
);
return;
}
// Idempotency: if the failure is the bundler's own bail-out guard
// ("project already has _schema_meta"), there's nothing to apply.
// Treat it as a no-op success so callers don't have to special-case
// already-bootstrapped projects.
if (_isAlreadyBootstrapped(response.data)) {
dev.log(
'Schema already bootstrapped on project $projectRef — skipping.',
name: 'SupabaseProvisioner',
);
return;
}
throw ProvisioningException(
_mapStatus(status, projectRef, response.data),
);
} on DioException catch (e) {
if (e.type == DioExceptionType.connectionTimeout ||
e.type == DioExceptionType.sendTimeout ||
e.type == DioExceptionType.receiveTimeout) {
throw ProvisioningException(
'Timed out while applying the schema. Try again, or use the '
'manual fallback.',
);
}
if (e.type == DioExceptionType.connectionError) {
throw ProvisioningException(
'Could not reach Supabase. Check your network and try again.',
);
}
// Log without the auth header.
dev.log(
'Dio error during provisioning: ${e.type} ${e.message}',
name: 'SupabaseProvisioner',
);
throw ProvisioningException(
'Network error while applying the schema. Try again.',
);
}
}