What's the difference between server actions and invoking helper functions in server components?
Answered
Order posted this in #help-forum
OrderOP
So this has been something that I've not been able to understand yet.
Server actions are all POST requests and you make a function a server action by using the "use server" directive. Generally Next says that you use those for mutations like posting/updating/deleting something. Me as a noob, also used them to fetch data in some of my early practice projects. What I don't understand is what's the difference between making a function in a file marked as "use server" and making a function in just a normal TS file, importing that function inside a server component and calling it. What are the down sides differences of making EVERYTHING a server action and/or making nothing a server action?
Server actions are all POST requests and you make a function a server action by using the "use server" directive. Generally Next says that you use those for mutations like posting/updating/deleting something. Me as a noob, also used them to fetch data in some of my early practice projects. What I don't understand is what's the difference between making a function in a file marked as "use server" and making a function in just a normal TS file, importing that function inside a server component and calling it. What are the
Answered by American Crow
This confuses a lot of people because the naming is not good.
You got the basics right though.
TL;DR: Think of
'use server' is not the opposite of 'use client'
If you do not have any notation at the top of your file the default is a server component which runs on the server.
You have to differentiate between server components and server actions.
Server actions are used for mutations (update, delete, create).
Server components are mainly used to get (read) data.
You are right both, server actions and server components run on the server but that's pretty much all they have in common.
If you have a file which holds a couple of (async) functions (no declration at the top) and you import a function into a server component you just import a function. It's just code splitting / having reusable functions. If you import these functions to the client and if they are async you will get an error. Asnyc functions are not supported on the client (yet).
If a file does have 'use server' at the top it actually contains a collection of server actions (mutations) which can be imported from a client component.
So it comes back to why should you use server actions to manipulate data OVER a "normal" async function invoked on the server? Server actions have several advantages e.g. progressive enhancement, more here: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#behavior
On the other hand server actions are run in sequence that's why they are usually not used for getting data
You got the basics right though.
TL;DR: Think of
'use server' as 'use server action''use server' is not the opposite of 'use client'
If you do not have any notation at the top of your file the default is a server component which runs on the server.
You have to differentiate between server components and server actions.
Server actions are used for mutations (update, delete, create).
Server components are mainly used to get (read) data.
You are right both, server actions and server components run on the server but that's pretty much all they have in common.
If you have a file which holds a couple of (async) functions (no declration at the top) and you import a function into a server component you just import a function. It's just code splitting / having reusable functions. If you import these functions to the client and if they are async you will get an error. Asnyc functions are not supported on the client (yet).
If a file does have 'use server' at the top it actually contains a collection of server actions (mutations) which can be imported from a client component.
So it comes back to why should you use server actions to manipulate data OVER a "normal" async function invoked on the server? Server actions have several advantages e.g. progressive enhancement, more here: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#behavior
On the other hand server actions are run in sequence that's why they are usually not used for getting data
47 Replies
American Crow
This confuses a lot of people because the naming is not good.
You got the basics right though.
TL;DR: Think of
'use server' is not the opposite of 'use client'
If you do not have any notation at the top of your file the default is a server component which runs on the server.
You have to differentiate between server components and server actions.
Server actions are used for mutations (update, delete, create).
Server components are mainly used to get (read) data.
You are right both, server actions and server components run on the server but that's pretty much all they have in common.
If you have a file which holds a couple of (async) functions (no declration at the top) and you import a function into a server component you just import a function. It's just code splitting / having reusable functions. If you import these functions to the client and if they are async you will get an error. Asnyc functions are not supported on the client (yet).
If a file does have 'use server' at the top it actually contains a collection of server actions (mutations) which can be imported from a client component.
So it comes back to why should you use server actions to manipulate data OVER a "normal" async function invoked on the server? Server actions have several advantages e.g. progressive enhancement, more here: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#behavior
On the other hand server actions are run in sequence that's why they are usually not used for getting data
You got the basics right though.
TL;DR: Think of
'use server' as 'use server action''use server' is not the opposite of 'use client'
If you do not have any notation at the top of your file the default is a server component which runs on the server.
You have to differentiate between server components and server actions.
Server actions are used for mutations (update, delete, create).
Server components are mainly used to get (read) data.
You are right both, server actions and server components run on the server but that's pretty much all they have in common.
If you have a file which holds a couple of (async) functions (no declration at the top) and you import a function into a server component you just import a function. It's just code splitting / having reusable functions. If you import these functions to the client and if they are async you will get an error. Asnyc functions are not supported on the client (yet).
If a file does have 'use server' at the top it actually contains a collection of server actions (mutations) which can be imported from a client component.
So it comes back to why should you use server actions to manipulate data OVER a "normal" async function invoked on the server? Server actions have several advantages e.g. progressive enhancement, more here: https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations#behavior
On the other hand server actions are run in sequence that's why they are usually not used for getting data
Answer
OrderOP
So in essence when I want to fetch data inside a server component I use a function (might be reusable might not be) inside the server component. When I want to mutate data I use server actions and I can invoke the server action in either the client or the server component. Last case is when I want to fetch data on the client and I guess best way to do that is using something like react hook form?
Cuz the whole useEffect fetching thing is kinda bad no?
omg I meant react query
my b
my brain is fried LOL
American Crow
Have to pick these statements apart:
- "When I want to mutate data I use server actions and I can invoke the server action in either the client" -> Yes
- "or the server component" -> No, it doesn't really make sense to run server actions in server components. If you want to manipulate data in a server component in most cases you'd directly call the ORM for example.
If you want to fetch data on the client, yes use a lib like react query or useSWR.
Ask yourself why you have to fetch on the client there are some reasons like infinitescrolling, or needing super fresh data (<1min intervals). But these should be rare in general.
- "When I want to mutate data I use server actions and I can invoke the server action in either the client" -> Yes
- "or the server component" -> No, it doesn't really make sense to run server actions in server components. If you want to manipulate data in a server component in most cases you'd directly call the ORM for example.
db.update(1,"hello")If you want to fetch data on the client, yes use a lib like react query or useSWR.
Ask yourself why you have to fetch on the client there are some reasons like infinitescrolling, or needing super fresh data (<1min intervals). But these should be rare in general.
OrderOP
Yeah, if it's data that doesn't update that often I can fetch it in a parent server component and pass it as props(?) to the client correct?
OrderOP
"or the server component" -> No, it doesn't really make sense to run server actions in server components. If you want to manipulate data in a server component in most cases you'd directly call the ORM for example. db.update(1,"hello") So what if I have a form inside a server component, I wouldn't call a server action on the action attribute of the form?I know that's generally a rarity given the fact that forms require giving feedback to the user if they do something wrong so most forms by default would be client components
American Crow
Yea it is a edge case. I think I'd actually call a server action from a server component in that case. Let me check the docs
OrderOP
Okay, thanks a lot btw you've been of tremendous help. I have one more question that might be a bit of a complicated one, but how would I go about implementing rate limiting on server actions? I assume it's not good practice to give the user the ability to just spam submit forms/data over and over again.
American Crow
Yea one sec i got working code for that
"use server"
// imports
import { ratelimit } from "@/app/server-actions/rate-limiter"
export async function generateBusinessNames(
userInput: string,
subClass: BusinessNameGeneratorSubclasses
) {
if (!userInput) {
return { error: "No User Input." }
}
// Rate Limiting
const ip =
headers().get("x-forwarded-for") ??
headers().get("x-real-ip") ??
"anonymous"
const { success } = await ratelimit.free.limit(ip)
if (!success) {
// In Dev let the request through and log that the user would be rate limited
if (process.env.NODE_ENV === "production") {
return {
error:
"Too many requests. Please try again in 1 hour or upgrade to a premium plan.",
}
}
console.log(
"Rate limited would've blocked this request. But DEV Mode is letting it through."
)
}
// ------------ END Rate Limiting
...rate-limiter.ts
"server only"
import { Ratelimit } from "@upstash/ratelimit"
import { Redis } from "@upstash/redis"
export const redis = Redis.fromEnv()
// Rate Limiter
export const ratelimit = {
free: new Ratelimit({
redis: redis,
prefix: "ratelimit:free",
analytics: true,
limiter: Ratelimit.fixedWindow(10, "3600 s"),
}),
paid: new Ratelimit({
redis: redis,
prefix: "ratelimit:paid",
analytics: true,
limiter: Ratelimit.fixedWindow(60, "3600 s"),
}),
}Using upstash in this case
"server only" is a helper package just to be double sure that this code is never run on the client (it just throws an error if imported to a client component)
x-forwared and x-real-ip are special headers available on Vercel
OrderOP
yeah but those helper functions are coming from redis
I do get the logic tho
I'm using a postgres db that I'm hosting on my machine during local development
American Crow
Yea i haven't done that but the idea is the same you can basically use LRU cache, or even the postgres db for the task. There are should be enough examples for it. Personally never done it
OrderOP
So lastly, if my app is not going to be used cross-platform (like have a mobile version) is there even a point in using (API) route handlers? Can't I just do everything with server actions/fetching from server components instead?
American Crow
You even should do everything with server actions/server components.
There are some use cases for route handlers as you mentioned. Having an external facing api is one of them since server actions can not be called from outstide.
Another use case would be the implementation of a webhook, same reason as before.
Under the hood a server action creates a route handler for you. So they are very similar, however you save a network trip with server actions
There are some use cases for route handlers as you mentioned. Having an external facing api is one of them since server actions can not be called from outstide.
Another use case would be the implementation of a webhook, same reason as before.
Under the hood a server action creates a route handler for you. So they are very similar, however you save a network trip with server actions
OrderOP
yes, that's what I thought
well this has been enlightening
oh btw I found a package called "limiter" from npm and it seems promising for rate limiting
ok I'm gonna mark your initial answer as solution, thanks so much
American Crow
you welcome. You picked all these things up very fast. You'll have a good time i am sure
OrderOP
Yeah, because I kinda had the puzzle pieces in my head, I just couldn't connect them all, and you gave me the puzzle box lol
@Order Yeah, because I kinda had the puzzle pieces in my head, I just couldn't connect them all, and you gave me the puzzle box lol
American Crow
i started this when i was learning.
server actions are missing but we discussed those in detail.
It might help you for your mental model
https://excalidraw.com/#json=IxXeSbOvQ8tl5-vOMDpQt,3xz59SGUcsAol8QJ-s9Xtg
dont share it (yet) since its not finished but you got the basics so it might already help you
server actions are missing but we discussed those in detail.
It might help you for your mental model
https://excalidraw.com/#json=IxXeSbOvQ8tl5-vOMDpQt,3xz59SGUcsAol8QJ-s9Xtg
dont share it (yet) since its not finished but you got the basics so it might already help you
OrderOP
woah that is dope, wait I forgot to ask something, so if I'm using something like tanstack react query to fetch data in the client I'd just make a function inside a separate file and then import it and call it from react query right? AFAIK I can also fetch initially from the server component, and then pass the data as props to the client component and use it as "Initial data" so that the page can load quickly initially
@Order woah that is dope, wait I forgot to ask something, so if I'm using something like tanstack react query to fetch data in the client I'd just make a function inside a separate file and then import it and call it from react query right? AFAIK I can also fetch initially from the server component, and then pass the data as props to the client component and use it as "Initial data" so that the page can load quickly initially
American Crow
The first part regarding the client side fetching: I think you refering to this, there is an example with react query in the video:
https://www.youtube.com/watch?v=OgVeQVXt7xU
(!) Be aware the Youtuber makes the mistake of "getting" data in a server action which works but is wrong in general. Other than that the example is fine if it would be a mutation.
Second thing you are refering to preload patterns which are described here:
https://nextjs.org/docs/app/building-your-application/data-fetching/patterns#preloading-data
https://www.youtube.com/watch?v=OgVeQVXt7xU
(!) Be aware the Youtuber makes the mistake of "getting" data in a server action which works but is wrong in general. Other than that the example is fine if it would be a mutation.
Second thing you are refering to preload patterns which are described here:
https://nextjs.org/docs/app/building-your-application/data-fetching/patterns#preloading-data
OrderOP
yeah I've seen this video from Josh and this was exactly what I was thinking about
I think the only difference from his approach would be to not "use server" and just create a function in a file and it (Should?) work properly
American Crow
I think so yea. Its getting very theoretical now though 😄
OrderOP
I'll give it a shot, it's crazy tho that information as important as this is not clear and readily available
mutating and fetching data is like the backbone of any backend
American Crow
Yea it is crazy maybe someone should draw a mental model for it
Kep in mind all these RSC concepts are very new and changed and will change more in the future. So it's hard to have on point documentation all the time
Kep in mind all these RSC concepts are very new and changed and will change more in the future. So it's hard to have on point documentation all the time
OrderOP
yeah just saw a video from bytegrad about the new useActionState() hook that's coming in react 19
so things are changing constantly, sometimes I feel like I'm spending more time learning what to do than doing stuff.. but that's the javascript way I guess
American Crow
yea same feeling for me 100%
@Order You could do me a favor and mark one of the answers which helped you most as answer to your original question. So others can benefit. We made this thread a bit long already and i answered like 10 questions which the mods most likely not happy about 😛
thank you
thank you
OrderOP
I did it already
American Crow
didnt even notice thank you
OrderOP
yeah ofc it was a great productive convo
OrderOP
@American Crow After some research I'm actually changing my mind about api routes. Looks like if you want to fetch any data SECURELY from a client component pretty much the only way to do so would be with API Routes. Think about it.. how would you do authentication/authorization checks if you just have a function you call from react query. Technically everyone can call that function and there's no auth layer. With an API route you can check for auth on the server and then return a response based on that.
@Order <@240974567730970625> After some research I'm actually changing my mind about api routes. Looks like if you want to fetch any data SECURELY from a client component pretty much the only way to do so would be with API Routes. Think about it.. how would you do authentication/authorization checks if you just have a function you call from react query. Technically everyone can call that function and there's no auth layer. With an API route you can check for auth on the server and then return a response based on that.
American Crow
if you have to do client side fetching and if you have to include secrets you have to go with API routes. You are correct on that. Check the excalidraw image it's in there. I forgot to mention it yesterday though.
OrderOP
Well with that information I think I finally got the whole picture.