[{"data":1,"prerenderedAt":16},["ShallowReactive",2],{"$fdm9GmkrN8-PRdQKF-OaZzotSDR81LF4X91wfU3jnr9U":3},{"slug":4,"title":5,"description":6,"date":7,"tags":8,"draft":13,"readingMinutes":14,"html":15},"supabase-edge-functions","Supabase Edge Functions: The Limits Lovable Won't Mention","Supabase Edge Functions explained for app builders: the 2-second CPU limit, the 150-second timeout, what they cost, and the code AI tools get wrong. October 2026.","2026-10-19",[9,10,11,12],"supabase","edge-functions","deno","lovable",false,4,"\u003Cp>Supabase Edge Functions are small TypeScript programs that run on Supabase&#39;s Deno-based edge runtime, close to your users, whenever your app calls them. They&#39;re where a Supabase app keeps the code that can&#39;t run in the browser: Stripe webhooks, emails, calls to OpenAI with a secret key, anything that needs a password the user mustn&#39;t see. If you built your app with Lovable, you have them whether you know it or not, because Lovable writes your backend code as edge functions and deploys them for you.\u003C/p>\n\u003Cp>That last part is why I&#39;m writing this. When a tool writes the functions, nobody reads the limits page until something returns a 504. So here are the numbers, from Supabase&#39;s own \u003Ca href=\"https://supabase.com/docs/guides/functions/limits\">Edge Functions limits page\u003C/a>, checked October 19, 2026.\u003C/p>\n\u003Ch2>The four limits that actually bite\u003C/h2>\n\u003Cp>\u003Cstrong>2 seconds of CPU time per request.\u003C/strong> Not wall time: CPU time. Waiting on a database query or an API call doesn&#39;t count, only the time your code spends computing. Parsing a big CSV, resizing images in JavaScript, or looping over 50,000 rows to build a report will hit it. Most webhook and email functions use a few milliseconds and never come close.\u003C/p>\n\u003Cp>\u003Cstrong>150 seconds of idle time.\u003C/strong> If the function hasn&#39;t sent a response 150 seconds after the request came in, the caller gets a \u003Ccode>504 Gateway Timeout\u003C/code>. This is the one AI features trip over: a function that waits for a long model answer before replying, or calls three APIs one after another, each slow on a bad day.\u003C/p>\n\u003Cp>\u003Cstrong>256 MB of memory.\u003C/strong> Fine for JSON. Not fine for loading a 100 MB PDF into memory to pull text out of it.\u003C/p>\n\u003Cp>\u003Cstrong>Wall clock: 150 seconds on the free plan, 400 on paid plans.\u003C/strong> That&#39;s how long a worker can stay alive, background work included. If you return a response early and keep working with \u003Ccode>EdgeRuntime.waitUntil()\u003C/code>, the extra work still has to finish inside this window.\u003C/p>\n\u003Cimg src=\"/blog/supabase-edge-functions/edge-function-request-timeline.webp\" alt=\"Timeline of one Supabase Edge Function request: short CPU slices count against the 2-second CPU limit, waiting on APIs only counts toward the 150-second timeout\" width=\"1600\" height=\"820\" loading=\"lazy\">\u003Cp>There are quieter limits too, and they explain a lot of strange bugs. Outgoing connections to ports 25 and 587 are blocked, so sending email over SMTP from a function won&#39;t work: use an email API like Resend or Postmark over HTTPS instead. Node libraries that need multithreading, like \u003Ccode>sharp\u003C/code> and \u003Ccode>libvips\u003C/code>, aren&#39;t supported, so the obvious way to make thumbnails isn&#39;t available. And a function only serves real HTML on a custom domain. Without one, a GET that returns \u003Ccode>text/html\u003C/code> comes back as \u003Ccode>text/plain\u003C/code>, which is very confusing the first time you see your page as raw source.\u003C/p>\n\u003Ch2>What they cost\u003C/h2>\n\u003Cp>Almost nothing, for most apps. Supabase&#39;s \u003Ca href=\"https://supabase.com/docs/guides/functions/pricing\">Edge Functions pricing\u003C/a> gives the Free plan 500,000 invocations a month and Pro and Team 2 million, then charges $2 per million after that. An app with a thousand daily users calling a function ten times a day stays around 300,000 invocations a month.\u003C/p>\n\u003Cp>The bill problems I&#39;d look for are elsewhere: a function on a schedule that runs every minute for no reason, or a function that calls an AI model on every page load. The invocations are cheap. The model calls they make aren&#39;t.\u003C/p>\n\u003Ch2>The code AI tools get wrong\u003C/h2>\n\u003Cp>None of this is specific to Lovable. It&#39;s what any code generator does when it writes for a runtime it knows less well than Node.\u003C/p>\n\u003Cp>\u003Cstrong>CORS.\u003C/strong> A function called from the browser has to answer the \u003Ccode>OPTIONS\u003C/code> preflight request and send the right \u003Ccode>Access-Control-Allow-*\u003C/code> headers on every response, errors included. Generated code often adds the headers to the success response and forgets the error path, so the real error shows up in the browser console as a CORS failure, and you go hunting for the wrong bug.\u003C/p>\n\u003Cp>\u003Cstrong>Stripe webhooks.\u003C/strong> Stripe&#39;s signature check needs the raw request body, and in Deno you have to use the async version, \u003Ccode>stripe.webhooks.constructEventAsync\u003C/code>, with Stripe&#39;s SubtleCrypto provider. The synchronous \u003Ccode>constructEvent\u003C/code> from every Node tutorial fails here. Supabase has a \u003Ca href=\"https://supabase.com/docs/guides/functions/examples/stripe-webhooks\">Stripe webhook example\u003C/a> that gets it right. If your payments &quot;sometimes don&#39;t register&quot;, check this first. I covered the rest of that topic in \u003Ca href=\"/blog/stripe-webhooks\">Stripe webhooks: the parts that break in production\u003C/a>.\u003C/p>\n\u003Cp>\u003Cstrong>Auth.\u003C/strong> By default a function checks that the caller sent a valid JWT. Webhooks from Stripe or GitHub don&#39;t send one, so someone turns verification off for that function, and the generated code may turn it off more broadly than it should. A function that changes data and accepts calls from anyone is worth ten minutes of your time to check.\u003C/p>\n\u003Cp>\u003Cstrong>Long work in a request.\u003C/strong> Anything that might take more than a few seconds (generating a report, processing an upload, a batch of emails) belongs in a queue, not in the request. On Supabase that can be \u003Ccode>pgmq\u003C/code> with a scheduled function draining it. If you&#39;re on Node elsewhere, \u003Ca href=\"/blog/bullmq\">BullMQ\u003C/a> is what I&#39;d use.\u003C/p>\n\u003Ch2>When to move code off edge functions\u003C/h2>\n\u003Cp>Edge functions are good at short, stateless work: receive a webhook, check it, write a row, reply. I keep them for that. When a function starts needing more than a couple of seconds of CPU, a native image library, or a long-running connection, it&#39;s time for a small server or a worker, not a cleverer function. That&#39;s a normal step in an app&#39;s life, not a failure of the first version.\u003C/p>\n\u003Cp>If your app was built with Lovable and the edge functions are where things have started breaking, that&#39;s exactly the work my agency \u003Ca href=\"https://lovable2live.com/\">Lovable 2 Live\u003C/a> does. For anything else, \u003Ca href=\"https://calendly.com/director-norsky/meeting\">book a free call\u003C/a>. All consultation is free.\u003C/p>\n",1792378921712]