In this talk, we'll get a live coded demo of building custom hand-rolled authentication. When you have the right tools (and we do), authentication can be quite simple and secure. This is more (and better) than just: "Install this library and you're good to go." When we're done we'll have our own auth code that can evolve with our ever-changing requirements without a need to learn some library-specific APIs. We'll be leveraging the Web Platform the way it was meant to be done to give us simple and secure server-side authentication for the web.
Server-side Auth with Remix, Prisma, and the Web Platform
AI Generated Video Summary
This Talk is about server-side authentication with Remix, Prisma, and the web platform. It covers adding authentication to a Remix app, troubleshooting and login setup, handling user login and session creation, creating user sessions and redirects, handling user ID retrieval and validation, and working with cookies in Remix. The speaker emphasizes that Remix is ready for production and suitable for enterprise apps. Remix simplifies the mental model and improves performance by bridging the network gap between the front end and back end.
1. Introduction to Server-side Auth with Remix
Hey Node Congress, my name is Kent C. Dodds and I'm really excited to give this talk, server-side auth with Remix, Prisma and web platform. We're working in Node because Node is awesome and Remix runs on Node and I love Remix. This talk is a live code demo of adding auth to an existing app based on the tutorial from Remix. It's a condensed version, but it should give you a good idea of how Remix approaches authentication.
Hey Node Congress, my name is Kent C. Dodds and I'm really excited to give this talk, server-side auth with Remix, Prisma and web platform. We're working in Node because Node is awesome and Remix runs on Node and I love Remix.
What this talk is is a live code demo of adding auth to an existing app and we're actually basing this off of part of the tutorial from Remix. So if you've gone through the jokes tutorial in Remix, that's kind of what we're going through here. This is not complete. I only get about 20 minutes with you, and so it's not going to have everything, but it should give you the right idea of how Remix approaches authentication, and I think it's in a really good way.
2. Adding Authentication to Remix App
Let's start by adding authentication to our Remix app. Currently, anyone can add jokes without authentication, and we want to prevent that. We'll begin by adding a user model in the schema.prisma file and associate jokes with specific users. We'll store hashed passwords for security. After updating Prisma, we'll create a jokester named Cody and use their ID to associate jokes with them. Finally, we'll push the changes to the database and resolve any type errors caused by seeding jokes without associated jokesters.
So let's go ahead and get started since we don't have a lot of time, let's jump right into the coding. So here we're actually going to take the full screen off, get rid of this, Remix you can find at Remix.run and you can read all about it and scroll through this, it's actually a pretty cool scrolling experience to go through that and get an idea of what Remix is.
This is the app that we're going to be working on, Remix, so great it's funny. It's a jokes app where you can get random jokes and it'll just have a bunch of different jokes for you. It's pretty fun and then you have permalinks and stuff and in the tutorial we add the ability to delete the jokes, you can add your own, and this actually works right now, but the problem is that there's no authentication going on right here so anybody in the world could add their own jokes, we'd never know who did it and people who add jokes they can't remove those jokes and we don't want to add that capability until they can log in because we don't want people removing other people's jokes.
So that is what we're going to be adding to our app, it's just the ability to authenticate. So we're going to start out in our schema.prisma file, so we've already got Prisma up and running that's holding all of our jokes and we have a seed to get all of these jokes put into the database already, but we want to add user authentication and so to do that we're going to need a user model so we can associate jokes to specific users. So let's go say model user and Copilot is going to help us out with this stuff. Not all this is what we want though, so we get an ID, I like the UUID personally, thanks a lot Copilot. We do want the created app, we want the updated app, and then we're going to have a username, not just a name and this needs to be unique. And then we don't really need an email here so we'll get rid of that, we do want a password though, but we're not storing like raw passwords, we're going to store a hash of the passwords to make it nice and secure. And then we have a relationship with the joke model inside of this user so we'll have jokes and that is an array of jokes. And it looks like my datetime, or Copilot didn't quite get this right so this is going to default to now, and there we go. So now we're getting a red underline here because the joke model is not associated to the user model and, whew, that was a close call, we had two N's right there. So now we want to add a jokester ID, and this is going to be a string, and then we'll have the jokester, which in this case is going to be a user, and we're going to add a relation with the fields for this relation is just the jokester ID, there we go, thank you, and the references is just the ID, and then on delete we'll cascade. So when we delete the jokester all their jokes are going to be gone. And let's make sure we spell this correctly. There we go. Okay, cool, so we've gotten Prisma updated, let's go ahead and run a couple of Prisma scripts, so we'll run npx Prisma db push, to push all of these changes. Yes, we were going to delete all of the things, and now we should have some type errors right in here, because we're now seeding the database with jokes that don't have jokesters associated. So let's go ahead and make our first jokester. And this is going to be Cody, we're going to await Prisma user create, and the data for this is going to have the username of Cody. And the password hash, is actually going to be twixrocks. And I have this pasted over here, because I, you don't want to watch me type that out. But this is basically twixrocks, hashed. And so that's going to be our password for our Cody user. So now that we have Cody, we can use Cody for generating or creating all of these jokes. So we'll say our data is going to be all the joke properties plus the jokester ID of Cody.ID. And so that can be our data. And now our TypeScript stuff, hopefully it will go away.
3. Troubleshooting and Login Setup
I'm not sure why it's not, let's just figure out what's going on here. We know that from the referrer header. Thank you very much. So now we've been able to seed our database and now we have users and everything and everything actually still works. Now creating a joke will not work because you need to have a user ID to create jokes, but each one of these jokes is associated to our Cody user and so now all we need to do is make it so that we can log in as our Cody user so we can add additional jokes and stuff to our app. Let's go ahead and go to our login, which is already all coded out for us. We go to slash login, then we'll see we have both login and register in here. I don't have a lot of time so I pre-coded all of this stuff up and we've already got our action for handling our form submission.
I'm not sure why it's not, let's just figure out what's going on here. Jokester ID, am I misspelling something? Jokester ID. There we go. You gotta spell things right in programming, gets kind of funny if you don't. Or rather, consistently. Doesn't have to be right, but it has to be consistent.
We know that from the referrer header. Thank you very much. Okay. So then I've got this script here called npm run setup DB. This is going to push and then regenerate everything. And of course, it blows up because the user doesn't exist in the database. So we're going to Prisma DB reset. Oh shoot. No, it's Prisma DB push. I thought that I'd done that already. Now it's Prisma DB migrate dev or no, it's just Prisma. Oh man. I'm falling all over myself. Ah, what am I doing? Yes. And this is going to be users. Sweet.
Okay. So now we've been able to seed our database and now we have users and everything and everything actually still works. So yeah, I'm actually technically still running this. So the app still works technically until we try to create a joke. Now creating a joke will not work because you need to have a user ID to create jokes, but each one of these jokes is associated to our Cody user and so now all we need to do is make it so that we can log in as our Cody user so we can add additional jokes and stuff to our app. So with that in mind, let's go ahead and go to our login, which is already all coded out for us. We go to slash login, then we'll see we have both login and register in here. I don't have a lot of time so I pre-coded all of this stuff up and we've already got our action for handling our form submission so you can see the form submission right there.
4. Handling User Login and Session Creation
We need to write the logic for logging a user in and getting their information when creating a new joke. We'll create a new file called SessionServerTS in the utils folder. It will contain an async function called login that accepts a username and password as strings. Using the database connection from db server, we'll find the user based on the username. If the user doesn't exist, we'll return null. If the user exists, we'll compare the provided password with the hashed password using bcrypt. If the passwords don't match, we'll return null. If everything is fine, we'll return the user.
We've got our validation being handled here to show validation errors and all of that stuff. That's already coded up for us. So all we need to do is write the logic for logging a user in and then we'll write some logic for getting the users information when they're creating a new joke.
So let's make a new file to handle all of our session stuff. So we'll go to utils and we'll create a new util called SessionServerTS. So this is only a server file. That's why it's called Session.server. And we're gonna export a new function. That's an async function called login. This is going to accept a user name and password. And that's user name is a string, and the password also a string. Both of these things are required.
And now we just need to, let's bring in the database actually. So db is gonna come from our db server. This is our connection to Prisma. And we'll say user.find first, and we'll say where user name. And now we'll have our user is, oh wait. So now if we don't have a user, then there is no user by that user name. And so we're going to just return null, and then our UI will handle it and say, your username or password is messed up.
If we do have a user, we need to double check that the password's correct. And we're gonna, we use bcrypt to generate the hash. We can use bcrypt to compare the hash. So we'll say bcrypt from bcrypt.js. And with that we can say const isCorrectPassword equals bcrypt, oh we're gonna await this, it's async, because it's slow, and that's like, that's the feature actually, is that it's slow and that's why it's secure. Fancy that. So if there is not, if this is not the correct password, then we'll return null and let our UI say username or password is messed up. And now if all of those things are fine then we can return the user. So that is authentication, that's login, when you've got your own database and you're just hashing stuff with bcrypt. It's actually really quite simple. And so we can log in, we can get a user, but we need to create a session.
5. User Login and Session Creation
Let's get our user by calling the login function with the username and password. If there is no user, we'll return a bad request with an error message. Otherwise, we'll console log the user and proceed to create the user session and redirect them to the jokes page. In Remix, we manage sessions using cookies and the create cookie session storage function. We configure the cookie by setting its name and other properties.
And so we're gonna, we'll do that next, but let's go ahead and continue with this for now. So let's get our user. User is await, login, we'll bring that in, username, and password, and if there is no user, then we're going to return a bad request with a form error that says, hey, this user does not exist, or whatever we want to say. So user, or yeah, incorrect, username or password. Thank you, copilot, that helps out a lot. Otherwise, if there is actually a user then we can actually redirect and everything. And we'll do that later. Let's just make sure that we can get the user first. So let's console log the user, and now that console log is going to show up in our log output here. So I'm going to just clear some space. We'll say Cody, and Twix rocks, and it's not implemented yet. We're not actually redirecting, but on the server side we are getting the user. So that totally works. Huzzah! That's awesome. And even our validation works as well. So if I go to register, and we're down to just a couple of things, we're going to get all of our validation logic and everything. We kind of built that before. So sweet. So now what we need to do is create the user session and redirect them to the jokes page. So let's do that.
6. Creating User Sessions and Redirects
We'll create a session cookie called the remix jokes session cookie and encode it for security purposes. To ensure the secret is set, we'll define it as a constant and throw an error if it's not defined. The cookie will be set to HTTP only and apply only to secure connections. The max age will be set to 30 days. We'll create a function called createUserSession that takes the user ID and an optional redirectTo parameter. The default redirect will be to the root path.
So I'm going to call it the remix jokes session cookie. That seems to make sense to me. And then also we want to encode this cookie. So even if the user pops open their dev tools and they look at the cookie, they'll have no idea what this thing is. So we're going to encode it. And we also... It means that even if they try to change it or something, we'll know that they messed it up. So we do have a secret. And this is processenv session secret. And with that, of course, TypeScript is mad at us about this. So we're going to make it constant and call this session secret. And then we'll say if that's not defined, then we'll throw a new error that says, come on, Copilot, thank you. Session secret must be set. Sweet. So we've got our secret set up.
We want to set this to HTTP only, so that users can't or third-party scripts can't access this cookie in the browser. That's one of the big problems with using JWTs in local storage. So yeah, HTTP only. We also want it to only apply to secure connections, so no man-in-the-middle attacks there, or rarely, I suppose. And then max age. Let's make this go, no, not seven days. Let's do 30 days. Who wants to log into a Jokes app every seven days? Not me. No. Okay, cool. So now we've configured our storage, so now we can create sessions with this storage.
So we're going to make a function called createUserSession with the user ID as a string. And we could also have a redirectTo so that redirectTo is configurable, but we'll just always redirect them to slash, you know what, let's do it. Let's say, redirectTo string. So they can control where they want the user to go when we create this user session.
7. Creating User Sessions and Redirects
Let's make our session and create a brand new session object. The user ID is set into the session, and a redirect is returned to the desired location. The set cookie header is used to set the cookie in the browser's registry. The create user session utility from Remix is used to create response objects.
So let's capitalize this properly. There we go. Okay, sweet. So let's make our session, we've got our session equals await storage, getSession. So what this is going to do is it's automatically going to create a brand new session for us. So we're not getting a session from an existing cookie. We're not passing an existing cookie. So it'll just create a session object for us. This is also async. So we'll need to make that async.
And now we'll say session set user ID, user ID. So we don't have to do any sort of like database look up or anything like that. It all just lives in the cookie. And we know that if it's in the cookie, then the user's authenticated. And there was no other way for that to get in there other than going through our session secret encoding and all of that stuff. So we're this is actually very, like quite secure. It's awesome.
So now that we have this, the user ID set into the session, we're going to return a redirect to wherever they wanted to redirect to. An important part of all of this is that we set the set cookie header so that the browser will set the cookie in the browser's cookies registry or whatever. So we'll have some headers right here with a set cookie, and we'll use the storage to commit the session. And here's our session. And the session is async. So we'll await that. And then this redirect is a utility from Remix that just is like, helps you create response objects. So that's what we're returning from this create user session. We'll export this. And now we can use create user session right here. We'll just return create user session with user.id and redirect to, which is coming from the UI. So based on how we got into this UI in the first place, the pages that can redirect to the login will set the redirect to. So the user can go right back to where they started from.
8. Handling User ID Retrieval and Validation
On the jokes page, I don't have good error handling yet. The user's ID is not being retrieved, causing issues when trying to add a joke. To solve this, I'll implement a require user ID function at the top of the form action. This function will get the user ID from the request object, which holds the cookie. By doing this, we can ensure that the user is logged in before proceeding with the form validation.
Okay, cool. So let's try this. So we're on login. I can say, Cody, Twix, Rox, and boom. Now I am on the jokes page and I've got my application right here. So I can look at my cookies right here and boom, there it is. That's my RJ session. It's all encoded and stuff. I have no idea what this is as a user and if I try to change it or anything, it wouldn't work because it wouldn't be, my server wouldn't be able to decode it at all. So that's sweet.
Now, when I want to go to the jokes slash new page, I want to be able to enter in a joke here and add that and it all blows up. I don't have good error handling on this page yet and the reason that it's blowing up is because we're not getting the user's ID yet. So let's go to that really quick in the last couple of minutes that we've got here and we'll say, hey, yeah, thank you TypeScript. You would have saved me from a terrible doom. What I really want to do is at the very top of my action of handling this form, I don't want to go through all the validation stuff and say, hey, you got this wrong. You got this wrong. And then they finally get it right. And only then do they realize, Oh shoot, I'm actually supposed to be logged in. So we're going to just put this at the very top to get the user ID and we'll say, oh, wait, get user ID. And actually let's call this require user ID so that we know that if they don't have it, we need to redirect them because like some routes might be, it might be optional to have a user ID. So in our case it's required. So we're going to say required here and we'll pass in the request. So require user ID is going to come from our session server and we'll export a function called require user ID that takes the request and the request is what holds the cookie. And so this is a request object. And that's just, that's a web standard right there request that's coming from the web fetch API, the web API. Super cool. So there's the web part of everything that we're doing. Also cookies, but yeah, so let's get our session. So wait storage, get session from the request headers, get cookie. So the request is automatically going to have the cookie on it because that's just the way the browser works.
9. Handling User ID and Joke Creation
If we have the user ID, we know the user is logged in. We can throw a redirect to the login page if the user is not supposed to be there. Remix handles these redirects for us, making the process simple and abstracted. If the user ID exists, we return it and associate it with the jokester ID. We can then create a joke associated with the user. Adding registration and logout is straightforward, with the ability to destroy the session when logging out. Overall, the process is simple and easy to implement.
And if that, um, if we have the user ID or rather, if we don't have the user ID, then we know that the user's not logged in. And instead of throwing a new error, we can actually throw a redirect to say redirect to login. So users not supposed to be here. Let's redirect them. So instead, you can throw responses in remix, which means that this is really nice and abstract double. You don't have to do a bunch of logic here. Like, is there a user ID? There's not. So let me redirect or whatever. You don't have to do that in every place. You need the user ID. You can just do it right here and remix will handle that for you. It's really, really awesome. And you can do that for, are they authorized and a bunch of other things too is really cool.
Okay. So if they do have the user ID, then we'll just return that user ID. And we'll come over here, grab that from the server. And now we've got the user ID right there. And that is going to be our jokester ID. Boom. Sweet. So now I can say, uh, let's just refresh your milk and, uh, let's see, milk, uh, milk is also the fastest liquid on earth. It's pasteurized before you even see it. Haha. Sweet. So now we have, uh, a joke that we were able to create and it's associated to our user. And that's awesome. Unfortunately, I don't have enough time to show you, like, let's add registration and log out and stuff, but you should get a good idea. Like basically log out is a matter of, um, instead of commit session, you destroy session and uh, that works. It's, it's pretty simple. But what I really want to point out here is that this is actually quite simple.
10. Using Platform APIs and Demo
It's using platform APIs. You're able to talk to the database directly. It's all like seamless between the client and the server. You also, I just really love that because we've implemented it, we see all this code and it's actually because it's simple, we can add and change whatever we want to. So that's the beauty of remix is it makes it so that you are in control and you can change things over time as needed. And I just really appreciate that simplicity of remix.
So that's it. Thank you all so much. I hope you have an awesome time at Node Congress and we'll see you all in the future. Bye. Hello. Hey again. Good to have you. Thanks for this amazing talk. So most people, 48 percent said blue, green, yellow, pink, red. Give us the word. What's the correct order? That is correct. So well done 48% of you.
Logo and Q&A
I can't remember the order of colors in the logo, but it's a nice logo. Let's move on to the audience questions. The first question is about the data in the cookie, whether it's encrypted or encoded. The data is signed with a secret to prevent tampering. When it comes back to the server, it can be verified. Another question is about REMIX readiness for production. Yes, it is. My website has thousands of users and a high number of page views and unique viewers per month.
I can't. Well, I can't. Really cool that people remember this. I just remember the font. But that's me. With the shadows. It's a nice logo but I don't remember the order of colors. I know the colors from the Coca-Cola logo. Oh, there you go. That's a bit easier.
So, again, we're going to jump to the questions from our audience, if that's okay with you. I'm happy to answer your questions. I'm looking at the chat here. We've got a couple already. I'd love to hear more. Keep them coming.
The first one you already answered in chat but for the people that haven't been reading the chat is from Argentyle1990. The data in the cookie, is that encrypted or encoded? Yeah, that's a great question. I answered it before realizing that I was supposed to wait to answer it now. No worries. But, yeah, so it is signed with the secret, so people can't make their own or change it or whatever. Excellent. So when it comes back to the server, you can verify that it actually was created by the server. Okay, cool, thanks. Oh, you just advanced to level one on the Discord Server? Congratulations. Oh, really important question from CCCChris. Is REMIX ready for production? Yes, it totally is. So my website is a production website, it has approaching 3,000 users, and that's, like, people who have created accounts and everything. I get around a half a million page views a month and over one to two hundred thousand unique viewers a month. So that's pretty production.
Remix, PayPal, and Tesla
When we hit 1.0, that was us signaling to the world that Remix is ready for production and we are actively maintaining it and working on it now too. If Elon Musk came to you and said, I'm starting PayPal all over again, would you choose Remix? For sure. The entire PayPal property is very well suited for Remix. I am in conversations with engineers at Tesla and various other companies that I'm not sure I'm at liberty to tell you, but yeah, a lot of people are very interested.
It's not like what I built at PayPal, but I would be so happy to have Remix when I worked at PayPal. So yes, when we hit 1.0, that was us signaling to the world that Remix is ready for production and we are actively maintaining it and working on it now too. And then as a follow-up on that, so let's say Elon Musk came to you and he said, I'm starting PayPal all over again, would you choose Remix then? Oh, yeah. Are you kidding? For sure. Yeah, yeah, yeah, I would 100% like the entire PayPal property is very well suited for Remix. And then actually, on that note, since you mentioned Elon, he hasn't reached out to me or anything, but I am in conversations with engineers at Tesla. I can tell you that because our conversation started on Twitter. So I know that it's public. But yeah, so we're talking with people at Tesla and various other companies that I'm not sure I'm at liberty to tell you, so I won't mention, but yeah, a lot of people are very interested. Super cool. Yeah. Well, I might have to join the engineering team of Tesla here in Amsterdam though. Do it!
Working with Cookies in Remix
Remix provides a nice API for working with cookies. You can decode the data and pull the user ID from it. While you can store application state in cookies, it's not recommended to store all of your app state in a cookie due to size limitations. However, Remix allows you to leverage web technologies and do whatever you want with cookies.
So it works like a JWT where you can decode the data, but there's nothing you can do with it on the server. I mean you can, you can do stuff with it. You can pull the user ID out of it. You could store like all of the application state in the cookie if you wanted to. And for some use cases that makes sense. You are limited to the like size of cookies and you could do like multiple cookies without any trouble. Just keep in mind that when you store stuff in a cookie, every single request is going to have that cookie in it. So probably don't store all of your app state in the cookie. But, but you could if you wanted to. And so yeah, like the, the cookies as a technology or web platform technology, like this has very little to do with Remix other than Remix gives you a really nice API for working with cookies. But outside of that it's all web technologies. And so, yeah, you can do whatever you want to because it's a cookie.
Remix Benefits for Enterprise Apps
Remix provides a simple mental model and a great development experience, making it an ideal framework for enterprise apps. The state management is seamless, as Remix ensures that the client and server stay in sync. Additionally, the out-of-the-box user experience is excellent, reducing the need for extensive customization. While focus management for accessibility requires additional consideration, Remix provides a solid foundation for building enterprise-size apps.
And so, yeah, you can do whatever you want to because it's a cookie.
Next question is from Hail to the Wood. What are you most excited about that Remix can provide for enterprise size apps? So nice piggyback on the previous question. Yeah, yeah, that's a super question. So I think for any enterprise, like one of the biggest problems with enterprise is not actually the technology. It's how do we get this to work with a team of developers. You have like, or even even a half a dozen teams all working on the same project. That was probably the biggest, one of the biggest problems that I had when I was a PayPal. Probably the biggest problem I had when I was at USAA, just coordinating effort on a single project with people who don't ever talk to each other and don't even review each other's code because they're in different areas of the app.
The cool thing about Remix is that the mental model is the same everywhere in the app. And so the like state management isn't really a question when you're working with Remix. In my talk, I mentioned how you don't have to worry about state management here because Remix is just... Basically, your Redux Store is the database. And so you're not thinking about how do I keep the client in sync with the server? You're building your app and Remix ensures that those things stay in sync just by nature of how it's built. So the mental model is basically a Web 1.0 mental model with a Web 4 developer experience. I don't want to say Web 3 because that actually has meaning now. But yeah, so it's a really awesome development experience with a very simple mental model. And so when you're working with a team of developers like enterprise apps are, I can't think of a simpler framework to go with.
On top of that, it's like the user experience is stellar. And so you and like the out of the box user experience is really good. And so you're doing... End user or developer experience? Yeah. End user. Yeah. So the developer experience is amazing, mental model simple, all of that. But then the user experience and user experience is really awesome as well. And so you don't have to spend so much time trying to make the user experience awesome because just the default user experience is really good. And so I mean, like, of course, you're going to want to think about focus management and stuff like that for accessibility. There's only so much that a framework could do. You won't be able to build an abstraction for focus management.
React Compatibility and Authentication Strategies
Maybe in some ways, but like react to use effects plus refs are a really nice way to do focus management. And all of react works just fine with with remix. And I guess I'll take this opportunity to say in the future, other UI libraries will likely work with remix as well. But enterprises will love remix. Cookies is the default of choice. Remix can also be used for other types of apps as well. If you're just building a web app, stick with the platform is usually the best choice. We have time for one more question, and it's from useAna Andrazin.
Maybe in some ways, but like react to use effects plus refs are a really nice way to do focus management. And all of react works just fine with with remix. And I guess I'll take this opportunity to say in the future, other UI libraries will likely work with remix as well. And so it's not a react framework, it's a web framework. And yeah, anyway, that's a long answer to that simple question. But enterprises will love remix.
Nice. Well, that's the answer. Right? Yeah, everyone. So my call MC is asking if you can direct questions to my son, but let's keep it with Kent. Kent, I think I well if you can see my son and let me know but I think people are here for you.
Where we see CCC Chris is asking cookies is the default of choice. There might be other options that might be better for certain scenarios or not needed. Yeah, so I can't think of other situations where you'd want to use something else for a web app. But Remix can also be used for other types of apps as well. So like you could actually take your Koa or your express REST server and rewrite it in remix, like reimplement the whole thing, remix.
You could have a GraphQL API that's written with Remix and actually the really cool aspect of doing that is that if eventually you decided, hey, we've got this REST API server, I want to have some sort of admin interface for it or something, you know, to inspect some of the data or whatever. Then you could build that because you've built it with Remix without having to completely re-architect something or run another thing alongside or do some sort of hacky thing to like generate HTML or whatever. And so, yeah, in those situations, if you're supporting and actually you could have your app that's built with Remix and say, hey, I want to have like my mobile client be able to interact with this. You can have what's called a resource route that can do anything that you want to respond to any requests with any kind of response and put a GraphQL endpoint on your web app. And then you have mobile clients. And so all of that to say that for those use cases, other authentication strategies might make more sense. But if you're just building a web app, if that's all that your web app is supporting, I can't imagine why you'd want to do a different authentication strategy. This is the web, and that's how we do on the web. So just use the platform. Stick with the platform is usually the best choice.
Yeah. Yeah. We have time for one more question, and it's from useAna Andrazin. You have two minutes for this question.
Unique Feature of Remix
Remix sets itself apart from other frameworks by building a solid foundation over the network chasm between the front end and the back end. This network bridge simplifies the mental model, improves performance, and reduces the need for manual management.
What would you say is the most unique feature that sets Remix apart from other frameworks? Good question. Yeah. Yeah. So for folks wondering, a lot of people compare Remix to Next.js. And we don't like to talk about that a lot just because it's distracting, but people do wonder, so we wrote a blog post about it, and I'll share a link to the blog post in the notes here later, or in the Discord server. But if you just Google Remix versus Next and look at the Remix blog post, you can see a very in-depth and objective comparison of the two. And the biggest thing for me that sets these two, or any framework apart from Remix, is that Remix builds a super, super solid foundation over the network chasm between the front end and the back end. And that is one of the biggest challenges for all frameworks or for all web apps. That network chasm is the reason why state management is hard for UIs. It's the reason why performance is a problem for UIs. So, with a really nice network bridge, you can drastically simplify the mental model, improve performance, and reduce the amount of stuff that you're having to manage yourself. So that's the biggest thing for me.