Next.js Discord

Discord Forum

Error: "Importing a component that needs next/headers... That only works in a Server Component."

Unanswered
Brown bear posted this in #help-forum
Open in Discord
Brown bearOP
Originally had this working all in one file where as soon as the page reloaded a new entry was added. However, I wanted to make it such that the new entry was added when a button was clicked. I was getting an error due to mixing the client/server functions, so I tried separating into 2 different files, but I'm now getting the error in the screenshot. How would I do this?

notes/page.tsx
"use client";

import { createClient } from "@/utils/supabase/server";
import { addMessage } from "@/utils/supabaseUtils";

export default async function Page() {
  const supabase = createClient();

  const handleClick = async () => {
    await addMessage();
  };

  const { data: messages } = await supabase.from("messages").select();
  return (
    <div>
      {" "}
      <button onClick={() => handleClick}> Add message </button>{" "}
      <pre>{JSON.stringify(messages, null, 2)}</pre>
    </div>
  );
}


utils/supabaseUtils.ts
"use server";
import { createClient } from "@/utils/supabase/server";

export const addMessage = async () => {
  const supabase = createClient();

  const { data, error } = await supabase.from("messages").insert([
    {
      message: "Your new message here",
      link: "Your link here",
      coordinates: { lat: 37, long: -34 },
    },
    // Add more objects for additional messages if needed
  ]);

  if (error) {
    console.error("Error adding message:", error.message);
  } else {
    console.log("Message added successfully:", data);
  }

  return { data, error };
};

33 Replies

American Crow
Error seems to be inside @/utils/supabase/server. What's the content of that?
add 'use server' at the top of it 😄
@Jboncz add 'use server' at the top of it 😄
American Crow
yea thats my guess as well
@Jboncz add 'use server' at the top of it 😄
Brown bearOP
to notes/page.tsx? but then i wouldnt be able to use the onclick?
No add it to
@/utils/supabase/server
I just tested, looks like you dont need 'use server' either way show me that file so I can see it.
Brown bearOP
/utils/supabase/server.ts
import { createServerClient, type CookieOptions } from "@supabase/ssr";
import { cookies } from "next/headers";

export const createClient = () => {
  const cookieStore = cookies();

  return createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return cookieStore.get(name)?.value;
        },
        set(name: string, value: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value, ...options });
          } catch (error) {
            // The `set` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: "", ...options });
          } catch (error) {
            // The `delete` method was called from a Server Component.
            // This can be ignored if you have middleware refreshing
            // user sessions.
          }
        },
      },
    }
  );
};
when i get use server to that file get this other error
Unhandled Runtime Error
Error: supabase.from is not a function

Source
app/notes/page.tsx (13:45) @ from

  11 |   };
  12 |
> 13 |   const { data: messages } = await supabase.from("messages").select();
     |                                             ^
  14 |   return (
  15 |     <div>
  16 |       {" "}
Lets take a step back.
You cannot have the page be async in a client component
@Jboncz You cannot have the page be async in a client component
Brown bearOP
mb sorry for late reply
what would be best way to implement this so onclick the messages are added
@Jboncz You cannot have the page be async in a client component
Brown bearOP
could i use useeffect to fix that
I’ll look when I get back to my pc
Brown bearOP
alr
Alright lets prototype this out.
You around?
@Jboncz You around?
Brown bearOP
yea hi
Just about done with an example for you
Brown bearOP
so without hte button i have no errors
alr
I know but I cant see your code in its entirety makes it diffult to troubleshoot, one sec
@Brown bear so without hte button i have no errors
Brown bearOP
but i want it so that on click it adds new one
I gotcha
With this, everytime I click the button to add a post, it calls a server action to add the post, and then changes 'posts' to empty string so it triggers the useEffect again to get updated content
'use client';

import { Button } from "@/components/ui/button";
import { AddPost, getPosts } from "./action";
import { useEffect, useState } from "react";

export default function Page() {
    const [posts, setPosts] = useState('');
    const [postsLoading, setPostLoading] = useState(true)
    const buttonClick = async () => {
        const result = await AddPost('abc123\n');
        if (result) {
            console.log('trigger reload')
            setPosts('')
        }
        console.log(result)
    }

    useEffect(() => {
        const get = async () => {
            const results = await getPosts('./myFile.txt')
            if (results) {
                console.log(results)
                setPosts(results)
            }
            setPostLoading(false)
        }
        get()
    }, [posts])


    return (
        <>
            {postsLoading ?
                <>
                    Loading
                </> :
                <>
                    {posts}
                </>
            }
            <Button onClick={() => buttonClick()}>Append to file</Button>
        </>
    );
}
server actions
'use server'
const fs = require('fs').promises;

export async function AddPost(data) {
    try {
        await fs.appendFile('./myFile.txt', data, 'utf-8');
        
        console.log('The data was appended successfully!');
        return true;
    }
    catch (err) {
        console.error(err);
        return false;
    }
}

export async function getPosts(filePath) {
    try {
        const data = await fs.readFile(filePath, 'utf-8'); // Read file with utf-8 encoding
        console.log(data)
        return data;
    } 
    catch (err) {
        console.error('Error reading file:', err);
        return null; // Or throw the error if preferred
    }
}
obviously im using a text file in place of your db
🙂
Does that example help?