I am trying to use server action in my current project. Some do work other will complete fail to compile. While it seems to work perfectly fine for others, the exact same code will not execute in my project.
import 'server-only';
const BUNNY_STORAGE_API_HOST = 'storage.bunnycdn.com';
export const uploadFile = async (path: string, file: Buffer) => {
const uploadFileUrl = new URL(`/${process.env.BUNNYCDN_STORAGE_ZONE}/${path}`, `https://${BUNNY_STORAGE_API_HOST}`);
await fetch(uploadFileUrl, {
method: 'PUT',
headers: {
AccessKey: process.env.BUNNYCDN_API_KEY || '',
'Content-Type': 'application/octet-stream',
},
body: file,
});
};
This is then imported into a server action:
'use server';
import 'server-only';
import { uploadFile } from './storage';
import sharp from 'sharp';
import { nanoid } from 'nanoid';
import { parseAsync } from 'valibot';
import { UploadImageSchema } from './schema/upload-image.schema';
export const uploadImageAction = async (formData: FormData) => {
const formDataObject = Object.fromEntries(formData.entries());
const data = await parseAsync(UploadImageSchema, formDataObject);
const imageArrayBuffer = await data.image.arrayBuffer();
const processedFile = await sharp(imageArrayBuffer).webp().toBuffer();
const path = `images/${nanoid()}.webp`;
await uploadFile(path, processedFile);
const imageUrl = new URL(path, `https://${process.env.BUNNYCDN_CDN_HOST}/`);
};
and then being called from a client component as per documentation:
'use client';
import { CloudUploadIcon } from 'lucide-react';
import { useForm } from 'react-hook-form';
import {
Button,
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
Input,
} from '@rocket-house-productions/shadcn-ui';
import { uploadImageAction } from '@rocket-house-productions/actions/server';
interface FileUploadProps {
onChange: (url?: string) => void;
endpoint: any; //keyof typeof ourFileRouter;
}
type FormValues = {
image?: File;
};
export const FileUpload = ({ onChange, endpoint }: FileUploadProps) => {
const form = useForm<FormValues>();
async function onSubmit(values: FormValues) {
const formData = new FormData();
formData.append('image', values.image!);
await uploadImageAction(formData);
}
// @ts-ignore
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="image"
render={({ field }) => (
<FormItem>
<FormLabel>Image</FormLabel>
<FormControl>
<Input
{...field}
required
type="file"
onChange={event => {
field.onChange(event.target.files?.[0]);
}}
// @ts-ignore
value={field.value?.fileName}
accept="image/png, image/jpeg, image/webp"
/>
</FormControl>
<FormDescription>Select image to upload</FormDescription>
</FormItem>
)}
/>
<Button disabled={form.formState.isSubmitting} type="submit">
Upload
</Button>
</form>
</Form>
);
};
But all I am getting this the following error.
hydration-error-info.js:63 ../../libs/shared/actions/src/storage.ts
Error:
× You're importing a component that needs server-only. That only works in a Server Component which is not supported in the pages/ directory. Read more: https://nextjs.org/docs/getting-started/
│ react-essentials#server-components
│
│
╭─[/Users/olafsiebert/html/rocket_house_productions/libs/shared/actions/src/storage.ts:1:1]
1 │ import 'server-only';
· ─────────────────────
2 │ const BUNNY_STORAGE_API_HOST = 'storage.bunnycdn.com';
3 │
4 │ export const uploadFile = async (path: string, file: Buffer) => {
╰────
Import trace for requested module:
../../libs/shared/actions/src/storage.ts
../../libs/shared/actions/src/server.ts
../../libs/shared/features/src/lib/file-upload/file-upload.tsx