I Fixed Next.js Server Actions

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
we need to talk about server actions the next 13.4 release next added the missing primitive for the react server component model the ability to mutate data from the client on the server this is one of the most important pieces to any web framework especially the server-side ones because you need a way not just to read data on the client but send data back to the server and this model is how we do it first thing you need to know about server actions is they don't work like other Primitives and other Frameworks they feel a little like a hybrid between the remix approach and the trpc approach but they're unique in a lot of ways as well some of those ways are really nice and composable some of them have some scary side effects I want to talk about all of that here the example the team loves to give is the progressive enhancement example which means that a form is posting to an end point without any JavaScript being involved at all if we take a look here at the example in the blog post that they put out you see they're importing KV from their new KV storage stuff the more important part here is the async increment function which has a use server call it awaits a kv.increment call which in this case is updating their KV star and then when you click look like it bumps that it does that because the buttons type is submit and the action is bound here and becomes the endpoint that this form posts to like a traditional form action the way browsers work before Ajax and JavaScript even existed this lets us write code in the usual react way without actually having to ship react in JavaScript to the user at all it's really cool how powerful this pattern is that said it's not really for me and there are a couple catches the big catch is the way scope works here if you were to have defined a variable outside it'll actually make its way into the form I'm going to do a kind of dangerous and silly example here where we're going to async function write file obviously we have to use server to let react know this is a server component and we need to let this behave on the server kind of status hello world sure path hello FS dot write file this is a path which is path and then it's data which is data and return done let's see if this works and then we have to bind it we'll go use the example here better than there if this works correctly then when I click this it should write the file in my terminal and look at that there's a new file in public hello.txt none of that code ran on the client no JavaScript on the client at all I could even disable JavaScript but since this is a used server function it was able to do something in this case my Dev server like write a file update my database use my environment variables and secure things there really really cool stuff there are some gotchas though this one is going to be a slightly contrived example but let's say we put const path equals public here instead because we were defining it using other things we passed in through props not the simplest example but things like this I suspect will be pretty common the waitress is to do this for now is a little scary to me they actually take whatever you're doing here in this case that variable we're defining that's outside of this closure but is accessible within it and they encode this in the form so if I was to save this and we go back here we look at the HTML we're going to see something interesting let's go to this form quick you'll see in this form we actually have encoded public hello text this is an action value that is being bound by react in order to make sure that the form posting gets this data to that form it's an interesting way of doing this there's a couple other methods they could have chosen the sketch here is let's say that this wasn't path this was secret path we didn't want the client to know where this file is being stored or maybe it was a secret key or something like that it's really scary that this gets encoded in the form in plain text right now and even if it's encrypted that data still gets sent to the user as HTML my expectation would have been that all of the things this closure needs would have been re-run when the post happens rather than encoded in the form and then only accessed through this specific created endpoint it's a weird behavior and it really does show how alpha this stuff is I've had my own fun set of bugs like headers and requests weren't accessible for a while but once that's been fixed these are the edgy edge cases that kind of break my mental model that scare me the biggest thing that doesn't jive with me here is that it changes how I understood server components and how they worked before previously when a server component returned something that would go to the client in the sense that it was either going as HTML or mounting other components that would do their own things but if it wasn't any return it didn't go to the user and I feel like that model made a lot of sense to me where with this there are things that go into that form that I didn't put there I didn't say put secret path inside of this form I just put a secret path variable here used it here and I passed this function to the form but passing this function to the form does not make it clear to me that anything that was accessed outside of this is now also being made part of the form the control flow here is what's scary to me but the benefits of progressive enhancement in line when you have behaviors like a delete button not having to write the exact input needed here like path string and having to deal with that in this level that's the change here that's so nice and I get why they're leaning in this direction it's just we have some gotchas that we're gonna have to clear out over the next few weeks that all said this is only one of the two ways you can use server actions in the two different ways behave almost entirely differently the other way would be breaking this out into its own file and consuming that in a client component so let's take a look at how you do that really quick first I'm going to newink this function section I'll just put this back in I don't even put it back in here going to yoink all of the contents here we're going to go to a different file we'll just name it actions.ts doesn't matter what you name it just go in with that for now use server on top not there also just realized I didn't select the workspace typescript version which will give us some very handy errors make sure you're always using the typescript version for the repo you're in I have a whole video about that coming in the near future I hope vs code makes that easier for us so now I have a use server file this U server directive isn't needed in it because the file is used server which tells the compiler hey everything in here needs to be accessible in this way if you export something that isn't a function it will yell at you in the compiler level so that's gonna yell for different reasons if I just import this for now see here we're getting an error that's because we're exporting something that isn't in action which it gets mad at us for because the whole point of this compiler step is that you can only export async functions such that they are accessible on a client or other environments it's a little weird to not be able to export things like this but I understand why they did it it keeps the Orca orchestration of what is important exported from where very clean and prevents weird cyclical import behaviors we now have our export costs right file that's being passed over here and now should be able to refresh click and once again the file is written and this time if we look at the form not going to include things it doesn't need because this time it all exists in that external separate file it has an ID in order for it to identify which endpoint it's supposed to hit but it doesn't need any data specific to this call because it's all done at this file level generally I'm going to recommend defining your actions in this way if you can it prevents these weird closures and leakage issues on top of that we can do a lot of fun things when we separate it including using client let's take a look at how we would do that so right now we're doing the form action behaviors let's say we want to do this the old single page app style instead let's make us a button.tsx obviously this has to be used client because we want it to run on server and client and actually ship JavaScript to the user we're going to export const right button equals turn div button right file on click equals and here's where things get fun we can actually import this action and do it then this values whatever you're returning there and since it's all just typescript it's whatever we put there I can go back here instead of returning done return success true and now over here we'll see the type of vowel is Success Boolean I can console.log we did it Val so we have to match this button and this is where I think some of the coolest magic happens I forgot to actually delete the file I can do that quick too first delete right and once again it wrote the file but this time we actually have things coming back in the console if you look at the network tab you can see that we posted an interesting payload with an interesting response because this is how the react compiler determines what actions are where it basically indexes them the way that hooks worked in the past where each action that a route has access to gets its own identifier and then when you post something to that endpoint it requires the identifier to know which action it is we can go way deeper on how that works under the hood later but it's also probably going to change it doesn't matter too much the simple thing to know is we are basically just doing a fetch post call when we Import in a client component and we can treat that like we would any other promise except we get the data back and it's typesafe it's like mini trpc in this way it is missing some pieces though most importantly it's missing validation which is very important to me which is why months ago when I first started using server actions I actually solved these problems we built a package at Ping called zact and I'm really proud of what we built with this act package the tldr is Zod server actions we wanted it to be as easy as possible to validate server actions so we give you a special wrapper you give it a Zod object and then you pass it in async function and now this is a validated function this function will not run unless your validator passes and you know the input of this is going to be correct so if I oink this example first I have to install Zack the npm installs act I will paste this in here instead in here we have a different action this time it's named validated action just a silly example it will return a message it's also again runs on server so none of this has to worry about scope leak or closures or any of that you can write whatever in here you can access Secrets do whatever you need as long as you don't return it the client will never see it really nice for keeping your application secure so if I grab this I change the action there validated action takes stuff in we Define that in the other file as you saw now we're going to click the button and submit normally add an input and do things the react way but I'm gonna be lazy and do that hey we're getting an error what's that error huh let's take a look at it on the server Oh weird you're not subscribed yet really come on we're putting so much work into all these videos and y'all couldn't bother to subscribe you're gonna hold the whole video because you avoided clicking one button come on so you've seen the server action here if the string isn't six characters I throw this cheeky little error so we can just go here and make this longer we'll say longer string now when I go and click right file it'll be fine and we get the message hello longer string because that's what we returned there nice and simple super cool that we're basically just doing trpc without everything on the outside just the inside function piece that you can import externally it's weirdly convenient and really nice this vaguely reminds me of the patterns I've seen in things like next Fetch and telefunk where you can just export a function and then call it like a hook that said these ones are much nicer and feel more native to react they're still very very early I find helpers like what I built here almost necessary we also have a dumb little hook I made which is extraordinarily early even more so than the rest of this and I can link this example here paste this here instead this package is old okay we have a lot to fix regardless here is like a more traditional react query type mutation thing where we call mutate and we have a data State loading State error State we can go back here we just trust the typescript things will be much easier and now we run the server action we get hello whatever Back ISO this is random text so that makes sense if I was to flex this it'd be a lot cleaner oh cool now I run this over action we get a loading State we get the hello state right after nice simple easy works just like react query used to but we get to take advantage of the New Primitives this is kind of a rejection of the progressive enhancement style that the earlier examples that next put out showed us but I don't think these Primitives should be locked to people using forms I have much more Dynamic stuff in my applications and I love using The Primitives for that too again they're still very early we're still figuring out how to do things like auth in them but I have a lot of hope that this pattern will be very scalable for many different types of applications I know we're excitedly already starting to use it I really appreciate y'all for waiting for my takes on this video as well as to check out my package please take a look at Zach we have it up on GitHub it's one of the first open source packages we made at ping it's had like 50 stars before we started talking about it last week and now it has almost 500. so if you want to start using server actions responsibly right now while sharing some of your mental model from react query this is one of the best ways to do it I'm really proud of what we made here check it out if you haven't thank you guys as always peace nerds
Info
Channel: Theo - t3․gg
Views: 55,603
Rating: undefined out of 5
Keywords: web development, full stack, typescript, javascript, react, programming, programmer, theo, t3 stack, t3, t3.gg, t3dotgg
Id: 9WvJDor5uvo
Channel Id: undefined
Length: 11min 41sec (701 seconds)
Published: Tue May 09 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.