
RedwoodJS 4
Nick Taylor hosts Dom and Anthony from RedwoodJS's core team to discuss new features in v4 including custom auth and Vite support
Episode Description
Nick Taylor hosts RedwoodJS core team members Dom Saadi and Anthony Campolo to explore Redwood v4's new custom auth API, experimental Vite support, and live-code a custom authentication flow.
Episode Summary
This live stream dives into RedwoodJS version 4, recently released at the end of January, with core team members Dom Saadi and Anthony Campolo walking through the framework's headline features: a new flexible auth API and improved GraphQL security. After a brief overview of what Redwood offers as a full-stack JavaScript/TypeScript framework — including its opinionated golden path inspired by Rails and Laravel, built-in Storybook integration, Prisma ORM, custom router, and the concept of "cells" as a declarative data-fetching primitive — the trio spends the bulk of the session live-coding a custom authentication flow from scratch. Using Redwood's CLI generators, they scaffold pages, components, and serverless functions, then wire up a login form that hits an API endpoint, validates credentials, stores a token in local storage, and hooks into Redwood's useAuth system. The exercise illustrates both how the new custom auth API works under the hood and why using a supported provider like Clerk or Netlify Identity saves significant effort. The session closes with a one-minute migration to experimental Vite support via a single CLI command, highlighting how Vite will eventually unlock server-side rendering for the framework, and a brief explanation of v4's GraphQL security improvements like query depth limiting and token caps.
Chapters
00:00:00 - Introductions and Audio Troubles
Dom Saadi introduces himself as a generalist on the RedwoodJS core team who handles releases, bug fixes, and triage work. He describes his role with a fun metaphor, likening himself to a garbage collector or runtime that cleans up resources and allocates work for others. Anthony Campolo, a developer advocate on the Redwood team, also introduces himself, though significant time is spent troubleshooting an audio issue where Nick cannot hear Anthony despite the live audience being able to.
The technical difficulties highlight one of the realities of live streaming with multiple guests, as Nick works through various fixes while Dom and the chat help relay information. Eventually a workaround is found using closed captioning, and the conversation moves forward with all three participants able to communicate.
00:04:38 - Redwood v4 Release and the New Auth API
Dom explains that Redwood v4 shipped at the end of January with two headline features: a new auth API and GraphQL security improvements. He describes how Redwood has always aimed to integrate authentication seamlessly, supporting providers like Clerk, Auth0, and AWS Cognito out of the box. However, prior to v4, users who wanted a provider Redwood didn't explicitly support were largely out of luck because the integrations were too tightly coupled to the framework's internals.
The new release changes this by exposing a flexible auth API that lets developers map any authentication provider's functions to Redwood's auth hooks. This means the framework no longer locks users into a limited set of supported providers, fulfilling Redwood's original vision of letting developers choose their preferred tools while the framework handles the wiring.
00:07:10 - What Is RedwoodJS and Its Core Concepts
Dom provides an overview of Redwood as a full-stack JavaScript and TypeScript solution designed to let developers build real applications and businesses without worrying about tooling configuration. He draws comparisons to Laravel, emphasizing that Redwood aims to be a complete framework with a golden path designed by experienced company builders like Tom Preston-Werner and others who dog-food the framework in their own products.
The discussion covers Redwood's core primitive called "cells," which provide a declarative approach to data fetching with four exports — query, success, empty, and error — eliminating the need for complex conditional rendering logic. Dom also explains Redwood's custom router, built by Tom, which deliberately avoids nested routes in favor of flat, predictable routing where developers always know exactly what a given route will render.
00:09:16 - Vite Support and Storybook Integration
Anthony expresses excitement about Redwood's experimental Vite support, noting that Redwood was among the last major frameworks still relying on Webpack, alongside Next.js. The conversation touches on how nearly every other framework — Solid, Remix, Svelte, Vue-based tools, and Shopify's Hydrogen — has already adopted Vite, and how attending ViteConf motivated the team to prioritize this migration.
Dom explains that while the Vite integration works well for basic development, Webpack's deep tentacles throughout the app — particularly around Storybook, Jest, and CSS framework loading — mean full coverage isn't there yet. The team respects Storybook's massive undertaking in supporting multiple frameworks and is waiting for Storybook v7's improved Vite support before fully committing to the switch.
00:15:00 - Cells, Routing, and Authentication Architecture
The conversation goes deeper into cells as Redwood's data-fetching primitive, with Dom explaining the render-then-fetch pattern and its trade-offs compared to frameworks like Remix that resolve data before navigation. He discusses how cells handle the common states of data loading — success, empty, error, and loading — without requiring developers to write chains of conditional statements.
Dom also covers Redwood's authentication architecture, explaining how the router is auth-aware from the start, enforcing authentication and role-based access at the layout level. Anthony adds that this tightly integrated auth solution is one of Redwood's key differentiators, noting that only Create T3 App offers a similarly complete built-in auth experience among JavaScript frameworks.
00:22:48 - Live Coding: Scaffolding a Redwood App
Nick bootstraps a fresh Redwood app using the CLI, choosing TypeScript, and the team discusses Redwood's use of Yarn 3 for package management. Dom explains the historical reason for choosing Yarn — workspace support that NPM didn't originally offer — and mentions that the Yarn team helped them navigate the upgrade from version one. The CLI generates mirror types for TypeScript compatibility with Redwood's unique file-naming conventions.
The group explores VS Code extensions, the project structure, and how to start the dev server using the Redwood CLI. Dom explains that everything in Redwood runs through the CLI with the pattern "yarn redwood [command]," drawing parallels to Rails generators for scaffolding pages, components, and other resources without manual file creation.
00:30:33 - Building Custom Auth: Client and API Setup
The live coding session begins in earnest as the team sets up custom authentication using Redwood's new auth API. They run the CLI command to scaffold custom auth, then examine the generated TypeScript interface that defines the auth client contract — login, logout, signup, getUserMetadata, and getToken functions that would normally be implemented by a supported provider like Clerk or Netlify.
Dom walks through how the auth system works internally: Redwood checks getUserMetadata to determine login state and uses JWT-based authentication with bearer tokens on GraphQL requests. They also generate a serverless function for the API side using the CLI, which creates the handler along with test and scenario files, the latter being used for database seeding in tests.
00:44:28 - Building Custom Auth: Wiring the Login Flow
The team wires up the complete login flow, creating a login component with a form, implementing the fetch call to the auth API endpoint, and connecting everything through Redwood's useAuth hook. They implement a simple password validation function on the API side that checks against a hardcoded list of valid passwords, returns a token on success, and a 401 status on failure.
After debugging a few issues — a missing submit button, incorrect localStorage method names, and an error response being saved as a token — they successfully demonstrate the full authentication cycle. The exercise, while deliberately insecure, effectively shows how Redwood's custom auth API maps provider functions to the framework's hooks, making the case for why using a supported provider saves considerable development time.
01:18:31 - Vite Migration and GraphQL Security
With about ten minutes remaining, Dom demonstrates the experimental Vite migration, which takes roughly one minute via a single CLI command: "yarn redwood setup vite." This generates a minimal Vite config using a Redwood plugin, encapsulating all the bundler configuration under the hood while still allowing developers to extend it. Nick notes how painless this is compared to manually unwinding Webpack configurations.
Dom then briefly covers the v4 GraphQL security improvements, including query depth limiting to prevent infinite resolver loops from deeply nested graph queries, and maximum token counts on GraphQL documents to protect servers from oversized queries. Anthony adds context about how GraphQL security has historically been a challenge with no blessed approach from the spec itself, making Redwood's built-in protections particularly valuable. The stream wraps with appreciation for Redwood's CLI-driven productivity and its comprehensive, opinionated approach to full-stack development.
Transcript
00:00:00 - Dom Saadi
[inaudible]
00:00:21 - Nick Taylor
So, we are back live at IAmDeveloper.live. My name is Nick Taylor, your host, and today I'm hanging out with a couple members from the RedwoodJS core team. We've got Dom Saadi and Anthony Campolo. Thanks for joining me, both of you. Dom, if folks aren't familiar with who you are, maybe give a little lowdown on who Dom the mysterious person is.
00:00:49 - Dom Saadi
Hey, everyone. Yeah, I've been on the RedwoodJS core team for, I want to say, like, two years now. So I do releases. If you see the release notes, I wrote those or had help writing them. My GitHub tag is jtor, so no relation to my name. But I have a lot of fun working on Redwood. I'm kind of like a generalist, so I'll help people with their projects or mainly fix bugs or address triage. Sometimes I'll lead projects myself, but I feel like I'm mainly like the garbage collector, or like the runtime, where I'm cleaning up resources or allocating things for people. But it's a lot of fun, and I learn a lot, and we're shipping a lot these days. I'm excited to talk about that and, yeah, what's coming up and what we just did.
00:01:42 - Nick Taylor
Oh, cool. No, that's awesome. Yeah, no, I'm excited to talk about all that too. And, yeah, maybe call yourself the Collector. It makes me think of Guardians of the Galaxy or the MCU. But yeah, Anthony, if you just wanted to go ahead and maybe let folks know who you are and what you've been up to.
00:02:04 - Anthony Campolo
Yeah, hello. My name is Anthony Campolo, and I've been on the stream in the past.
00:02:09 - Nick Taylor
I can't hear you, Anthony, for some reason.
00:02:12 - Anthony Campolo
You can't hear me?
00:02:15 - Nick Taylor
Why?
00:02:15 - Dom Saadi
[unclear]
00:02:20 - Nick Taylor
Okay. Yeah, why are you not getting picked up? Hold on a sec here. Testing, because I can't hear him either. Like I told you before the stream, Dom, it's never perfect, but this is actually the first time I have two guests, so bear with me for one second here. Let's take a peek here, because I see it coming through there, but why am I not hearing you? I'm just gonna ask the folks in the chat if they can hear you.
00:02:53 - Dom Saadi
Hello?
00:02:54 - Nick Taylor
Hello? Folks can hear Anthony. Do you mind, anybody just give a thumbs up in the chat? And if not, I'm gonna go and see. Okay.
00:03:08 - Anthony Campolo
Yeah, I can hear myself.
00:03:09 - Nick Taylor
I tested this before, so I'm not sure why it's not working, but that's life. I'm just gonna ask. Let's see here. Can anyone hear Anthony?
00:03:22 - Anthony Campolo
Dev said yeah.
00:03:25 - Nick Taylor
All right. I'll just wait. Okay. So everybody can hear you except me. I do have closed captioning on, which is good. So keep going ahead and say who you are, and I'm gonna see what's up here. All right.
00:03:46 - Anthony Campolo
[unclear]
00:03:47 - Nick Taylor
I'm glad folks can hear you, though.
00:03:48 - Anthony Campolo
My name is Anthony Campolo. I am a developer advocate on the Redwood team. Yeah, I see some friendly faces in the chat. Thanks for hanging out. And hopefully Nick will get this whole situation figured out soon.
00:04:03 - Nick Taylor
Why can't I hear you? Oh, man. So weird. Okay, let's keep going, and I'm gonna figure this out. Did you do your intro, Anthony? Give a thumbs up if you did, because I didn't hear it. Okay, cool. All right. So I'm gonna sort that out. Dom, you were saying you've been shipping a lot of stuff, and one of those things you've shipped recently is RedwoodJS 4. It dropped two or three weeks ago. Is that right?
00:04:38 - Dom Saadi
Yeah, just at the end of January, I want to say, like the 30th, so been two or three weeks now.
00:04:43 - Nick Taylor
Okay, cool, cool. Awesome. And I guess what are the big headlines there in terms of what shipped?
00:04:51 - Dom Saadi
Yeah. I think the tagline David Price wanted to go with was something like the new Auth API and then GraphQL security features or improvements. Those were the headliner statements.
00:05:06 - Nick Taylor
Okay, cool. Cool. Yeah. Yeah. Awesome. And I guess what does that entail exactly? That's like the high level.
00:05:17 - Dom Saadi
Yeah. So Redwood prides itself on integrating for you so you don't have to do any config. So auth was a huge part of that. Auth was actually one of the first things we shipped, way back when, before we were v1, and it was Auth0. We didn't have one auth provider we all loved, or one that was massively more popular in the JS ecosystem. So we didn't want to risk isolating people who didn't use Auth0, or people who only use AWS Cognito, for instance. We wanted to make sure there was a way for you to still use Redwood with the auth provider you want. And I don't think we realized that until just this release, to be honest, because the integrations were all very internal to the framework. You could set up Clerk or set up the in-house one, but if you wanted to use something we didn't support, you were pretty much out of luck.
00:06:23 - Nick Taylor
Okay, so basically there are hooks for people to get in there and plug in whatever they need through some common API. Okay, yeah, that's cool. Anthony, can you do me a favor for one second and try speaking? Because I think I might have fixed it.
00:06:44 - Anthony Campolo
Can you hear me now?
00:06:45 - Nick Taylor
God damn it. Nope, nope. Can you go ahead and speak again? Because I have closed captioning, at least.
00:06:52 - Anthony Campolo
Yeah, test, test. Can the captions hear me?
00:06:56 - Nick Taylor
Yeah, test, test. Okay. All right. For now, because people can hear you, I'm just gonna follow along in the closed captioning. It's a little awkward, but I don't want to spend too much time sorting this out while we're live, so.
00:07:08 - Anthony Campolo
Okay.
00:07:10 - Nick Taylor
Okay, cool. So yeah, I guess let's back up a bit. There's all this new stuff in Redwood, but for folks that might be new to Redwood, what is Redwood exactly? If you were to explain, like, I'm a dev, I want to dig into Redwood, what is it? What does it give me? And then we can kind of maybe go from there.
00:07:33 - Dom Saadi
Yeah. So Redwood is a full-stack JS/TS solution for building a real business or a real app. We do focus on, like, you could build your startup with this, but we don't want that to scare people off. If you're not building a startup, it still is great. It has everything you need. We want to be complete in a Laravel way. We're not quite there yet. We don't have an emailer, for instance. But those are the features that are on our roadmap and on our mind. You should use this from day one, and if you need an auth solution, you're going to have options. You're not going to have to touch a Babel config, or anything related to tooling. You're going to focus on your business instead, your actual logic specific to you. And we'll take all the pain of Webpack or Babel. When those upgrades come out, we'll make sure they work. We'll understand what breaks and what doesn't.
00:08:39 - Nick Taylor
Yeah.
00:08:40 - Dom Saadi
And yeah, that's kind of the major reason to use Redwood. There's a golden path there that people like Tom and Peter and David have thought over. And these are people who did build, and are building, companies now. So yeah, they're also feeling the pain and trying to push those patterns back into a framework from a dogfooding perspective.
00:09:04 - Nick Taylor
Yeah, cool. Cool. Yeah, Anthony, I think I can hear you. I did a hack to make it work, so I don't know if you had anything you want to add to that. Yeah, yeah, I can hear you.
00:09:16 - Anthony Campolo
Yeah, I think that probably covered it. I was kind of looking at some other things. Sorry, did we mention Vite at all?
00:09:26 - Dom Saadi
Oh, not yet.
00:09:27 - Anthony Campolo
Cool.
00:09:28 - Dom Saadi
Yeah, that is one of the newer things.
00:09:30 - Anthony Campolo
Yeah, I've been very excited for Vite support because, you know, there are so many frameworks now using it, and it seems like for the most part the only other one that was still on it was Next, which is like, Vercel employs the person who maintains Webpack, so it kind of makes sense they would stick around with that. But yeah, I think we were kind of the last ones to make this switch. We're not on Vite entirely. It's kind of like an experimental opt-in feature. But that's one thing that I've been really excited for and looking forward to us integrating.
00:10:06 - Nick Taylor
Yeah, no, for sure. I mean, everybody... well, aside from obviously Vercel using Turbopack for stuff, Vite is used pretty widely. I mean, starting off in Vue, like my coworker Ryan Carniato, they've plugged it into Solid. Hydrogen uses it from Shopify, Remix, and Svelte as well. So I think pretty much most frameworks have migrated to that, you know.
00:10:40 - Anthony Campolo
So yeah, it's pretty much everybody. This really came through for me watching ViteConf, because at the end of last year they had all the framework authors there and all these presentations, and that was why I said, man, Redwood's not here. That sucks.
00:10:59 - Nick Taylor
Yeah, no, it's. But the experimental. It's experimental in the sense that like, you've got it working, but it's not necessarily like recommended for production yet, basically. Is that what it comes down to?
00:11:13 - Dom Saadi
Yeah. Like you can set it up and I think it works fairly well. But we've had some like, edge cases where your favorite CSS framework might not be like auto loaded like the same way. Webpack kind of like has its tentacles all over the app. You know, it needs to know about everything for Storybook, for Jest and Vite needs to like, it works in the, in the very smallest way, which is like you start the dev server and then your logic is there, but we need to integrate it across the board and it's just not there yet. So your results may vary a little bit.
00:11:50 - Nick Taylor
Okay, gotcha, Gotcha. Yeah, speaking of Storybook, I forgot there's that integration as well. This ties into when you're building a product for your company or some platform for your company. Redwood's using React under the hood. Storybook supports, as far as I know, Redwood only supports React. Is that right?
00:12:14 - Dom Saadi
Yeah.
00:12:15 - Nick Taylor
Yeah.
00:12:15 - Dom Saadi
Okay.
00:12:16 - Nick Taylor
Yeah, so I'm a big fan of Storybook, and it's nice to have it tightly integrated with this because I've always been the person who sets up Storybook and configures Webpack. It's kind of nice to just be like, okay, here it is, and it's working. That's good to hear. And you were talking about the Vite integration. There's an open source project called Open Sauced, and I actually migrated their Storybook to Vite. I don't think they had that many loaders or plugins in terms of Webpack, but it sounds like you do more, because you were saying all the tentacles are everywhere.
00:13:08 - Dom Saadi
Or it's probably not that many plugins, but it's just a lot of files in places. And specifically with Storybook, like you said, there's actually a lot of mocking you have to do. If you just want to render your page or component, sometimes you need the router to be there, and then you need all this other stuff above it.
00:13:30 - Anthony Campolo
Yeah.
00:13:31 - Dom Saadi
So it's really just making sure we have full coverage before we recommend it, because we're not confident we've made sure it works across the board yet. You should totally try it out. It's not going to break your app, I don't think. And if it does, you can just go back to Webpack super easily. But it is way faster.
00:13:54 - Nick Taylor
Okay, yeah. I know because I was speaking with Michael Chan on the stream at some point. They have Vite support now, but there's still a part of Storybook that is still using Webpack, which they're working on pulling out. When you change your components, it does re-render faster with Vite, but it'll be nice once it's all been Vitified, or whatever they call it.
00:14:24 - Dom Saadi
Yeah, their v7 release is coming very soon, I want to say in the next couple months, and that's when I saw they had legit Vite support, or I guess legit enough that they're recommending you try it out and switch. So I'm excited, and definitely a lot of respect for Storybook, because they do, like, I think I do Webpack and they do like 10 times as much Webpack, you know? The kind of stuff they have to support, and even with their testing integration now, it's crazy what they have to do to be that framework-agnostic.
00:15:00 - Nick Taylor
Okay, yeah, no, for sure. Because they support quite a few frameworks now: Vue, Svelte, React, obviously Preact. And so yeah, there's a ton of work to rework that. But getting back to Redwood, because we're not talking about Storybook really, I guess maybe some other concepts we can go over. I definitely want to dig into some of the stuff that came in v4, but for folks, again, who are still new to Redwood or haven't tried it before, what are some other concepts? I remember, when chatting with Anthony, there's the concept of cells, and I think it'd be good to kind of go over that because that's kind of like a, I don't know if it's a primitive of Redwood or what you would call it, but it's definitely a core component of stuff going on in Redwood.
00:15:57 - Dom Saadi
Yeah, cells are like the... a primitive is a good word. It's the way you get data on the page. Every framework has a data-fetching solution, and cells, if I have to put them in a box, are the render-and-then-fetch model, which at this point is a bit of an outdated paradigm. Other frameworks like Remix make their pitch on having that all done before you even navigate to the page.
00:16:28 - Anthony Campolo
Right.
00:16:29 - Dom Saadi
So we're definitely exploring moving in those directions. You have the trade-offs that there's no waterfall if the data is all there. With cells, you have a waterfall depending on how you nest it, and you could get into some hot water there. But it takes advantage of a declarative approach where you have four exports: the query, then what do you want to show based on what happened? If there was data, then you want to go to success, like, hey, data's on the page, here it is, you can interact with it. But there's gonna be cases when there is no data, and you probably want to show something else, like "Get started," or some kind of call to action, maybe something more fun, I guess. And then there's an error, and you need to do something else again. That happens all the time. It's not like that's a one-off thing. It's literally on every page, or in different ways.
00:17:29 - Dom Saadi
So we don't want you to have to do a bunch of if statements all the time for if this happened and then if that happened, render this or render that. Because that can get gnarly in React if you have too many ways to render something and then you have to make sure you're rendering null here or is it coming in and out of the page. We just want you to focus on what do I want to show. And Redwood will take care of the routing in terms of the rendering there. That's the core idea.
00:18:00 - Nick Taylor
Okay. And so do you use an existing router solution like React Router, or is there a bespoke Redwood router?
00:18:12 - Dom Saadi
Yeah, there's actually a bespoke one. And this is like Tom's baby. Like he coded this, which is awesome.
00:18:18 - Nick Taylor
Okay.
00:18:18 - Dom Saadi
So definitely we took our cues from the existing routers, like Reach Router specifically. I think we looked heavily at how it worked.
00:18:29 - Nick Taylor
Okay.
00:18:29 - Dom Saadi
But Redwood does own its router, and it is for very specific reasons. Tom specifically did not like nested routes. He wants to know where a route goes, like what am I seeing when I render the route? I don't want to have to figure that out. That was kind of his take. There are definitely tons of advantages to nested routes, but you often don't know right away what you're going to see when you navigate there, depending on how much nesting is involved and how many different components can change things above you. So with Redwood, you kind of know exactly what you're getting when you look at the route.
00:19:10 - Nick Taylor
Okay, gotcha.
00:19:12 - Dom Saadi
You can just go to that page and you'll know right away.
00:19:16 - Nick Taylor
Okay, cool, cool. Yeah, no, that makes a lot of sense. And I guess in terms of the nesting, a lot of frameworks are using nesting now. Remix has nested layouts. Next.js is using nested layouts. So in the context of cells, it's a similar concept, I imagine, because it's all just components at the end of the day.
00:19:38 - Dom Saadi
Yeah, I think I used the word routing, but that may not have been totally appropriate. Cells don't technically route, but they route in the sense of which way am I supposed to go. They do routing in that sense of I'm supposed to show this because there's data, but you won't see the URL update. Redwood still has layouts in the sense of wanting to share components across pages. That's easy to do. That's the most nesting we have. And you can enforce authentication at that level too. I think that's one of the parts that's pretty unique: the router is auth-aware already. It knows if you're allowed to see this page or not. If you're not authenticated, or you don't have the roles, you don't have to go figure that out yourself.
00:20:32 - Nick Taylor
Okay, that's nice. The logic gets set somewhere in one spot and then it just propagates through the nesting.
00:20:40 - Dom Saadi
I think that's really important. A lot of the things we're realizing, especially with startups, is you kind of need auth from day one. You're pretty much always going to need it, especially in staging, when you're probably the most vulnerable because you don't have the most security, or you're trying to move fast and you're trying to deploy a lot. So for these things, especially not to have auth in the router, it's a lot of work, and it's a lot of work to do after the fact. So it's nice to have that up front.
00:21:15 - Anthony Campolo
This is still a thing that I use as one of the key benefits that Redwood has over other framework solutions, is that very tightly integrated auth solution. At this point, Create T3 App is the only one I can think of that really comes with a very good built-in experience. And they use NextAuth, which Next users usually have to build in themselves.
00:21:39 - Nick Taylor
No, for sure. And having an auth-agnostic solution is super nice. I've used Clerk before, and it hooks in pretty nicely. But auth is one of those things where, because I worked on custom auth for SAML, which is a whole other beast, it was a terrible idea doing a custom thing. Leave auth to people who specialize in it and just integrate it. Like you were saying, everybody's gonna need it. Even if you start with a landing page, at some point you're gonna need login for an admin section or a member section. So it's crucial, but it's not something people really want to manage themselves. That ties into the simplicity with Redwood in terms of it trickling down through nested layouts. I think that's super cool. I was wondering, would you want to maybe take a peek into some code?
00:22:48 - Nick Taylor
Like if I loaded up just a bare-bones RedwoodJS app, maybe you can talk about some of the new things we were talking about before, and we can maybe try to put some of those things in place.
00:23:03 - Dom Saadi
Yeah, totally. That sounds like fun.
00:23:04 - Nick Taylor
Okay.
00:23:05 - Anthony Campolo
Yeah, yeah.
00:23:06 - Nick Taylor
It's the first time I'm actually doing my Pairing View with three people, so let's see what happens. Okay. It looks okay. I tested this all with myself replicated three times, but anyways. Cool, cool. All right, so we're just in Pairing View. Oh, I should get out of here. I was working on some Remix stuff for work, so I'll just close this. And I guess I dropped a link to Redwood. I'll drop it again. So if folks want to get started, check out the docs there. But to get started, because it's been a while and I work on a bunch of frameworks at work, what's the command to bootstrap it? Is it npx or npm init for Redwood? I can't remember.
00:23:50 - Dom Saadi
We use Yarn, so it'd be yarn create redwood-app, I think, all dashes, and then the name of it. Or, you're right, yeah, create and then redwood-app.
00:23:59 - Nick Taylor
Okay.
00:24:00 - Dom Saadi
Then whatever you want to call it.
00:24:03 - Nick Taylor
All right, cool. So let's go into dev streams. Fun with RedwoodJS. There we go. Look at me, Ma. Okay, so we'll let this install. So I'm curious with the choice of Yarn Create versus something else. I mean, Yarn's pretty fast. I imagine you're using version one of Yarn still, versus... because I know there are a lot of people who didn't really go to Yarn two or Yarn three.
00:24:38 - Anthony Campolo
Hey, go to Yarn three. Actually.
00:24:39 - Dom Saadi
Yeah, we did.
00:24:40 - Anthony Campolo
Dom.
00:24:41 - Nick Taylor
Oh, you did. Okay.
00:24:42 - Dom Saadi
Yarn 2, you're absolutely right. It was not an easy upgrade, and we tried. Yeah. And we were just like, yeah, we can't do this. And then Yarn 3 came out, and the Yarn team actually helped us get through it.
00:24:55 - Nick Taylor
Oh, that's awesome.
00:24:56 - Dom Saadi
Some parts of the upgrade that were a little gnarly. But yeah, as soon as you globally use Yarn 1, as you can see here with the emojis, like the truck and stuff like that, as soon as you download the app, Yarn 3 is already there for you, and you shouldn't have to do anything. It should just start using it.
00:25:18 - Nick Taylor
Okay. Okay, gotcha.
00:25:20 - Dom Saadi
But to your question, back in the day we were using workspaces, and that was actually something that was just specific to Yarn. Nowadays, I think npm does have it natively, but it didn't.
00:25:33 - Anthony Campolo
And PNPM probably wasn't really around yet.
00:25:37 - Nick Taylor
Yeah. Yeah.
00:25:37 - Anthony Campolo
This is like 2018 when they first started building this.
00:25:41 - Nick Taylor
Yeah, I remember when, because I was working at Dev.to before, and there was the Yarn 2 post that came out from, I think his name's Mael, the core maintainer. And the reception of version 2 was not great. But anyway, I'm super comfortable with TypeScript. So do you want to do that, or should we just go for JavaScript? What do you prefer?
00:26:10 - Dom Saadi
Yeah, TypeScript's the way.
00:26:12 - Nick Taylor
All right. Okay, cool. And this is nice too. A lot of projects, you know, TypeScript's definitely gotten a lot more popular, so it's nice that you can bootstrap it here. I think most projects do that now. I think Next.js, correct me if I'm wrong, Anthony, but I think they default to TypeScript by now. You can still choose JavaScript, but I think they default to TypeScript.
00:26:39 - Anthony Campolo
Yeah, I think with 13 they moved over to it.
00:26:43 - Nick Taylor
Yeah, yeah, yeah. Okay. Okay, cool. All right. All right. Yeah, so we got a few things
00:26:48 - Dom Saadi
Yeah, it's a big split. I think we might be one of the only teams that still has a lot of die-hard... not die-hard, but not that they love JavaScript, more that they don't like TypeScript.
00:27:01 - Anthony Campolo
Yeah, deep TypeScript haters around our club.
00:27:05 - Nick Taylor
Yeah, yeah.
00:27:06 - Anthony Campolo
[unclear], so lots of interesting conversations.
00:27:12 - Nick Taylor
Cool. This is loading up right now. It's just setting up a few things here. I don't know if you want to speak to some of the things that are getting set up here, but obviously TypeScript. I guess the Git preference is just doing the repo. But I saw it generating types before. Is that related to cells or something? Or what are those types that it was generating before?
00:27:35 - Dom Saadi
So it's related to Redwood's directory structure a little bit. We didn't want to have a bunch of index.js files everywhere, because you don't know where you are. So we always name everything, like HomePage or whatever it is, and there is no homepage index file in the directory itself. But Redwood kind of has this Babel magic to it where you will always export the file the component's directory is named after, so it's just understood. But TypeScript does not just understand that, so we have to kind of tell it there's something going on here. I don't know if it's as well used, but they're called mirror types. It's literally for a file system. I don't know why else you would use it. So it's probably one of those things you'd never write yourself, but in our case, we have to. We usually come up with these ideas, and then we don't know if the ecosystem will let us do them or not, but we usually figure them out one way or another.
00:28:46 - Nick Taylor
Okay, cool, cool, cool. VS Code had this recommended extension, so I just went ahead and did that. I saw a few that I probably didn't want to keep, but whatever, it's a live stream. We've got everything loaded up here. Let me close all these extension windows. We've got the app bootstrapped here. I imagine if we want to start it, is it yarn start or yarn dev? It's been a while. I went through this with Anthony last year, at some point.
00:29:22 - Dom Saadi
Yeah, it'd be Yarn. So your command is going to be yarn redwood, and then that gives you the Redwood CLI. Everything is after that, and then dev would be what you want.
00:29:32 - Nick Taylor
Okay. Yeah. Okay, I remember this now. And this is getting back to what you were saying about like PHP and Laravel. And I know, I don't know Tom, but I know they're fans of Ruby and Rails, so like there's generators and stuff here, right? So like similar concepts from Rails. That's pretty nice. Let's just load up the site in dev mode just for folks and give that a second. Of course it opened it up in the window. I didn't want it to open up, but that's okay. Let's just close that and get another window. There we go. There we go. We're all set up. Cool. Back to tab. Yeah. All right. So awesome. All right, so we're up and running and at this point we can just kind of get down to business, right? Like we can. You get all the usual stuff. Like you get the hot reloading, hot module reloading and all that stuff, right?
00:30:33 - Dom Saadi
Yeah, it's already ready for you.
00:30:36 - Nick Taylor
Cool. Okay, so let's go back to talking about some of the new stuff, and I'll drop those links again for people. So I guess, I don't know if you want to do the auth part. We don't necessarily have to integrate a third-party thing, but is the API to wire up your auth solution pretty approachable? We could make up something, you know what I mean, like if the query string has logged in or something. Or what does the API kind of look like, or how does it integrate?
00:31:12 - Dom Saadi
Yeah, it may not be that simple, but we could just try it. You want to do yarn redwood setup auth and then custom, and we can walk through what it looks like. You got the Fig. Is that Fig for the autocomplete?
00:31:30 - Nick Taylor
Yeah, yeah, yeah, yeah. Oh, I forgot. I forgot to pass something, I think.
00:31:34 - Dom Saadi
Oh, yeah. So setup auth, and then it's which auth you want to do. Custom auth, in this case, which is not one we support.
00:31:44 - Nick Taylor
Okay. Okay. Yeah. Okay. So sure. I guess netlify. I don't know.
00:31:51 - Dom Saadi
Oh, I mean, like you could do netlify, but if you want to see how the API works and possibly do a little like A fake login. It would be custom in that case. Yeah.
00:32:02 - Nick Taylor
Okay, why don't I see custom? Well, I don't know.
00:32:05 - Dom Saadi
Sorry.
00:32:07 - Nick Taylor
No, it's not that. That probably just means there are definition files for Fig, so it probably just doesn't have it yet. Okay, cool. Have you messed around with any of the...
00:32:17 - Dom Saadi
Oh no, I, I, I'm pretty bare bones. I use like the terminal that comes with like Mac. I don't install like another one.
00:32:26 - Anthony Campolo
I like it.
00:32:27 - Dom Saadi
The Rust one, right? Yeah.
00:32:29 - Nick Taylor
Okay, yeah. What do you use, Anthony? I can't remember. Warp. Okay, yeah, I checked that out, and then my coworker, who's very privacy-centric, was like, "Oh, you need telemetry."
00:32:46 - Anthony Campolo
Yeah, yeah, I mean like that's gonna be for me. It's like I am not doing anything sensitive enough that I'm like super concerned about that. But it's, yeah, it's a reasonable thing. I think that's something that's not going to be required after beta also.
00:33:01 - Nick Taylor
Okay, yeah. Okay, that's good to know. All right, cool. We got this set up here. Okay, cool. All right, so I guess where do we look here now? I imagine it added to the API or something.
00:33:18 - Dom Saadi
So the API is actually pretty simple. There's not much to it. There's a decoder you have to implement. Redwood has JWT-based authentication, and every GraphQL request will have a bearer token. And then if you go to the graphql.ts file, it should be on the API side. In this case it didn't make it for you because it's custom, but we would have to make a decoder, like, how do I decode the JWT? What do you want me to do? Pretty simple. But we can do that later. Actually, most of the logic is on the website because there's a lot of hooks you have to define. You want to go to the auth.ts file on the website. Yeah, that one, I guess, not tsx.
00:34:16 - Nick Taylor
Yeah, I guess. I guess it's not TSX because there's no JSX in it.
00:34:20 - Dom Saadi
Yeah, that's right. I think some other ones do. Like clerk will have jsx. So it's just like. Yeah, you're a lot to keep track of.
00:34:28 - Nick Taylor
Cool, cool. All right, so okay, so we've got the typescript interface here. So this is what we need to implement, I guess.
00:34:35 - Dom Saadi
Exactly. Okay. Yeah, like a Redwood auth provider, like for Netlify or Clerk for instance, we'll have all these functions already implemented for you. Like, it knows how to log in via Netlify identity or log out, that kind of stuff. But in this case, since it's our custom auth, we have to do it all.
00:34:56 - Nick Taylor
Gotcha. That's a good exercise to show how it works. This looks like, I guess these are just placeholder emails and IDs here.
00:35:12 - Dom Saadi
Yeah, it's all placeholder everything. All of this would have to be rewritten, probably using some kind of fetch library. When you log in, there'd probably have to be some validation. Definitely when you sign up, like you can't use that username. It depends on how deep we'd want to go with it, but these are just little examples.
00:35:39 - Nick Taylor
Okay, so is there anything we need to do right now? It's custom, like you said. So here's the top type, I guess. And then. So we just have to. Those are all done there. So we just have to implement the client like a. Like I was mentioning. Okay, so with, with the, with the placeholder ones, can we actually log in? Like, because I don't see any passwords in the. Or. Or how. How would it work if we wanted to use the custom right now?
00:36:08 - Dom Saadi
Yeah, so I happen to know this just because I worked on it underneath. But like, the first way Redwood will try to see if you're logged in is it'll call. Get user metadata. So if we wanted to. Just like, if we wanted Redwood to be like, okay, like this person is logged in. Like, it usually tries to. Like, is there a user? Can I get a user? Like, if I can't, then, like, you're logged out and.
00:36:34 - Nick Taylor
Okay, yeah.
00:36:35 - Dom Saadi
And usually the client will say that like, the clear client will be like, yeah, there's nothing here. Like, there's. So in this case, we could just have that return like something and then in that case there would be a user. But that would let us kind of start with the get token function and then we could kind of try to set up the cross between the website and the back end at the same time.
00:37:02 - Nick Taylor
Okay, so should I just copy this payload here from. For the meta user data and just return it right away. Is that what you're suggesting? Or.
00:37:12 - Dom Saadi
Yeah, that might work. Like, and you can put it in like the. The client function. Yeah, for like own off client.
00:37:18 - Nick Taylor
Yeah. All right, so.
00:37:21 - Dom Saadi
Oh, wait, I should say everyone, don't do this. This is like horrible. Don't make your own.
00:37:28 - Nick Taylor
Oh, oh, actually it already has the meta user data. This is just a unique person. We just don't have any roles yet. That should return us this unique user id with emailxample.com, right?
00:37:44 - Dom Saadi
Yeah, you're right. In this case, even getting current user. Is that one there okay?
00:37:52 - Nick Taylor
Yeah, I think so. There's get token. So yeah, it's a fake token, fake metadata. I guess if you do the sign up, it just registers that person.
00:38:08 - Dom Saadi
You're right. Just with this, it might just act like we're logged in. Since we have it all hard coded, we could try that by making a page and destructuring the auth hook and seeing what it returns.
00:38:24 - Nick Taylor
I can come to pages here. These are some error pages and 404. So I just create a new route in here. Or is it a. Is it a route or is it a page? I know the. Some frameworks have different naming. A lot of them they call it routes these days.
00:38:38 - Dom Saadi
But so you could just stay on the CLI because like making files and naming stuff is too much work. So just do yarn, redwood, generate page and then you could call it like home or whatever you want to.
00:38:53 - Nick Taylor
So generate and then page, and then let's call it awesome. In terms of casing, because the JSX will be capitalized, but the page, can I name it lowercase and it handles that, or...
00:39:10 - Dom Saadi
Yeah, it'll go through and, like, I don't know if it's camel case, but for the path it'll be kebab-case, the component will be PascalCase like you mentioned. It'll take care of it.
00:39:23 - Nick Taylor
Yeah, okay. No, it's nice, because I'm only bringing it up because the other day I was helping somebody with an open source project, and they couldn't figure out why it wasn't loading. They were importing a PascalCase component, but the actual component file was lowercase, and it's one of those errors where you just don't really know what's going on.
00:39:46 - Dom Saadi
So you burn a day that way. It's horrible.
00:39:50 - Nick Taylor
Yeah. Okay, so it creates a folder and then this is nice. I believe, I can't remember. Did we generate a page when we looked at Redwood last time? Anthony? I can't remember.
00:40:05 - Anthony Campolo
Yeah, I think we just went through the basics, like create a page, get a database set up, get a cell set up, hit that database. I think we did do some auth, because it was 1.0 and I wanted to show off one of the auth things. I forget exactly how we did it or what we used.
00:40:24 - Nick Taylor
I've lost all sense of time in the pandemic. It was probably a year ago, anyways. Okay, cool. But the thing I like about this, you were talking about Storybook before being integrated. I like how the generator in the CLI gives us the page, it gives us a test file, and it also gives us the stories all co-located. I know people have opinions about folder structures, but I feel pretty strongly about co-locating it. It just makes more sense, I find. And I'm assuming folks on your team do as well. Okay, I think we need to go...
00:41:05 - Dom Saadi
a little further, but we're talking about making things more co-located when you have components on that page. But that's for the future.
00:41:14 - Nick Taylor
Okay, so we won't look at the test and stories right now, but we'll go to the page that got generated and I'll just give us some real estate here. Okay, so we've got our custom auth in place. So what's the next step? If we want to integrate auth to the page.
00:41:30 - Dom Saadi
So if you go back to the auth file real quick, you'll notice at the end it should export two things: AuthProvider and useAuth. So the auth provider has already been hooked up for you. That's in the app file and the routes file. But we want that useAuth hook because that's how we do React things with our components. Just go ahead and call that and it'll give you a bunch of stuff. In our case, I guess we want to see... You can import it from... oh, maybe it'll just... oh, the top one is great. Yeah, cool. useNoAuth is a bit funny. That's like an internal.
00:42:16 - Nick Taylor
Yeah. Yeah. All right. So in here, I guess we can use a lot of things in here. But what for what we're just kind of demoing right now. What should I pull out?
00:42:26 - Dom Saadi
I'm kind of curious what isAuthenticated will return, given the boilerplate there.
00:42:32 - Nick Taylor
Okay. Yeah. Yeah. Okay. And then I guess maybe get the current user.
00:42:37 - Dom Saadi
Yeah.
00:42:41 - Nick Taylor
So we could just say, I guess, isAuthenticated, then otherwise we can just do null, I guess. And then I won't bother with the current user yet. But I guess we can just start this up and see if we're authenticated.
00:43:11 - Dom Saadi
Yeah. Go to your routes file real quick. There's one gotcha. When you generated the page, the path just used the name of the component, so we could just make it slash so that we don't have to navigate anywhere for now in the first prop.
00:43:32 - Nick Taylor
Okay. Yeah, it's slash already. Or do you mean slash at the end as well?
00:43:35 - Dom Saadi
Oh, just make it like slash, like the index.
00:43:39 - Nick Taylor
Okay, gotcha. Yeah, so it just hits the home page. Gotcha.
00:43:42 - Dom Saadi
Yeah, there you go. Okay, perfect.
00:43:44 - Nick Taylor
Cool. Okay, so let's start this back up again. Yeah, I forgot about the CLI with the generators and stuff. That's definitely super powerful. I'm not a Rails dev, but where I was working before was a Rails monolith, and I remember doing database migrations and just creating some new things that way. It's definitely super handy, because a lot of that stuff, you could do it by hand, obviously, but it's just tedious. Okay, so let's see what it gave us here. Back here. So there we go. We got the awesome page, and we are authenticated.
00:44:28 - Dom Saadi
Okay, that's pretty funny. Yeah.
00:44:31 - Nick Taylor
Okay, cool. So I guess that's kind of nice if you just want to get started. Like you could have the custom auth. Like say you don't know what you're using for auth yet. Maybe be a way to just kind of start building out stuff.
00:44:45 - Dom Saadi
Yeah, it's like, depending on where you want to start, most of it happens when you're logged in. The hooks are not useful when you're not logged in. They don't do anything.
00:44:58 - Nick Taylor
But yeah, yeah.
00:44:59 - Dom Saadi
In this case, yeah, you wouldn't want that to start. So we could go a couple directions here. We could act like the authentication logic's all good and work with the other hooks and implement those. Or we could back it up and be like, our implementation's a little bugged. Thanks, everybody.
00:45:22 - Nick Taylor
Yeah, I guess it'd be nice to go through the whole login flow, so maybe spruce up the custom one a bit. I don't know. I don't know what you think. Andy. Anthony, should we go yolo?
00:45:35 - Anthony Campolo
Of course. Yolo it every day.
00:45:39 - Nick Taylor
Cool. We can head back into what was auth ts, right?
00:45:45 - Dom Saadi
Yeah.
00:45:48 - Nick Taylor
If we were to implement this for real, like our custom auth, even if it's not the best implementation of an auth solution, I guess the first thing is, what do you want to do? Do you want to maybe just do an in-memory database, like some object that's kind of holding everything?
00:46:07 - Dom Saadi
Yeah. We could make it so that getToken will check localStorage or something like that to see if something's there, and we just kind of use that. Or, if not, we could make a custom function that it hits.
00:46:19 - Nick Taylor
Okay, so yeah, gotcha. Cool, cool. Yeah, we could use localStorage. Might as well show some Web APIs. Okay, so if we go to here... no, that's the interface. Where's the actual client again? Oh, here. Okay. So I guess the first thing would be, I'm just gonna comment this out. So we would have to implement the login. And like you were saying, does the real login normally get parameters here, like username and password?
00:46:57 - Dom Saadi
Yeah, we would probably call this in the context of a form submission. So yeah, like a username and password.
00:47:06 - Nick Taylor
And then we can build out the page in a bit here. We could just do a simple check, like if it's got a username and password, and then return it. Sure. And then we could just say the username could be the email, I guess. I don't know.
00:47:41 - Dom Saadi
Yeah, that's how DB Auth does it by default is it'll just keep them the same.
00:47:46 - Nick Taylor
Okay. And then here we can just do the same thing. All right, cool. So that's good. Anybody can log in. Again, for folks watching, this is not something you want to ship to prod. This is strictly for demonstration purposes. [unclear]
00:48:06 - Dom Saadi
And we could make like a custom function just to make it like kind of cool and make it like, okay, like if your password is one of these, you get to log in. But if it's not.
00:48:14 - Nick Taylor
Okay, yeah, we can do that. Cool. All right. isValidPassword, passing a password. Again, just having some fun, just building out some stuff here. Then we could do this, put passwords here. You could just say it's an array. We'll just say password, password0, I don't know, we'll just keep it simple. And then we could just say passwords.includes(password). Again, just a very trivial demo, not production-grade, unless Anthony gives me the thumbs up. I'm not pushing this to prod.
00:49:21 - Dom Saadi
I meant like an API side function. But this also works. It's not going to be any more secure in the API side function. It'll just be more roundabout.
00:49:33 - Anthony Campolo
I mean, but.
00:49:34 - Nick Taylor
But we can do the API because we might as well showcase that. So let me.
00:49:38 - Dom Saadi
Yeah, you're going to hit something. It's not going to be on the client. Uh-huh.
00:49:41 - Nick Taylor
Yeah, exactly. So let's go to the API. And this brings up a question I had. Right now I'm fairly certain there's no server-side rendering story, right? Is that correct?
00:49:52 - Dom Saadi
Correct.
00:49:53 - Nick Taylor
No, I know that. I know it's been talked about, but
00:49:56 - Anthony Campolo
there isn't part of that story.
00:49:57 - Dom Saadi
Right, right. The idea with switching to Vite is that it'll enable that a lot easier than Webpack would.
00:50:05 - Nick Taylor
Yeah, yeah, no, I know for sure. But having said that, there is API routes available in Redwood. Okay, Right.
00:50:13 - Dom Saadi
Yeah.
00:50:14 - Nick Taylor
Okay, so let's go. And I guess also just a quick
00:50:17 - Anthony Campolo
Shout out in the chat. We got Talk to Me Gooseman, who works for Zeal, who's been doing a lot of work with the Redwood stuff.
00:50:26 - Nick Taylor
Oh, nice, nice. And also thanks for the follow, Gooseman. I saw you pop up before, but for some reason I couldn't find your name in the chat. But thanks for the follow. Oh, now I see him, or them, sorry. Hey. All right, cool. Okay, so I imagine we should use a generator if we're going to create a new API route. Yeah, exactly. Okay, you got this. We might as well do this the whole Redwood way, because otherwise it's going to be tedious, just copy-pasting or doing whatever. All right, so we're going to do the same thing again. Yarn Redwood generate, and I guess, is it API or what is it?
00:51:08 - Dom Saadi
it's function this time?
00:51:09 - Nick Taylor
Okay,
00:51:13 - Dom Saadi
maybe.
00:51:14 - Nick Taylor
Yeah. And there we go. Cool.
00:51:22 - Dom Saadi
Then it yells at us that we have a job to do here that we're not going to worry about.
00:51:29 - Nick Taylor
But that does bring up a question. Obviously once this is deployed, it'll be running in a serverless context, the API. But for local development, does it simulate that?
00:51:39 - Dom Saadi
No, the function, whether you deploy it serverlessly or server-fully, won't... There will never be a collision where two requests could come in and it doesn't wipe the context before. It'll always have one context per request, but there shouldn't be any noticeable difference between dev and when you deploy.
00:52:05 - Nick Taylor
Okay, cool. We created that function. So we now have an auth folder. And then I guess I understand what the auth test function file is. But what's auth dots or what's a scenario file in the context of the API?
00:52:23 - Dom Saadi
Yeah, so this is like a database scenario. Think about it that way, like, what do I want this function to do when my database looks like this? That's the scenario. I have three users, or I have zero users, or I have invalid data. What should happen? So you get to seed a database before your test is run, which is helpful, but we've actually had some scaling issues with this because you have to seed a database before every test. They have to run sequentially, more or less. We haven't spun up a bunch of databases. This works great in the beginning. We need to make it a little better as you scale.
00:53:10 - Nick Taylor
Okay. And correct me if I'm wrong, Anthony, but like, I think when we, when we did our stream last year, one of the other parts of the tech stack of Redwood is Prisma, right? Is that correct?
00:53:24 - Anthony Campolo
Yeah, yeah. We spun up a Railway database and connected to that with Prisma, Prisma schema.
00:53:32 - Nick Taylor
Okay, yeah, because I was listening to, I think it was PodRocket yesterday, and they had one of the developer advocates from Prisma on. They had something interesting, which I think, since you're using Prisma with Redwood, could matter. They have a new product, I think it's in version 4 as well, it's called Accelerate, and they're going to be able to cache the database throughout the edge in different locations. I only listened to it literally last night while I was out for a walk, but it sounded pretty interesting. Obviously your writes will still be centralized, but it sounds interesting in terms of if you were to deploy your Redwood app to the edge.
00:54:13 - Anthony Campolo
Yeah. This is super-duper new, and this has been a long-time pain point for Prisma. You get as much as you can onto the edge, but still end up with a single connection to your database, and you end up with this bottleneck. So they had the data proxy, and now they have this. They've been trying out some different things to mitigate this. This is very, very new. I haven't even tried this. It looks like you have to join a waitlist. Do you know anything about this, Dom?
00:54:41 - Dom Saadi
No, I don't. I'm sorry. Yeah.
00:54:45 - Nick Taylor
Oh yeah, no, it's all good.
00:54:46 - Dom Saadi
Prisma point releases... I unfortunately don't know as much about their business side.
00:54:51 - Nick Taylor
Okay. Yeah, no, like I was saying, I literally listened to this last night while I was out for a walk. So it's just, it just sounded like something really interesting that I think people who are using Redwood could benefit since Prisma comes out of the box. I think they did say it's in private beta right now because they're obviously testing it still. But anyways, that's just. Yeah, okay, cool. All right, so in our case here for the auth, we don't really need to seed anything in the database because we're not even touching the database right now.
00:55:25 - Dom Saadi
Right? Yeah, yeah. This scenario is only for tests, actually. So if you're not going to write tests, then it's not going to be useful for you.
00:55:34 - Nick Taylor
Yeah, okay, gotcha.
00:55:35 - Dom Saadi
But you should write tests, right?
00:55:37 - Nick Taylor
Yeah, yeah, yeah. Cool. All right, so let's get to our handler here, and I'll just close the terminal here so we get a bit of real estate. Okay, so there's all kinds of stuff in here. I see some JSDoc types in here for the different things. This is all AWS-specific, it looks like. Okay, so we have what I would say probably looks like a typical serverless function, regardless of what platform you're using, or most people use something similar like this. We've got the event, which I imagine is going to contain our payload with our username and password.
00:56:16 - Dom Saadi
Yeah, exactly. Here we could be like, given those usernames and passwords, we could decide if they logged in or not.
00:56:25 - Nick Taylor
Yeah. Okay, cool. Let me paste that in here. We can just get rid of this. Well, we could still keep isValidPassword, I guess. So in here we could do the same thing again: isValidPassword(event.body.password). It's not on the type, but I imagine that's where it's going to be.
00:56:54 - Dom Saadi
I think so, yeah. As long as if we make it, we're probably going to do the JSON application content type or whatever. So I think it would just be on the body.
00:57:04 - Nick Taylor
Okay. Yeah, so just say username, string, password. I think that should do it. Or maybe not. Oh, it's on body though. Anyways, we'll just go with any and any for now.
00:57:21 - Dom Saadi
I'm a fan of any. Yeah.
00:57:23 - Nick Taylor
Okay. All right. Again, don't ship that to prod. The red lines were just bothering me. Okay, so we're going to check that password and if it's valid, we'll return this. Okay. And otherwise.
00:57:41 - Dom Saadi
And then return back a token that they could save. Or we could have the hook save it in localStorage or something horrible like that. Sorry.
00:57:52 - Nick Taylor
Yeah, so we could just have like a object right here. Okay, so.
00:57:58 - Dom Saadi
Oh, it's all right. It's already being stringified so you should be okay. Is that how it works? Yeah.
00:58:03 - Nick Taylor
I don't know.
00:58:05 - Dom Saadi
Will it do it recursively?
00:58:09 - Nick Taylor
Oh, sorry. Yeah, I saw the stringify. Yeah, it'll do it recursively.
00:58:12 - Anthony Campolo
So.
00:58:13 - Nick Taylor
Okay, yeah, so we'll just say token then. We can just put whatever we want in there right now. I guess we can worry about that after. But I guess username maybe, and yeah, event.body.username. We don't want to return the password in the token, though, I don't think. Okay, so we got a token here.
00:58:44 - Anthony Campolo
Else
00:58:47 - Nick Taylor
we'll just throw status code 403, I guess.
00:58:51 - Dom Saadi
Sure. Or like a... yeah, I actually am pretty bad with HTTP because I'm so GraphQL right now. It's like, oh, just return 200, right? That's not how it works.
00:59:03 - Nick Taylor
Yeah, it's all good. We can do the same thing. I'm definitely no HTTP code expert, but we'll just say error.
00:59:16 - Dom Saadi
Yeah, that would exactly.
00:59:18 - Nick Taylor
I don't know, I'll just say nope, because I know security practices. You shouldn't say if it's a bad username or password. You have to be ambiguous. Just trying to throw in some stuff there. Okay. We're not using the context, but in the case, no pun intended, but in the context of Redwood. What does the context contain here?
00:59:40 - Dom Saadi
To be honest, I don't know. I've never used it. It's more of like. I think it's provider specific based on where you deploy. It might be important in that sense, but I don't think you're going to be reaching for it.
00:59:53 - Anthony Campolo
That much
00:59:55 - Nick Taylor
I know. On Netlify, if you're on the edge, it provides information about the location. I guess it's similar for that. Okay, so this looks like it should be good. We're going to see if it's a valid password, we'll return a token, otherwise 401 with... I just put an error message in there, so I think that looks pretty good. And if we come back to our... I guess we'll need to create a login page now.
01:00:24 - Dom Saadi
Yeah, we'd need a little form. Sorry, this is a pretty involved demo, unfortunately. It's auth.
01:00:30 - Nick Taylor
No, it's all good.
01:00:32 - Dom Saadi
Pretty full featured. But in this case you could generate a component so you still don't have to right click on any file directories.
01:00:41 - Nick Taylor
Okay, say component login.
01:00:45 - Dom Saadi
Yeah, that sounds great.
01:00:48 - Nick Taylor
All right. No, I mean, I think it's fine to go through the whole flow for this particular one. I know we touched on the other new features in here, but I think it's a fun exercise to show people how the auth flow would work. Okay, cool. So let's go to the login component, I guess. All right, so let's just make a form real quick, I guess. Then label, sure, and then input. Thank you, GitHub Copilot. All right, okay, I'm pretty happy with that. I'll just put some inline style here to make it readable when we load it. So let's just do display grid and then let's just do place-items center. That'll work. Anyways, cool. Okay, so we got that form component, and now we'll just need to import that into... I guess we can do it in our awesome page even.
01:02:10 - Dom Saadi
Yeah. And then after, in the onSubmit handler, we would call our login function from useAuth.
01:02:19 - Nick Taylor
Yeah, gotcha. Oh, there's a RedwoodJS form. That's not what I'm looking for, but...
01:02:26 - Dom Saadi
Oh yeah, you're gonna have to just. We do have react hook form as an integration.
01:02:32 - Nick Taylor
Okay, okay, gotcha.
01:02:33 - Dom Saadi
But in your case, yeah, HTML works.
01:02:37 - Nick Taylor
Oh, wait, no, it's Login. There it is. Sorry. So we can do this. Okay, so basically, if we're not authenticated, show the login form. Okay, so at this point we've got useAuth, so we're going to have to hit the client now first because we shouldn't be logged in at this point, right? Like if we look at the auth.ts...
01:03:01 - Dom Saadi
Right. We could change the auth.ts file. So yeah, the metadata, we could just make it return nothing for now, I think. Or maybe we could check localStorage to see if something's there. Then we could have the login function save something to localStorage so that we know there's a user after having logged in.
01:03:26 - Nick Taylor
Gotcha.
01:03:27 - Dom Saadi
We will need a bit of fetch here.
01:03:30 - Nick Taylor
Fetch logic here... actually, if we go back to the login for a sec, are we going to do an action here, or is this going to be all client-side with onSubmit?
01:03:49 - Dom Saadi
Yeah, all client side.
01:03:53 - Nick Taylor
Right. And then we could pass this in as a prop, but I'm just going to do it in here for now. So let's see here, login, and then up top here function login, and then that's going to take... that's onSubmit, so that's the form event. I forget, whatever, we'll just go with the generic one. Okay, so we're going to submit it. So I'm going to do event.preventDefault(), I guess, and then... yeah, you are right, it's pretty involved. It's all good though.
01:04:47 - Dom Saadi
It's like one of the custom and baked in. Right. So you can go back to building your startup.
01:04:54 - Nick Taylor
Okay, so let's just say this HTML form element, you know, let's just get them by IDs. I'm just going to keep it easy. Email password.
01:05:10 - Dom Saadi
Yeah, does that have like. Yeah, you're right. I don't know if it has the target but Perfect.
01:05:15 - Nick Taylor
Yeah. Normally I do the event target and you can get the form, and there's another way to do it, but for the sake of time we'll do it this way. At this point I need to make this async, and then from here I'm going to await fetch. What did we call it? We called it auth, right?
01:05:38 - Dom Saadi
Yeah. Here you can actually just call your useAuth hook so that you can share the logic. But you could just do it here, whatever's in the best interest of time.
01:05:50 - Nick Taylor
Well, for the use auth, how would I go about using that?
01:05:55 - Dom Saadi
You destructure useAuth in the login component and then pass the login function. Or you just use it in the... there's going to be two logins here, but I'd probably just wrap it with the preventDefault like you've done, and then just pass email and password to login and await whatever that does.
01:06:20 - Nick Taylor
Let me just rename this to. Be different.
01:06:30 - Dom Saadi
You'll need async before the function too. Sorry.
01:06:35 - Nick Taylor
Oh, did I not have it?
01:06:36 - Dom Saadi
That might be why I can't refactor.
01:06:42 - Nick Taylor
mixing up arrow and functions. I got to import the use auth there. In here you said I should have login, right?
01:06:54 - Dom Saadi
Yeah.
01:06:57 - Nick Taylor
So then we could do this and then I need to. Oh, I see what you mean here. Okay, so this should be event. We'll get this done and then we're going to log in and then from there. Let me just grab this stuff. It's in line. I know, but. I think this is what you meant. I know I'm doing it in line here, but.
01:07:28 - Dom Saadi
Oh, that's all good. Yeah, that's perfect.
01:07:31 - Nick Taylor
I'm going to pass those two in, and let's just get rid of this for now. Okay, don't worry about the CSS there. Okay, so email and password. What's it saying here? Okay, it's saying the login needs only one. What's it...
01:07:52 - Dom Saadi
Well, we can. We can do anything we want with login. So yeah, you could put them in an object just to get it to be happy. But yeah.
01:08:01 - Nick Taylor
Okay.
01:08:02 - Dom Saadi
Since it's our function and we own this auth, we control the interface and the signature.
01:08:07 - Nick Taylor
Okay, so. Okay, well we can go back to just email password. Sure. Okay, so this is going to do the login hook which will cause it to re render once we get some updates and other stuff. So at this point we've got the login and then we want to have is authenticated, I guess still. And where was it? Oh, we're in the login component. That's why should we. I guess we should pass the use auth into the login so it can be used in the.
01:08:46 - Dom Saadi
This still I think will work. So like when it re renders it'll just like not render this component anymore because the. Yeah, yeah.
01:08:56 - Nick Taylor
So we could do this like if is authenticated. Oh, I see what you mean. It's used over there. Okay, so we don't even need to check it is authenticated here.
01:09:06 - Dom Saadi
Yeah, because the awesome page will know better.
01:09:11 - Nick Taylor
Okay, so now we've got our login, so I guess we need to come back to auth ts.
01:09:17 - Dom Saadi
Yeah, we just have to implement this function now.
01:09:20 - Nick Taylor
Okay, there we go. Okay, so this is where we do the fetch, right?
01:09:27 - Dom Saadi
Right? Yeah.
01:09:28 - Nick Taylor
Okay, so this will need to be async. At this point I want to do... wow, not Netlify, but at this point we can do API auth.
01:09:46 - Dom Saadi
We actually follow the Netlify way by default. Instead of Netlify, it would be Redwood Functions.
01:09:56 - Nick Taylor
Functions and then API.
01:09:59 - Dom Saadi
Auth, or no API, but just the auth, and then I think that should work.
01:10:05 - Nick Taylor
Okay. And then we need to pass the payload. Okay, so in here, yeah, we're posting it. That looks pretty good. Thank you, Copilot, for the sake of time. Okay, so that's going to get us the response, and then after that we're going to do const token equals await response.json(), I guess. Yeah, that's not capitalized. Did I not call it response? Oh, it's because I'm out of the function. It'd help if I put it up. Okay. All right, cool. And that's good. I feel like we have an extra bracket.
01:11:04 - Dom Saadi
I don't know why Yeah, I don't know why. It's not like shifting it one way to the right.
01:11:09 - Nick Taylor
Yeah, wait, there's one. One, two. One, two. Login. It looks good. I don't know what's going on there. We'll find out shortly. Do you see anything missing there, Andy? Why am I calling you Andy today, Anthony? It's weird that it's showing, because if I cut this out, it should complain. It's still not complaining. Oh, it's the client up there. Okay, that's there, and then it should be one outside of here, I think.
01:11:57 - Dom Saadi
I think you're good now. We just had an extra one, it looked like. Is that.
01:12:01 - Nick Taylor
Yeah, okay. Yeah, that's good. All right, so we got the token, and then at that point, we'll set that into local storage, I guess.
01:12:12 - Dom Saadi
Yeah, that works.
01:12:16 - Nick Taylor
localStorage. I'll just say token, token.
01:12:24 - Dom Saadi
Then one more thing here is we have to change getUserMetadata to look for that token, and then if it doesn't find it... let me double-check what it should return. It should just return null, nothing.
01:12:43 - Nick Taylor
Okay, well, in that case, if there's nothing in local storage for the key, it returns null. So we can just always return it.
01:12:53 - Dom Saadi
True. Yeah, perfect.
01:12:55 - Nick Taylor
Okay, let's save that. That's the login to get user metadata. And I guess get token would do the same thing here.
01:13:09 - Dom Saadi
Yeah, we may not be able to. Or I think we could get away without doing that one just to see where we're at.
01:13:17 - Nick Taylor
Okay. All right, so let's run this again, I guess,
01:13:24 - Dom Saadi
and then let's see what happens.
01:13:26 - Nick Taylor
Yeah. All right, here we go. Go. Boom. It keeps opening in the other browser window, but that's okay here. Okay, let's see here. Let's open up Dev tools. Okay, so we're not authenticated.
01:13:52 - Dom Saadi
Yeah.
01:13:53 - Nick Taylor
All right, so nick@iamdeveloper.com, and then we'll put password.
01:14:01 - Dom Saadi
Yeah, that was one of the two. Okay. Passwords, right?
01:14:04 - Nick Taylor
Yeah. And I'll press Enter. Oh, I forgot to put a submit button. It's not doing the preventDefault. One sec. All right, let's do this again. Login. Okay. What did we get here? localStorage.set... oh, it's probably wrong. Oh, it's key. What is it?
01:14:43 - Dom Saadi
Do you just, like. I don't know.
01:14:47 - Nick Taylor
No, no, I know what it is.
01:14:49 - Dom Saadi
The index. Like, as if it's an object or.
01:14:53 - Nick Taylor
No, it's not that. It's setItem and getItem. It's been a minute.
01:15:00 - Dom Saadi
It'd be nice if they were just maps like regular js, but I don't know.
01:15:04 - Nick Taylor
Yeah. Okay, so let's do this again. nick@iamdeveloper.com, password. Okay, so we're authenticated. It worked. The error you're seeing there is from before. So it looks like it works.
01:15:25 - Dom Saadi
Yeah, I mean, if you put in one of the two valid passwords, you're logged in. So that's our custom auth. Ship it. I agree. Gooseman.
01:15:33 - Nick Taylor
Yeah, yeah, yeah, exactly. Okay, cool. And we can do a test here just to show folks. So if we come to application, I'm going to clear the storage. So we get rid of our local storage. We should get the login page again. There we go. And whoops, I refreshed twice. Nick at iamdeveloper.com, and if I put in an invalid password like asdf or whatever, I should get a 401.
01:16:02 - Dom Saadi
A few bugs.
01:16:04 - Nick Taylor
Yeah, we got an unauthorized.
01:16:06 - Dom Saadi
Oh, we did. So what we said. Oh, we set the error as the token. It's pretty funny.
01:16:12 - Nick Taylor
Oh, yeah, yeah. So here, if we come into localStorage, yeah, there's our token. It's object Object, but okay. Yeah, so we should just throw, I guess, and handle it differently.
01:16:29 - Dom Saadi
Yeah. This has actually been way more fun than I thought. I thought custom auth would always just be a pain, but it's interesting to hook into the Redwood flow and see how it works.
01:16:46 - Nick Taylor
Yeah, for sure. What we could do here is return null, and then that should set the token to null. Come back just to see for fun. All right, let's clear this out, refresh, and then let's log in again. Nick at iamdeveloper.com, and let's do an invalid password. It says it's authenticating. Okay, there's something there, but the auth works. Again, not production-grade, but it's showing the custom auth. Cool. I know we dug into that for a while.
01:17:36 - Dom Saadi
In this case, if you were like, "I want to use Keycloak or Cognito," this is exactly what you would do. You would just map Cognito's login function to the login function. So, okay, hopefully that makes a bit more sense now.
01:17:51 - Nick Taylor
Yeah, the nice thing about this is we went through that whole flow, and I don't know your thoughts on this too, Anthony, but I think it's nice to show we did it custom and it took a while. If we had an out-of-the-box provider to hook in, we could do what you're saying: map login, map this, map that, and boom, you're ready to go. So even though it was a journey, I think it's good to show that it probably makes sense not to do something custom if you don't have to.
01:18:27 - Dom Saadi
and you never have to. Yeah.
01:18:29 - Anthony Campolo
Yeah, that was great.
01:18:31 - Nick Taylor
I know we've got about 10 minutes here. I could go maybe a little longer if you folks wanted to. But we touched on the new custom auth stuff. Can you kind of just touch briefly, high-level again, on what the other new things were? We have the experimental Vite support. I don't know if that's something you want to look at in 10 or 20 minutes or not.
01:18:54 - Dom Saadi
Oh, that'll take like one minute, actually, if you're...
01:18:57 - Nick Taylor
Okay, well, let's do that.
01:19:00 - Dom Saadi
Yeah, yeah, this would have been way easier. So you can just stop the dev server and run yarn redwood setup vite. Okay, so just like we set up auth, setup commands are kind of how you configure Redwood on demand. And in this case you're like, "I want Vite set up, even if it's experimental, give it to me." So just, yeah, hit that and Redwood will do all the config for you.
01:19:27 - Nick Taylor
Well, first off, that's super awesome because having worked in webpack before and like undoing webpack stuff to move something else, this is nice that this is automated. Again, I know it's experimental, but continue. Yes.
01:19:45 - Dom Saadi
This new Vite package has all that Vite config you alluded to under wraps, but it's there so that if you wanted to extend it, you can, because that's what you need to do sometimes. Now you should just be able to start the dev server, but there are a few new files.
01:20:04 - Nick Taylor
Yeah. So just take a peek here. Well, these are all the file changes I made, but okay, so there's a Vite config.
01:20:12 - Dom Saadi
Yeah, there you go.
01:20:14 - Nick Taylor
Okay, so if we close that and then here. Okay. So minimal Vite config, which is nice. You're using a Redwood plugin, so nice, clean architecture in terms of the bundling and tooling here. And that migration was pretty painless, I'm assuming. Now, once we run it, if we go back to yarn redwood dev, it's going to load up a lot faster.
01:20:43 - Dom Saadi
Usually the website loads up so fast that the API side hasn't started and there's a bit of a but. Yeah.
01:20:54 - Nick Taylor
Okay. Yeah. So basically, I know it's experimental, but just to show folks we did that quick migration. If we come back here, I'll just stop it. We just ran yarn redwood, set up vite and just said yes to the experimental. And I was up and running like you said a minute later. So I think that's pretty cool. Do you have a lot of folks trying out the vite now? I know it's experimental, but, um, I
01:21:21 - Dom Saadi
I think we have... we try to, like, in the Discord, we have a little bit of, "Hey, this doesn't work. Can you fix it?" So we know that way, if there are bugs, people are using it. But I don't know if we're seeing mass adoption of it or not yet.
01:21:36 - Nick Taylor
Okay, yeah, but Vite is pretty amazing. Like you were alluding to with the ViteConf talks Anthony was mentioning, there are a lot of talks about just how awesome Vite is. It's obviously a Vite conference, but so many people are leveraging it in really cool ways. And I just wanted to reiterate what we were talking about before, where I know there's no SSR, server-side rendering support yet, but getting Vite into here is what will enable the team to do that. So that's definitely pretty big. And I think, I mean, the project is already super awesome, but once you land SSR support, it's a full-blown package. You've got Prisma, so you've got your ORM and database integrations, GraphQL, the concept of cells, the CLI with all the generators, Storybook integration. It's really a one-stop shop. People always talk about opinionated stacks, but when you don't have opinions, and this is not knocking React, but when React first came out, it was just like, "Oh, use whatever state management library you want to use," or use whatever for this.
01:22:59 - Nick Taylor
And, you know, that is good, but it's more mental load on folks. So having tried-and-true, tested stuff that is opinionated, sure, there are some trade-offs with that, but at the end of the day, the goal is you want to be able to ship something. You're trying to produce value, and it sounds like the way that Redwood's bundled all this together, it's just done that.
01:23:27 - Dom Saadi
Yeah, thank you. And yeah, that's definitely the value proposition we want to deliver on. So I'm glad you felt that a little bit with the auth stuff, because it can be painful if you try to do it all on your own.
01:23:41 - Nick Taylor
Yeah, we got about four minutes here. So we talked about the Vite integration. We went through the new Auth API, which allows you to integrate anything, including our super-awesome custom auth that we have here. But it's escaping me what the third thing was that you had mentioned before.
01:24:05 - Dom Saadi
I mentioned GraphQL security.
01:24:07 - Nick Taylor
Okay.
01:24:07 - Dom Saadi
Yeah, yeah, we're a bit bad at marketing because it sounds like we weren't secure before, but like, no, it's just like we have even more security. So it's like, I don't know. Improved GraphQL security. I don't know how quite to say it, but okay.
01:24:24 - Nick Taylor
And I guess I feel like I'm one of the few people that hasn't really used GraphQL that much. I've used the GitHub API, and my experience prior to that was my blog when I was using Gatsby back in like 2017, 2018. So what does it mean that the security is improved? I know security is an important thing, but can you speak to what kind of improvements have been done there? Or, if not, we can point to some docs.
01:25:00 - Dom Saadi
So we have security in the sense of, like, are you logged in? Then you can't do this. That's always been there, no question. It's more that GraphQL has a bunch of ways you could kind of load the server if you're trying to do bad things. You could nest queries really deep. GraphQL is a graph, which means you could do, like, "I want the author, and then I want the post, and then I want the author and the post," and you could just kind of keep going, theoretically. But Redwood is not gonna let you nest infinitely. It stops at like five, for instance, and then we're just canceling that. You can configure that if you want it to be more deeply nested. But if someone was allowed to do that, the resolvers would just keep calling each other over and over again. And even if they're authenticated or whatnot, if it's a public API, it might not matter.
01:25:59 - Dom Saadi
So we have security in that sense. There's a maximum number of tokens you can put in the GraphQL document. So you could imagine if that's not there, and it's like, "I want this giant query," and it's 3,000 lines or something of everything in your graph...
01:26:20 - Nick Taylor
Okay, gotcha.
01:26:21 - Dom Saadi
It might hang up your server.
01:26:23 - Nick Taylor
Yeah. And just to throw this question at you, Anthony, because you used to work at StepZen and you definitely have way more GraphQL experience than I do, is that something typical that you've seen people do? From my high-level perspective, GraphQL kind of seemed to be marketed to front ends as like, this is the mecca, you'll never have to worry about databases and stuff again, you can get literally anything you want. Have you seen people just try to jam the whole kitchen sink into GraphQL and just get it all back, or what have you seen typically?
01:27:00 - Anthony Campolo
Yeah. I mean, the auth thing, there's always been a lot of different schools of thought in terms of how to do it. It would really depend on what server you're using and what conventions they have. So with Apollo, they would have their own way of getting the context, kind of similar to what we're doing. And then there have been libraries built, like GraphQL Shield and stuff like that. So I think it's been a thing that everyone's had to figure out on their own. There hasn't really been a blessed way to do it from the GraphQL spec, so that's why it's always been a bit of a challenge.
01:27:40 - Nick Taylor
Gotcha. Okay, cool, cool. Well, I gotta say, honestly, I feel like we've only been talking 30 minutes, and it's like an hour and a half has gone by. But this was super cool. I'm still not super well-versed in Redwood, but getting back to the point of what I was saying before, how it just seems to make you productive, like the CLI, I don't know if you market the CLI a lot, but it's a pretty powerful tool to get bootstrapped really quickly. And aside from generating pages or routes, sorry, API routes, stuff like setting up Vite...
01:28:23 - Anthony Campolo
[unclear]
01:28:23 - Nick Taylor
We're pretty much at time. The last thing I guess I would ask is, this is not so much related to the updates, but you have the Yarn... so you have the Redwood CLI. Can people extend it? Like, say if I'm a company and I'm using Redwood, and I'm like, "I want to create a custom generator." I still need to bootstrap a page, but maybe I need to do a few other things. Is that something that you can do out of the box, provided you implement that custom generator?
01:28:51 - Dom Saadi
So right now, no. We've thought about plugins though, and it's not as hard as we thought. Haven't shipped any form of it. But yeah, you can change the existing generator templates. So, like when you generate a page, it's like there's like nothing to it. It's like I rendered myself like, this is my name, that's the page. But if you wanted it to be a bit more fleshed out, you could do that. Like, you could change the template.
01:29:19 - Nick Taylor
Cool, cool. No, that's awesome. Well, we're pretty much at time. I just want to thank both of you. And Anthony, apologies for the audio issue I had with you at the beginning. It's still not resolved. I did a hack to get it working so we could at least talk, but I'm going to have to sort that out. But no, this was super cool. Thanks again for all this info on the new v4, the Vite support. If you're using Redwood, check that out, because you'll definitely get a faster dev environment going, and faster builds too, I imagine. I'm just gonna drop a link to Redwood again and to each of yours. I know you're not really on socials, but I'll just drop the links where people can find you. Dom and Anthony, I dropped your Twitter because people can go there and find every place you're at, basically. Before we go, I don't know if you have anything you want to end off with, but if not, totally cool, just calling it here.
01:30:22 - Dom Saadi
No. Thanks for having me, Nick. Yeah, it was a lot of fun to pair. Glad you had a good time.
01:30:29 - Nick Taylor
Yeah, yeah, no, it's always fun. I wouldn't call it the hot seat, but it's fun doing this stuff, at least for me. But yeah, this was super fun for me too. And yeah, I'd love to have you on again at some point, and we can maybe dig into something else another time. And same for you, Anthony. Yeah, next week... I always forget who's on, but there's definitely people next week. Check out the schedule. I'll be dropping it on Twitter and socials later. But thanks again, everybody, for tuning in. And if you haven't yet, check out Redwood, redwoodjs.com, and we'll see you all next week.