How to run all edge functions in local development #6786
Replies: 7 comments 34 replies
-
@waynebloss thanks for your solution. I was wondering if you use the js api to invoke the functions? e.g. await supabase.functions.invoke('my_function') I cannot find a way to set this url. But maybe i might have to settle for making my own fetch calls for now, still helps a lot thanks for sharing |
Beta Was this translation helpful? Give feedback.
-
I tried this but the first function binds to port 3000 and the second one cant be served since it tried to run it on the same port. I am also assuming that Do I need another handler file for each function that declares a different port for each in the serve? |
Beta Was this translation helpful? Give feedback.
-
Great solution! I just started using it. I wanted to know if you've given any thought to using a mix of jwt verified and non-verified handlers (e.g. verified for user-initiated functions and non-verified for webhook functions). I guess since this will be only for local environments I can just authenticate the requests to normally non-verified functions, but I was interested if there's a simple solution for testing all the functions together how they will be in production Thank you again for sharing++ |
Beta Was this translation helpful? Give feedback.
-
Whats in your |
Beta Was this translation helpful? Give feedback.
-
Great hack, I'd love the ability to dev multiple functions locally. I'm currently working on a project where there is a lot of logical dependencies between functions and it's very tedious to test the whole thing running. |
Beta Was this translation helpful? Give feedback.
-
Great solution! It's been a life saver for me. Today I wanted to add an oak server function to our project. It could just be me not understanding fully, but I couldn't get it to work with http/server. I had to modify the localdev function a bit to use Deno.listen:
// Solution from: https://github.com/supabase/supabase/discussions/6786
// Modified to use Deno functions instead of std/http for oak
import { HandleMethod } from 'https://deno.land/x/oak@v11.1.0/application.ts';
// Local
import { corsHeaders } from '../_shared/cors.ts';
console.log('Setting up localdev function...');
// Import handlers for all functions you want to serve.
// Note: oak app handlers must be bound to the app instance
const handlers = {
hello: await import('../hello/handler.ts').then((it) => it.handler),
'invite-user': await import('../invite-user/handler.ts').then((it) => it.handler),
'make-me-admin': await import('../make-me-admin/handler.ts').then((it) => it.handler),
'oak-server': await import('../oak-server/app.ts').then((it) => it.app.handle.bind(it.app))
} as Record<string, HandleMethod>;
// Handle incoming requests and route to the appropriate function handler
function localdevHandler(req: Request, conn: Deno.Conn) {
// This is needed if you're planning to invoke your function from a browser.
if (req.method === 'OPTIONS') {
return new Response('OK', { headers: corsHeaders });
}
console.log(`${req.method} ${req.url}`);
const url = new URL(req.url);
const [, handlerName] = url.pathname.match(/^\/?([^\/]+)/m) ?? [];
try {
if (!handlerName) {
throw Error(`localdev: No handler name found in URL path ${url.pathname}`);
}
const handler = handlers[handlerName];
if (!handler) {
throw Error(
`localdev: No matching handler function found for ${handlerName} in ${JSON.stringify(Object.keys(handlers))}`
);
}
return handler(req, conn);
} catch (err) {
console.error(err.message);
return new Response(JSON.stringify({ error: err.message }), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
status: 500
});
}
}
// Listen for incoming requests
const listener = Deno.listen({ hostname: 'localhost', port: 8000 });
for await (const conn of listener) {
(async () => {
const requests = Deno.serveHttp(conn);
for await (const { request, respondWith } of requests) {
const response = await localdevHandler(request, conn);
if (response) {
respondWith(response);
} else {
respondWith(new Response('Not Found', { status: 404 }));
}
}
})();
}
import { app } from './app.ts';
await app.listen({ port: 8000 });
import { Application, Router } from 'https://deno.land/x/oak@v11.1.0/mod.ts';
const router = new Router();
router
// Note: path will be prefixed with function name
.get('/oak-server', (context) => {
context.response.body = 'This is an example Oak server running on Edge Functions!';
})
.post('/oak-server/greet', async (context) => {
// Note: request body will be streamed to the function as chunks, set limit to 0 to fully read it.
const result = context.request.body({ type: 'json', limit: 0 });
const body = await result.value;
const name = body.name || 'you';
context.response.body = { msg: `Hey ${name}!` };
})
.get('/oak-server/redirect', (context) => {
context.response.redirect('https://www.example.com');
});
export const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods()); Refs: |
Beta Was this translation helpful? Give feedback.
-
Since CLI v1.38.6 this has been added natively when running |
Beta Was this translation helpful? Give feedback.
-
Please note: Since CLI v1.38.6 this has been added natively when running
supabase functions serve
without any function name: https://supabase.com/docs/reference/cli/supabase-functions-serveHello, just wanted to share how I've been running all of my edge functions at once in local development.
I create one single edge function that imports all the others and acts as a proxy for them. Then, I run:
Here's an example from my project file at
supabase/functions/localdev/index.ts
:Beta Was this translation helpful? Give feedback.
All reactions