Server Actions in Client Components
Answered
Polar bear posted this in #help-forum
Polar bearOP
I have a NextJS frontend and an Express backend set up. I am trying to do a quick "proof of concept" by implementing a button (on a client component) that fetches some data from my express API, and logs it to the console. I am trying to use server actions to do this, but the response returns undefined every time.
On the top level of my tree (top-level is server component), I also fetch the data to prove that it works: (refers to
And here is the
When the
Instead, the response from the API is undefined. In fact, it doesn't even make a call to the server at all (no activity is logged on the backend). It's getting stuck on the front, and why is this?
On the top level of my tree (top-level is server component), I also fetch the data to prove that it works: (refers to
fetchRecords)import { fetchMember, fetchRecords } from "@/app/api/read"
...
export default async function Board() {
const response = await fetchMember()
const response2 = await fetchRecords()
return response && response2 ? ( // returns true with data )And here is the
fetchRecords function:'use server'
...
export async function fetchRecords(): Promise<(APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true }) | null> {
try {
return await fetch<APIResponse<{ records: AttendanceRecord<number>[] }>>(
"http://localhost:5000/api/attendance/records",
{
method: "GET",
headers: headers()
},
FetchResultTypes.JSON
) as unknown as (APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true })
} catch {
return null
}
}When the
fetchRecords is called from the top-level server component, it successfully returns a non-null response with the data. This, however, does not work:"use client"
...
<button
onClick={async () => {
const response = await fetchRecords()
console.log(response?.success)
}}>
Fetch Records
</button>Instead, the response from the API is undefined. In fact, it doesn't even make a call to the server at all (no activity is logged on the backend). It's getting stuck on the front, and why is this?
Answered by Polar bear
well my friend wrote this fix:
and
export async function fetchRecords(): Promise<(APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true }) | any> {
try {
return await fetch<APIResponse<{ records: AttendanceRecord<number>[] }>>(
"http://localhost:5000/api/attendance/records",
{
...(await platformOptions())
},
FetchResultTypes.JSON
) as unknown as (APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true })
} catch(err) {
return console.log(`ERROR:\n\n${err}`)
}
}and
import { RequestOptions } from "@sapphire/fetch"
export async function platformOptions(): Promise<Partial<RequestOptions>> {
if (typeof window === "undefined") {
const { headers } = await import("next/headers")
return {
headers: headers()
}
} else {
return {
credentials: "include"
}
}
}25 Replies
@Polar bear I have a NextJS frontend and an Express backend set up. I am trying to do a quick "proof of concept" by implementing a button (on a client component) that fetches some data from my express API, and logs it to the console. I am trying to use server actions to do this, but the response returns undefined every time.
On the top level of my tree (top-level is server component), I also fetch the data to prove that it works: (refers to ``fetchRecords``)
ts
import { fetchMember, fetchRecords } from "@/app/api/read"
...
export default async function Board() {
const response = await fetchMember()
const response2 = await fetchRecords()
return response && response2 ? ( // returns true with data )
And here is the ``fetchRecords`` function:
ts
'use server'
...
export async function fetchRecords(): Promise<(APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true }) | null> {
try {
return await fetch<APIResponse<{ records: AttendanceRecord<number>[] }>>(
"http://localhost:5000/api/attendance/records",
{
method: "GET",
headers: headers()
},
FetchResultTypes.JSON
) as unknown as (APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true })
} catch {
return null
}
}
When the ``fetchRecords`` is called from the top-level server component, it successfully returns a non-null response with the data. This, however, does not work:
ts
"use client"
...
<button
onClick={async () => {
const response = await fetchRecords()
console.log(response?.success)
}}>
Fetch Records
</button>
Instead, the response from the API is undefined. In fact, it doesn't even make a call to the server at all (no activity is logged on the backend). It's getting stuck on the front, and why is this?
'use server'
...
export async function fetchRecords(): Promise<(APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true }) | null> {
try {
const res = await fetch<APIResponse<{ records: AttendanceRecord<number>[] }>>(
"http://localhost:5000/api/attendance/records",
{
method: "GET",
headers: headers()
},
FetchResultTypes.JSON
);
const data = await res.json() as unknown as (APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true })
return data
} catch {
return null
}
}@Ray ts
'use server'
...
export async function fetchRecords(): Promise<(APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true }) | null> {
try {
const res = await fetch<APIResponse<{ records: AttendanceRecord<number>[] }>>(
"http://localhost:5000/api/attendance/records",
{
method: "GET",
headers: headers()
},
FetchResultTypes.JSON
);
const data = await res.json() as unknown as (APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true })
return data
} catch {
return null
}
}
Polar bearOP
It's already returned as JSON data thanks to the fetch that I use
sapphire/fetchcatch the error and log it
@Ray catch the error and log it
Polar bearOP
TypeError: fetch failedon the server component, no error and it fetches successfully
just not on client
can you try with normal fetch instead of
sapphire/fetch?@Ray can you try with normal fetch instead of `sapphire/fetch`?
Polar bearOP
export async function fetchRecords2() {
try {
const response: any = await fetch(
"http://localhost:5000/api/attendance/records",
{
method: "GET",
headers: headers()
}
)
return await response.json()
} catch(err) {
return console.log(err)
}
}TypeError: fetch failedsame error
@Ray ts
headers: Object.fromEntries(headers())
Polar bearOP
oop okay actually there's a new error
with this
TypeError: fetch failed
at Object.fetch (node:internal/deps/undici/undici:11576:11) {
cause: RequestContentLengthMismatchError: Request body length does not match content-length header
at write (C:\Users\Parker\Dev\aedash\node_modules\.pnpm\next@13.5.4_@babel+core@7.23.6_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\undici\index.js:1:77165)
at _resume (C:\Users\Parker\Dev\aedash\node_modules\.pnpm\next@13.5.4_@babel+core@7.23.6_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\undici\index.js:1:76786)
at resume (C:\Users\Parker\Dev\aedash\node_modules\.pnpm\next@13.5.4_@babel+core@7.23.6_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\undici\index.js:1:75473)
at connect (C:\Users\Parker\Dev\aedash\node_modules\.pnpm\next@13.5.4_@babel+core@7.23.6_react-dom@18.2.0_react@18.2.0\node_modules\next\dist\compiled\undici\index.js:1:75361) {
code: 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH'
}
}that's... odd
@Ray hmm, just pass the header you need?
Polar bearOP
it may have to do with my authentication cookie.. perhaps theres some difference between server / client components that causes it to be obtained differently
still i dont understand what that error means
@Ray the header of content-length doesn't match
Polar bearOP
well my friend wrote this fix:
and
export async function fetchRecords(): Promise<(APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true }) | any> {
try {
return await fetch<APIResponse<{ records: AttendanceRecord<number>[] }>>(
"http://localhost:5000/api/attendance/records",
{
...(await platformOptions())
},
FetchResultTypes.JSON
) as unknown as (APIResponse<{ records: AttendanceRecord<number>[] }> & { success: true })
} catch(err) {
return console.log(`ERROR:\n\n${err}`)
}
}and
import { RequestOptions } from "@sapphire/fetch"
export async function platformOptions(): Promise<Partial<RequestOptions>> {
if (typeof window === "undefined") {
const { headers } = await import("next/headers")
return {
headers: headers()
}
} else {
return {
credentials: "include"
}
}
}Answer
Polar bearOP
kinda funny tbh. 😹
i suppose it's one way to solve the client / server discrepancy
and hey it works so
thanks for your help @Ray :) i am very grateful