Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remotion/lambda/client too large to be used in Supabase Edge Functions? #4862

Open
tristanbob opened this issue Feb 5, 2025 · 10 comments
Open

Comments

@tristanbob
Copy link

tristanbob commented Feb 5, 2025

Problem

The @remotion/lambda client package is too large to be used in Supabase Edge Functions, which have a 20MB size limit. When trying to use the Lambda client in a Supabase Edge Function, the bundle size exceeds 34MB, resulting in a 413 error (request entity too large).

Feature Request

It would be valuable to have a lightweight version of the Lambda client that can run within Supabase Edge Functions' 20MB limit. This could be achieved by:

  1. Creating a minimal client package that only handles core Lambda operations
  2. Splitting the package into smaller modules that can be imported separately
  3. Optimizing dependencies to reduce bundle size

Technical Details

  • Current bundle size: ~34.9MB
  • Target size: < 20MB
  • Environment: Deno-based Supabase Edge Functions
  • Related packages: @remotion/[email protected]

Impact

This would enable developers to use Remotion Lambda directly from Edge Functions, simplifying architectures and reducing infrastructure complexity.

Additional Context

Supabase Edge Functions run on Deno and have specific limitations around package size and imports. Supporting this environment would make Remotion more accessible to developers using Supabase's serverless platform.

Error when deploying edge function:

Bundling Function: start-render
Download https://jsr.io/@supabase/functions-js/meta.json
Download https://jsr.io/@supabase/functions-js/2.4.4_meta.json
Deploying Function: start-render (script size: 34.9MB)
unexpected status 413: {"message":"request entity too large"}

Example Supabase Edge Function

import { createClient } from "npm:@supabase/[email protected]";
import {
  renderMediaOnLambda,
  speculateFunctionName,
} from "npm:@remotion/[email protected]/client";
import { corsHeaders } from "../_shared/cors.ts";
import "jsr:@supabase/functions-js/edge-runtime.d.ts";

console.log("Initializing start-render function");

interface RenderRequest {
  projectId: string;
  photos: any[];
  videoSettings: any;
}

console.info("Start render function initialized");

Deno.serve(async (req) => {
  // Handle CORS preflight requests
  if (req.method === "OPTIONS") {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    console.log("Start render function called");

    // Get secrets from Supabase, excluding pre-populated ones
    const {
      REMOTION_AWS_ACCESS_KEY_ID,
      AWS_ACCESS_KEY_ID,
      REMOTION_AWS_SECRET_ACCESS_KEY,
      AWS_SECRET_ACCESS_KEY,
      REMOTION_SERVE_URL,
      REMOTION_AWS_REGION,
      SUPABASE_STORAGE_BUCKET,
      SUPABASE_ACCESS_KEY_ID,
      SUPABASE_SECRET_ACCESS_KEY,
      SUPABASE_STORAGE_REGION,
    } = (await Deno.env.get("SUPABASE_SECRETS"))
      ? JSON.parse(Deno.env.get("SUPABASE_SECRETS") as string)
      : {};

    // Use pre-populated environment variables
    const SUPABASE_URL = Deno.env.get("SUPABASE_URL");
    const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY");

    // Validate AWS credentials
    if (!REMOTION_AWS_ACCESS_KEY_ID && !AWS_ACCESS_KEY_ID) {
      throw new TypeError(
        "Set up Remotion Lambda to render videos. AWS credentials are missing."
      );
    }
    if (!REMOTION_AWS_SECRET_ACCESS_KEY && !AWS_SECRET_ACCESS_KEY) {
      throw new TypeError(
        "The AWS secret access key is missing. Add it to your Supabase secrets."
      );
    }

    const supabaseClient = createClient(
      SUPABASE_URL ?? "",
      SUPABASE_SERVICE_ROLE_KEY ?? "",
      {
        auth: {
          autoRefreshToken: false,
          persistSession: false,
        },
      }
    );

    // Get the authorization header from the request
    const authHeader = req.headers.get("Authorization");
    if (!authHeader) {
      throw new Error("No authorization header");
    }

    // Get the JWT token from the authorization header
    const token = authHeader.replace("Bearer ", "");

    // Verify the JWT and get the user
    const {
      data: { user },
      error: userError,
    } = await supabaseClient.auth.getUser(token);
    if (userError || !user) {
      console.error("Invalid user token:", userError);
      throw new Error("Invalid user token");
    }

    const { projectId, photos, videoSettings }: RenderRequest =
      await req.json();
    if (!projectId || !photos || !videoSettings) {
      throw new Error("Missing required parameters");
    }

    console.log("Starting render for project:", projectId);

    // Start the Remotion Lambda render with Supabase Storage support
    const renderResult = await renderMediaOnLambda({
      serveUrl: REMOTION_SERVE_URL as string,
      composition: "VideoPreview",
      functionName: speculateFunctionName({
        memorySizeInMb: 2048,
        diskSizeInMb: 2048,
        timeoutInSeconds: 120,
      }),
      region: REMOTION_AWS_REGION as string,
      codec: "h264",
      imageFormat: "jpeg",
      inputProps: {
        photos,
        videoSettings,
      },
      outName: {
        bucketName: SUPABASE_STORAGE_BUCKET as string,
        key: `${projectId}.mp4`,
        s3OutputProvider: {
          endpoint: `${SUPABASE_URL}/storage/v1/s3`,
          accessKeyId: SUPABASE_ACCESS_KEY_ID ?? "",
          secretAccessKey: SUPABASE_SECRET_ACCESS_KEY ?? "",
          region: SUPABASE_STORAGE_REGION ?? "eu-central-1",
          forcePathStyle: true,
        },
      },
    });

    // Use EdgeRuntime.waitUntil for database operation
    const dbPromise = supabaseClient
      .from("rendered_videos")
      .insert({
        project_id: projectId,
        user_id: user.id,
        status: "processing",
        progress: 0,
        bucket_name: renderResult.bucketName,
        storage_path: `${projectId}.mp4`,
        render_config: {
          photos,
          videoSettings,
        },
      })
      .select()
      .single();

    // @ts-expect-error EdgeRuntime is available in Supabase Edge Functions
    EdgeRuntime.waitUntil(dbPromise);

    return new Response(
      JSON.stringify({
        success: true,
        renderId: projectId,
        bucketName: renderResult.bucketName,
        outputKey: `${projectId}.mp4`,
      }),
      {
        headers: {
          ...corsHeaders,
          "Content-Type": "application/json",
        },
      }
    );
  } catch (error) {
    console.error("Error in start-render:", error);
    return new Response(
      JSON.stringify({
        success: false,
        error: error instanceof Error ? error.message : String(error),
      }),
      {
        status: 400,
        headers: {
          ...corsHeaders,
          "Content-Type": "application/json",
        },
      }
    );
  }
});
@tristanbob
Copy link
Author

tristanbob commented Feb 5, 2025

I'm tagging @laktek who might be interested in this topic.
Previous change for Supabase: #4844

Semi-related, there is some new guidance on Supabase Edge Functions found here:
https://supabase.com/docs/guides/getting-started/ai-prompts/edge-functions

@JonnyBurger
Copy link
Member

You are right, I only tested it locally with the Docker setup...

We find that if @remotion/lambda/client is bundled, that is only 6MB though, mainly from the AWS SDKs, we even have a test for it:

test('Bundle should be below 6MB', async () => {
const file = await fs.promises.readFile(outfile, 'utf-8');
expect(file.length).toBeGreaterThan(10000);
expect(file.length).toBeLessThanOrEqual(6000000);
});

Therefore, before I investigate this, are you sure that it's not the Supabase Client instead that's making the bundle so big?

@tristanbob
Copy link
Author

@laktek when you have time, can you review this issue?

I tried creating a minimal video to test Remotion Lambda client running on Supabase Edge Functions but I still run into the function size problem when deploying the function.

@laktek
Copy link

laktek commented Feb 6, 2025

@tristanbob Can you share the latest code sample you have with the imports?

@laktek
Copy link

laktek commented Feb 6, 2025

@JonnyBurger Looks like the npm version still results in a 31MB bundle. I used the code from here (no other external dependencies, except latest Remotion client)

import {
  renderMediaOnLambda,
  speculateFunctionName,
} from "npm:@remotion/[email protected]/client";

Deno.serve(async (req) => {
  const { props } = await req.json();
  try {
    const response = await renderMediaOnLambda({
      serveUrl: "https://remotion-helloworld.vercel.app",
      composition: "HelloWorld",
      codec: "h264",
      // FIXME: Replace with your AWS region
      region: "eu-central-1",
      // FIXME: Add your function specs here
      functionName: speculateFunctionName({
        memorySizeInMb: 2048,
        diskSizeInMb: 2048,
        timeoutInSeconds: 120,
      }),
      inputProps: props,
    });
    return new Response(JSON.stringify(response), {
      headers: { "Content-Type": "application/json" },
    });
  } catch (error) {
    console.error(error);
    return new Response(JSON.stringify({ error: (error as Error).message }), {
      headers: { "Content-Type": "application/json" },
      status: 500,
    });
  }
});

Also, tried esm.sh/@remotion/[email protected]/client while it's only 300KB, it throws this error: [unenv] fs.readFile is not implemented yet!. I guess it's because it's bundled for the browser and missing the Node global references.

@hunxjunedo
Copy link
Contributor

looks like shared and pricing are taking much space, what if we divide the lambda package into two ? one for the main AWS sdk functions and the other for helper functions like price estimation and validation etc.

@laktek
Copy link

laktek commented Feb 6, 2025

I think mainly it's the @remotion/renderer dependency.

Also, if you can publish this as a JSR module, Supabase Edge Functions can only pull the files that are actually needed when bundling (unfortunately with npm we have to bundle the whole package)

@JonnyBurger JonnyBurger linked a pull request Feb 8, 2025 that will close this issue
@tristanbob
Copy link
Author

Was this new lambda-client package created to help with this problem? #4871

What is the status of that package? Is it ready to be tested?

@JonnyBurger
Copy link
Member

@tristanbob Yes, this is for solving this issue.

I'll try to get a new release out soon!

@JonnyBurger
Copy link
Member

@tristanbob Can you try using @remotion/lambda-client instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants