Skip to main content
RapidDev - Software Development Agency
lovable-integrationsEdge Function Integration

How to Integrate Lovable with Wasabi

Connect Wasabi to Lovable using its S3-compatible API through a Supabase Edge Function. Wasabi charges $0.0059/GB stored with zero egress fees on all traffic — making it cost-effective for read-heavy apps with frequent downloads. Store Wasabi Access Key and Secret Key in Lovable's Cloud Secrets, then use the AWS SDK v3 with Wasabi's regional endpoint in the Edge Function. All file operations route through the function, keeping credentials server-side.

What you'll learn

  • How Wasabi's zero-egress pricing model works and when it saves money over B2 or S3
  • How to create a Wasabi bucket and obtain access credentials
  • How to configure the AWS SDK v3 S3Client with Wasabi's regional endpoint in a Deno Edge Function
  • How to generate pre-signed download URLs from Wasabi for direct browser access to private files
  • How to choose between Wasabi, Backblaze B2, and AWS S3 for your specific Lovable app use case
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate15 min read40 minutesStorageMarch 2026RapidDev Engineering Team
TL;DR

Connect Wasabi to Lovable using its S3-compatible API through a Supabase Edge Function. Wasabi charges $0.0059/GB stored with zero egress fees on all traffic — making it cost-effective for read-heavy apps with frequent downloads. Store Wasabi Access Key and Secret Key in Lovable's Cloud Secrets, then use the AWS SDK v3 with Wasabi's regional endpoint in the Edge Function. All file operations route through the function, keeping credentials server-side.

Wasabi Integration in Lovable: Zero-Egress Hot Storage

Wasabi's key differentiator is its pricing model: zero egress fees and zero API request fees, with storage at $0.0059/GB. For applications where files are downloaded frequently — media players, document viewers, image galleries, software distribution — egress costs on AWS S3 ($0.09/GB) or even Backblaze B2 ($0.01/GB outside Cloudflare) can become significant. Wasabi eliminates this variable cost entirely.

The practical implication for Lovable apps: if your app stores 50 GB and users download 500 GB per month (e.g., a video library or software download portal), AWS S3 would cost about $46.15/month (storage + egress). Wasabi would cost about $0.295/month (storage only). Even compared to B2 with Cloudflare CDN (which also achieves zero egress), Wasabi is simpler to set up for zero-egress operation — no CDN configuration required.

Like Backblaze B2, Wasabi uses the S3-compatible API with AWS Signature v4 authentication. The integration code is nearly identical to an S3 or B2 integration — point the AWS SDK's S3Client at Wasabi's endpoint URL and use Wasabi credentials. This makes Wasabi straightforward to adopt if you have existing S3 integration experience. The Edge Function pattern remains the same: credentials in Cloud Secrets, all operations proxied server-side, frontend communicates only with the Edge Function.

Integration method

Edge Function Integration

Wasabi integrates with Lovable via a Supabase Edge Function that uses Wasabi's S3-compatible API with AWS SDK v3. Store Wasabi Access Key ID and Secret Access Key in Lovable's Cloud Secrets, configure the S3Client with Wasabi's regional endpoint, and build an Edge Function that handles file upload, listing, download URL generation, and deletion. The React frontend calls the Edge Function — never Wasabi directly — eliminating CORS issues and keeping credentials server-side.

Prerequisites

  • A Wasabi account at wasabi.com (free trial with 1 TB storage for 30 days, then $0.0059/GB/month)
  • An active Lovable project with Lovable Cloud/Supabase enabled
  • Access to Lovable's Cloud tab → Secrets panel
  • Basic understanding of S3-compatible object storage (buckets, objects, keys, pre-signed URLs)

Step-by-step guide

1

Create a Wasabi bucket and generate access credentials

Log in to your Wasabi account at console.wasabisys.com. In the left sidebar, click 'Buckets' and then 'Create Bucket'. Enter a unique bucket name (bucket names must be globally unique across Wasabi), and select the region closest to your users — Wasabi offers US, EU, and AP regions with different endpoint URLs. The region determines the S3-compatible endpoint you will use; note it as you create the bucket (e.g., us-east-1 uses s3.wasabisys.com, eu-central-1 uses s3.eu-central-1.wasabisys.com). Set the bucket access to private. Leave versioning disabled unless your app specifically needs file version history — enabling it creates storage costs for each version retained. Click 'Create Bucket' to complete the setup. Next, create Access Keys for API authentication. In the Wasabi console, click your account name in the top right → 'Access Keys'. Click 'Create New Access Key'. Wasabi presents a Root User Access Key (has full account access — avoid using this) and recommends creating Sub-User Access Keys for applications. However, Wasabi's sub-user system is under their IAM section: go to IAM → Users → Create User, create a user named 'lovable-app', and under Policies, attach a policy that allows s3:GetObject, s3:PutObject, s3:DeleteObject, and s3:ListBucket on your specific bucket ARN. For faster setup: use the root access key during development (acceptable for initial testing). Switch to a scoped sub-user key before going to production. After creating the access key, Wasabi shows the Access Key ID and Secret Access Key once — copy both values immediately. The Secret Key is never shown again.

Pro tip: Choose your Wasabi region carefully — data cannot be moved between regions later without creating a new bucket and migrating files. Select the region closest to most of your users for the best download performance.

Expected result: You have a private Wasabi bucket with its region and endpoint URL noted, and Access Key ID plus Secret Access Key copied and ready to store as Lovable secrets.

2

Store Wasabi credentials in Lovable Cloud Secrets

Open your Lovable project and navigate to the Cloud tab by clicking '+' next to the Preview. Click 'Secrets' to open the encrypted environment variable panel. Add the following secrets for Wasabi. Add WASABI_ACCESS_KEY_ID with your Wasabi Access Key ID. Add WASABI_SECRET_ACCESS_KEY with your Wasabi Secret Access Key. Add WASABI_BUCKET_NAME with the name of the bucket you created. Add WASABI_ENDPOINT with the full HTTPS endpoint URL for your bucket's region. Wasabi's endpoints follow this format: for us-east-1 use https://s3.wasabisys.com; for us-east-2 use https://s3.us-east-2.wasabisys.com; for us-west-1 use https://s3.us-west-1.wasabisys.com; for eu-central-1 use https://s3.eu-central-1.wasabisys.com; for ap-northeast-1 use https://s3.ap-northeast-1.wasabisys.com. Add WASABI_REGION with just the region identifier (e.g., us-east-1). For each secret: click the '+' button in the Secrets panel, type the exact key name, paste the value without any surrounding whitespace, and save. Key names are case-sensitive and must exactly match the names used in the Edge Function's Deno.env.get() calls. Never paste these credentials into the Lovable chat window. Credentials in chat history are visible and persisted — always use the Secrets panel. Lovable's security infrastructure — which includes SOC 2 Type II and ISO 27001 certifications — encrypts all secrets at rest and makes them accessible only to your Edge Functions.

Lovable Prompt

I've added my Wasabi credentials to Cloud Secrets: WASABI_ACCESS_KEY_ID, WASABI_SECRET_ACCESS_KEY, WASABI_BUCKET_NAME, WASABI_ENDPOINT, and WASABI_REGION are all configured. Create a Supabase Edge Function called 'wasabi-storage' that uses these to handle file upload, listing, pre-signed URL generation, and deletion using Wasabi's S3-compatible API.

Paste this in Lovable chat

Pro tip: Wasabi endpoint URLs are region-specific. Using the wrong endpoint (e.g., s3.wasabisys.com for a bucket in eu-central-1) causes misleading errors. Verify the endpoint URL in the Wasabi console by looking at the bucket's properties page which shows the correct endpoint.

Expected result: Five secrets are stored in Cloud → Secrets: WASABI_ACCESS_KEY_ID, WASABI_SECRET_ACCESS_KEY, WASABI_BUCKET_NAME, WASABI_ENDPOINT, and WASABI_REGION. No credential values appear in any source code or chat history.

3

Build the Wasabi S3-compatible Edge Function

The Wasabi Edge Function is structurally identical to a Backblaze B2 integration — both use the AWS SDK v3 S3Client pointed at a custom endpoint. The only configuration difference is the endpoint URL and credentials. This makes Wasabi adoption straightforward if you have seen an S3 or B2 integration. The key S3Client configuration for Wasabi requires: setting endpoint to your WASABI_ENDPOINT value, setting region to your WASABI_REGION, providing credentials from environment variables, and setting forcePathStyle: true (Wasabi, like B2, requires path-style URLs). Without forcePathStyle, the SDK constructs virtual-hosted-style URLs (bucket.s3.wasabisys.com) that Wasabi does not support. For pre-signed download URLs, Wasabi generates URLs that allow direct browser access to private objects without routing through the Edge Function. This is particularly important for large files: a 500 MB video should be delivered directly from Wasabi to the user's browser, not proxied through your Edge Function which has request size limits. The getSignedUrl function from @aws-sdk/s3-request-presigner works with Wasabi using the same S3Client configuration. Wasabi pre-signed URLs can have expiry times from 1 second to 7 days. For downloadable content, 1-24 hours is appropriate. For streaming media where users play videos in a browser player, set a longer expiry (24 hours) to avoid the stream failing mid-playback if the URL expires. Pre-signed URLs for Wasabi use the same format as AWS S3 pre-signed URLs — query parameters with X-Amz-Expires and signature values.

Lovable Prompt

Create a Supabase Edge Function at supabase/functions/wasabi-storage/index.ts using Wasabi's S3-compatible API. Import S3Client, PutObjectCommand, ListObjectsV2Command, DeleteObjectCommand from npm:@aws-sdk/client-s3 and getSignedUrl, GetObjectCommand from the pre-signer package. Configure the client with WASABI_ENDPOINT, WASABI_REGION, WASABI_ACCESS_KEY_ID, WASABI_SECRET_ACCESS_KEY from environment variables, and forcePathStyle: true. Handle actions: list (with optional prefix), upload (base64 file content), get-download-url (pre-signed URL, 1 hour expiry by default but accept an optional expiresIn parameter), delete. Include full CORS handling and descriptive error messages.

Paste this in Lovable chat

supabase/functions/wasabi-storage/index.ts
1import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
2import {
3 S3Client,
4 PutObjectCommand,
5 ListObjectsV2Command,
6 DeleteObjectCommand,
7 GetObjectCommand,
8} from 'npm:@aws-sdk/client-s3'
9import { getSignedUrl } from 'npm:@aws-sdk/s3-request-presigner'
10
11const corsHeaders = {
12 'Access-Control-Allow-Origin': '*',
13 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
14 'Access-Control-Allow-Methods': 'POST, OPTIONS',
15}
16
17serve(async (req) => {
18 if (req.method === 'OPTIONS') {
19 return new Response('ok', { headers: corsHeaders })
20 }
21
22 const endpoint = Deno.env.get('WASABI_ENDPOINT')
23 const region = Deno.env.get('WASABI_REGION')
24 const accessKeyId = Deno.env.get('WASABI_ACCESS_KEY_ID')
25 const secretAccessKey = Deno.env.get('WASABI_SECRET_ACCESS_KEY')
26 const bucket = Deno.env.get('WASABI_BUCKET_NAME')
27
28 if (!endpoint || !region || !accessKeyId || !secretAccessKey || !bucket) {
29 return new Response(
30 JSON.stringify({ error: 'Missing Wasabi environment variables. Check Cloud Secrets.' }),
31 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
32 )
33 }
34
35 const s3 = new S3Client({
36 endpoint,
37 region,
38 credentials: { accessKeyId, secretAccessKey },
39 forcePathStyle: true,
40 })
41
42 try {
43 const body = await req.json()
44 const { action, key, prefix, content, contentType, expiresIn } = body
45
46 if (action === 'list') {
47 const result = await s3.send(new ListObjectsV2Command({
48 Bucket: bucket,
49 Prefix: prefix || '',
50 Delimiter: '/',
51 }))
52 return new Response(JSON.stringify({
53 files: result.Contents ?? [],
54 folders: result.CommonPrefixes ?? [],
55 count: result.KeyCount ?? 0,
56 }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } })
57
58 } else if (action === 'upload') {
59 if (!key || !content) throw new Error('key and content are required for upload')
60 const bytes = Uint8Array.from(atob(content), c => c.charCodeAt(0))
61 await s3.send(new PutObjectCommand({
62 Bucket: bucket,
63 Key: key,
64 Body: bytes,
65 ContentType: contentType || 'application/octet-stream',
66 }))
67 return new Response(JSON.stringify({ success: true, key }),
68 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } })
69
70 } else if (action === 'get-download-url') {
71 if (!key) throw new Error('key is required')
72 const ttl = Number(expiresIn) || 3600
73 const url = await getSignedUrl(
74 s3,
75 new GetObjectCommand({ Bucket: bucket, Key: key }),
76 { expiresIn: ttl }
77 )
78 return new Response(JSON.stringify({ url, expiresIn: ttl }),
79 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } })
80
81 } else if (action === 'delete') {
82 if (!key) throw new Error('key is required')
83 await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }))
84 return new Response(JSON.stringify({ success: true }),
85 { headers: { ...corsHeaders, 'Content-Type': 'application/json' } })
86 }
87
88 throw new Error(`Unknown action: ${action}`)
89
90 } catch (err) {
91 const message = err instanceof Error ? err.message : 'Unknown error'
92 return new Response(JSON.stringify({ error: message }),
93 { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } })
94 }
95})

Pro tip: For video streaming use cases, set expiresIn to 86400 (24 hours) when generating pre-signed URLs. Shorter expiry times can cause video playback to fail mid-stream if the user watches a long video that exceeds the URL's expiry window.

Expected result: The wasabi-storage Edge Function is deployed and active in Lovable Cloud. Test it by calling the list action and verifying your Wasabi bucket contents are returned. Upload a small test file and confirm it appears in the bucket in the Wasabi console.

4

Build the React interface and connect to Wasabi

With the Edge Function deployed, build the React components for your specific Wasabi use case. The frontend calls supabase.functions.invoke('wasabi-storage', { body: { action, ...params } }) for all storage operations, processing responses and updating state accordingly. For media-heavy applications (the primary Wasabi use case), build components that handle large file uploads gracefully. Add client-side validation for file type and size before starting the upload. For files over 5 MB, show a progress indicator using XMLHttpRequest or the Fetch API with progress events — reading large files as base64 before sending can take several seconds and the user needs visual feedback. For video or audio content, the pre-signed download URL from Wasabi can be used directly in an HTML5 video or audio element src attribute. This streams the media directly from Wasabi to the user's browser with no intermediary. The Edge Function only generates the URL — the actual file data never passes through it. Set a generous pre-signed URL expiry (6-24 hours) for media to ensure playback sessions do not expire. For production file management, store all file metadata in Supabase: the Wasabi object key, original filename, file size, MIME type, upload timestamp, and uploader's user ID. This enables building a rich file library with Supabase queries — filtering by type, searching by name, sorting by date — without needing to call Wasabi's list API. The Wasabi pre-signed URL is only fetched when the user actually needs to access a file. For applications with user-generated content, add file key namespacing: prefix all keys with the user's Supabase user ID (uploads/{userId}/{filename}) to logically separate files by user. This does not enforce access control at the storage level (B2 and Wasabi bucket policies are less granular than S3 IAM), but combined with Supabase RLS on the metadata table, ensures users only see their own files in your app's UI.

Lovable Prompt

Build a media library component using the wasabi-storage Edge Function. The library shows a grid of uploaded media files (images and videos). Each card shows a thumbnail (for images, fetch the pre-signed URL and use it as img src; for videos, show a video icon with filename). Clicking a card opens a modal: images show the full image, videos show an HTML5 video player with the pre-signed URL as the source. Add an upload button that accepts image and video files up to 200 MB, shows upload progress, and saves metadata to a Supabase media_files table with columns: id, user_id, wasabi_key, filename, mime_type, size_bytes, created_at. The grid loads from Supabase, not from Wasabi list calls.

Paste this in Lovable chat

Pro tip: When using Wasabi pre-signed URLs in HTML5 video elements, add crossOrigin='anonymous' to the video tag. Some browsers require this for CORS-enabled video sources, and Wasabi's pre-signed URLs work with cross-origin media requests when the attribute is present.

Expected result: A functional media library loads in your Lovable app. Files upload to Wasabi via the Edge Function, metadata is stored in Supabase, the grid renders from Supabase queries, and media playback uses pre-signed Wasabi URLs directly in browser media elements — with no egress fees regardless of how many times users view the content.

Common use cases

Video and media library with high download volume

A Lovable app that serves video content, audio files, or high-resolution images to many users generates substantial egress costs on AWS S3. Wasabi eliminates these costs with zero egress fees, making it ideal for media libraries, e-learning platforms with video courses, stock media sites, or any application where files are downloaded many times.

Lovable Prompt

Build a video library feature using the wasabi-storage Edge Function. Users can upload MP4 files up to 500 MB. Display uploaded videos in a grid with thumbnails, title, and file size. Each video card has a 'Watch' button that gets a pre-signed URL from Wasabi and opens the video in a modal player. Show the total storage used by the current user. Store video metadata (title, key, size, duration, thumbnail_url) in Supabase.

Copy this prompt to try it in Lovable

Software distribution portal for downloadable files

Apps that distribute software installers, data exports, or large downloadable assets to many users would incur high egress costs on bandwidth-priced storage. Wasabi's zero-egress model makes it the obvious choice: store binaries, exports, and downloads in Wasabi and users download as many times as needed with no additional per-GB cost.

Lovable Prompt

Create a file distribution portal using Wasabi storage. Admins can upload files (ZIP, EXE, DMG, PDF) to specific version folders in Wasabi. Users see a downloads page listing available files by category and version. Each file shows its version, size, and release date. Clicking Download gets a pre-signed URL valid for 15 minutes and starts the download. Track download counts in a Supabase downloads table each time a URL is generated.

Copy this prompt to try it in Lovable

User-generated content platform with unpredictable traffic

Content platforms where user uploads become viral or unexpectedly popular face unpredictable egress costs on bandwidth-priced storage — a single popular file downloaded millions of times could generate a large unexpected bill. Wasabi's flat storage-only pricing makes costs completely predictable regardless of how many times files are downloaded.

Lovable Prompt

Build a content sharing feature using Wasabi for storage. Users upload images and short videos (max 50 MB). Content appears in a public feed visible to all users. Each piece of content can be viewed (opens pre-signed URL) any number of times with no per-view cost. Store content metadata in Supabase with user_id, wasabi_key, mime_type, created_at. Implement infinite scroll for the feed loading 20 items at a time from Supabase.

Copy this prompt to try it in Lovable

Troubleshooting

Edge Function throws 'AuthorizationHeaderMalformed' or 'SignatureDoesNotMatch' errors

Cause: The AWS Signature v4 authentication requires the region value to match the bucket's actual region exactly. Using the wrong region identifier or an endpoint URL from a different region than the bucket causes signature validation to fail. Also occurs if the secret key contains extra whitespace from copy-pasting.

Solution: Check your WASABI_ENDPOINT and WASABI_REGION secrets match each other. For example, if WASABI_ENDPOINT is https://s3.eu-central-1.wasabisys.com, then WASABI_REGION must be eu-central-1. Find the correct endpoint for your bucket by navigating to Wasabi console → Buckets → your bucket → Properties — it shows the exact endpoint URL. Delete and re-enter the secrets to eliminate whitespace issues.

Pre-signed video URLs stop working mid-playback

Cause: The pre-signed URL was generated with a short expiry (e.g., 1 hour) and the user is watching a long video that exceeds that window. When the URL expires, the video player receives a 403 error and stops playing.

Solution: Generate video pre-signed URLs with a 24-hour expiry (expiresIn: 86400 in the Edge Function call). For very long content (courses, long-form videos), consider 7-day expiry (604800 seconds). Wasabi supports up to 7 days for pre-signed URL expiry. Pass the expiresIn value as a parameter from the frontend when requesting a download URL for video content.

ListObjectsV2 returns no files even though files exist in the Wasabi bucket

Cause: The list action uses a Prefix parameter. If a non-empty prefix is passed and does not match the actual file path prefix, no results are returned. Also occurs if the bucket name in the WASABI_BUCKET_NAME secret does not exactly match the bucket name in Wasabi.

Solution: Call the list action with prefix: '' to list all root-level objects without prefix filtering. Verify the WASABI_BUCKET_NAME secret matches the exact bucket name in the Wasabi console (bucket names are case-sensitive). Check whether files are stored under a subfolder prefix that requires a non-empty prefix parameter.

Best practices

  • Wasabi's zero egress pricing benefit is most significant for read-heavy apps — calculate whether your app's download pattern justifies Wasabi over Backblaze B2 (which has zero egress only via Cloudflare) before choosing.
  • Always use forcePathStyle: true when configuring the AWS SDK S3Client for Wasabi — Wasabi does not support virtual-hosted-style bucket URLs.
  • For media files served in browser players, set pre-signed URL expiry to 24 hours to prevent playback interruptions from expired URLs.
  • Store all file metadata in Supabase and use Wasabi only for actual file storage — Supabase queries for file listings are faster and cheaper than Wasabi ListObjects API calls.
  • Namespace all Wasabi object keys with user IDs (uploads/{userId}/{filename}) to logically separate user data and simplify per-user file management.
  • Never use Wasabi's root access key in production — create a scoped sub-user with only the S3 permissions your app needs (GetObject, PutObject, DeleteObject, ListBucket).
  • Add file type validation on the frontend before sending to the Edge Function — reject unexpected MIME types early rather than storing potentially unsafe files.

Alternatives

Frequently asked questions

What makes Wasabi different from Backblaze B2 for Lovable integrations?

Both are S3-compatible and significantly cheaper than AWS S3. The key difference is egress fees: Wasabi has zero egress fees unconditionally, while B2 has zero egress fees only for traffic routed through Cloudflare's CDN. For Lovable apps that serve files directly (without a CDN) or use a non-Cloudflare CDN, Wasabi saves more. For apps already using Cloudflare, B2 is slightly cheaper on storage ($0.006/GB vs $0.0059/GB). The integration code is nearly identical for both.

Does Wasabi have an equivalent to Lovable's native AWS S3 connector?

No, Wasabi does not have a native Lovable shared connector. Integration requires building a custom Edge Function as described in this guide. Only AWS S3 has a native connector in Lovable's 17 shared connectors. However, since Wasabi uses the S3-compatible API, the Edge Function code structure is the same as an S3 integration — just with different endpoint and credential values.

What is Wasabi's minimum storage duration policy?

Wasabi charges a minimum of 90 days for each object stored. If you delete an object before 90 days, Wasabi still charges for 90 days of storage. This minimum storage policy means Wasabi is not cost-effective for frequently deleted temporary files — use Lovable Cloud Storage or S3 for short-lived temporary files. For long-term storage of media, documents, or archives, the 90-day minimum is not a concern.

Can I use Wasabi for serving public files without a pre-signed URL?

Yes. You can set a Wasabi bucket to public, which makes all objects accessible via a direct URL (https://s3.REGION.wasabisys.com/BUCKET/KEY). This is simpler for public content like product images or public downloads. However, public buckets have no access control — any file in the bucket is accessible to anyone who knows the URL. For user-specific files, always use private buckets with pre-signed URLs.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.