Next.js Discord

Discord Forum

Want to run server function every time a mutation happens on client side to get presignedUrl ...

Answered
Siricid woodwasp posted this in #help-forum
Open in Discord
Siricid woodwaspOP
export async function uploadPresignedUrl() {
  const user = await runWithAmplifyServerContext({
    nextServerContext: { cookies },
    operation: (contextSpec) => {
      return fetchAuthSession(contextSpec);
    },
  });

  if (!user) {
    throw new Error("No user");
  }
  const bucket = Bucket.bucket.bucketName;
  const command = new PutObjectCommand({
    Bucket: bucket,
    ACL: "public-read",
    Key: crypto.randomUUID(),
  });
  const client = new S3Client();

  const url = await getSignedUrl(client, command, {
    expiresIn: 60,
  });
  return {
    url,
  };
}
Answered by Ray
if this doesn't work, try import the action to the page where the client component render and pass the action to it
View full answer

40 Replies

Siricid woodwaspOP
calling it on client side
export clientcomponent = () => {

const handleUpload = async () => {
    const { url } = await uploadPresignedUrl();
    if (onChange) {
      if (Array.isArray(acceptedFiles)) {
        acceptedFiles.forEach((file) => {
          mutation.mutate({ file, url });
        });
      } else {
        mutation.mutate({ file: acceptedFiles, url });
      }
    }
  }
};
your await need to be called inside a useEffect if you really want to fetch data clientside.

You can do this like that:
useEffect(() => {
        const fetchData = async () => {
            const data = await getData();
...
        };

        fetchData();
    }, []);
Siricid woodwaspOP
yea i did that it it doesnt work
why not?
Siricid woodwaspOP
ok let me try again it was throwing some weird error
ok 🤔
Siricid woodwaspOP
yea it throws this error SyntaxError: await is only valid in async functions and the top level bodies of modules
show your code
Siricid woodwaspOP
export const UploadFileComponent = () => {
  const [onChange, setOnChange] = useState<File | File[] | null>(null);
  const [progress, setProgress] = useState<Record<string, number>>({});
  const [url, setUrls] = useState<string | null>();

  const onDrop = useCallback((acceptedFiles: File[]) => {
    setOnChange(acceptedFiles);
  }, []);
  const { getRootProps, getInputProps, isDragActive, acceptedFiles } =
    useDropzone({
      onDrop,
    });

  useEffect(() => {
    (async () => {
      const { url } = await uploadPresignedUrl();
      setUrls(url);
    })();
  }, []);

  const mutation = useMutation({
    mutationKey: ["fileUpload"],
    mutationFn: async ({ file }: { file: File }) => {
      if (!url) {
        throw new Error("No url provided");
      }
      await axios.put(url, file, {
        headers: {
          "Content-Type": file.type,
          "Content-Disposition": `attachment; filename="${file.name}"`,
        },
        onUploadProgress: (progressEvent) => {
          setProgress((prevProgress) => ({
            ...prevProgress,
            [file.name]: Math.round(
              (progressEvent.loaded * 100) / (progressEvent.total ?? 0)
            ),
          }));
        },
      });
      return file;
    },
    onSuccess: (file) => {
      toast(`File ${file?.name} uploaded`, {
        description: `File type: ${file?.type.split("/")[1]}, Size: ${
          file?.size
        }`,
      });
    },
    onError: (file) => {
      toast(`Couldn't upload file ${file?.name}`);
    },
  });

  const handleUpload = async () => {
    if (onChange) {
      if (Array.isArray(acceptedFiles)) {
        acceptedFiles.forEach((file) => {
          mutation.mutate({ file });
        });
      } else {
        mutation.mutate({ file: acceptedFiles });
      }
    }
  };
yea it needs to be in it's own function like this:
const fetchData = async () => {
            const data = await getData();
...
        };

        fetchData();
Siricid woodwaspOP
k let me try
Siricid woodwaspOP
async function getURL() {
    const { url } = await uploadPresignedUrl();
    return url;
  }


  const handleUpload = async () => {
    const url = await getURL();
    if (onChange) {
      if (Array.isArray(acceptedFiles)) {
        acceptedFiles.forEach((file) => {
          mutation.mutate({ file, url });
        });
      } else {
        mutation.mutate({ file: acceptedFiles, url });
      }
    }
  };

Doesnt work either
Siricid woodwaspOP
If I call this server action in parent server component it will only run once right?
@Siricid woodwasp Click to see attachment
look like the error coming from sst?
@Siricid woodwasp Click to see attachment
do you see error if you comment out the body of uploadPresignedUrl?
Siricid woodwaspOP
yes no error then
@Siricid woodwasp yes no error then
maybe try changing the target in tsconfig.json to esnext
@Ray maybe try changing the `target` in `tsconfig.json` to `esnext`
if this doesn't work, try import the action to the page where the client component render and pass the action to it
Answer
Siricid woodwaspOP
@Ray if this doesn't work, try import the action to the page where the client component render and pass the action to it
Siricid woodwaspOP
then it works but url expires in 60 seconds to it just makes some mutattion rest fails
what is the error
Siricid woodwaspOP
Error:+await+is+only+valid+in+async+functions+and+the+top+level+bodies+of+modules
import { UploadFileComponent } from "@/components/upload-file";
import { Metadata } from "next";

export const dynamic = "force-dynamic";

export const metadata: Metadata = {
  title: "Settings",
};

export default async function SettingsPage() {
  return (
    <div>
      <h1>Settings</h1>
      <UploadFileComponent />
    </div>
  );
}
@Siricid woodwasp Error:+await+is+only+valid+in+async+functions+and+the+top+level+bodies+of+modules
try this in next.config.js
experimental: {
    esmExternals: true
  },
Siricid woodwaspOP
this is where next client component is called if i call action here it just runs once
i need to run it on every mutation
you don't need to call
just pass it
<UploadFileComponent action={uploadPresignedUrl} />
Siricid woodwaspOP
@Ray this works in dev mode will it keep woking when i bundle it
Siricid woodwaspOP
any idea what is the type of action
for typescritp
just do typeof uploadPresignedUrl
Siricid woodwaspOP
thanks btw I didnt thought this was possible i created seperate api for this lol was going to switch to that
I have dueled with it since 14.0.0 lol
and still haven't fixed yet in 14.0.4 lol