skip to content

Let's Learn RedwoodJS!

If we want to use the Jamstack, but also we need a database, but also you just want to build things and not waste all your energy configuring them? Anthony Campolo will teach us how Redwoodjs can help!

Full Transcript

Click to expand the full transcript

Captions provided by White Coat Captioning ( Communication Access Realtime Translation (CART) is provided in order to facilitate communication accessibility and may not be a totally verbatim record of the proceedings.

JASON: Hello, everybody! Welcome to another episode of Learn with Jason. Sorry for the technical difficulties, but we're here and ready. With me today is Anthony Campolo. Thank you so much for taking the time to join us today.

ANTHONY: Hey. I am so excited to be here. Thank you so much for having me. I'm someone who like am a budding developer, advocate developer, educator myself. Your work has been such a huge inspiration to me. So it's really great to be here.

JASON: Thank you so much. That's my warm fuzzy for the day. So now that we're done battling the computers and we are live, ready, talking to everybody, I'm going to send a tweet I meant to send out a while ago. Let's see. Might just be the trick. Twitch.TV. So my computer just rebelled and wouldn't cooperate at all. It wouldn't stream. It was just yelling at me. It was being rude. It hurt my feelings. But we are up and running now, and I'm just plain thrilled about it. AJC web dev, which y'all should be following.

ANTHONY: That's the one.

JASON: Okay, tweeting. It has been twote. Chat, thank you for hanging out with us. Appreciate y'all spending time. So Anthony, for those of us who are not familiar with your work, as you said you are a budding dev advocate, what's your background? What have you been up to?

ANTHONY: Yeah, I'm someone who came through the nontraditional background. I was originally a music teacher. That's partly why I resonated with your story, you know, someone who came up in touring bands and worked on your band's website and all that stuff. It was like, yeah. I did all that. I did cross country, like a 22-state tour back in the day. I did that whole life. Then I spent a couple years running a performing arts summer camp. So it was a camp that I went to myself as a camper, and it started as like a theater camp and they added rock band and film and all these other things. So it was like a general performing arts summer camp. That was really incredible. I learned a lot about just operations and logistics and admin work and how to run events. It was great, but it wasn't like really what I wanted to do for the rest of my life. So I eventually found my way to web development type stuff. I originally was interested in machine learning and was learning Python and Tenser Flow and that kind of stuff, which is cool. And I'm still really passionate about it. But it was hard to really make progress there, whereas web dev, you can just start building stuff, like throw up an HTML page and see the results. A lot of people have talked about the virtues of that feedback loop. I eventually joined Lambda school, studying full stack web development. I'm still currently in that program. I'm going through the computer science unit, which is like the final unit after all the web stuff. So I'm getting near the end of that. Really passionate about RedwoodJS and kind of here to talk about that.

JASON: Absolutely, yeah. So you mentioned before we started -- hey, Chris. What's up? Thank you for the raid. Hello, everyone.

ANTHONY: Good person to raid.

JASON: Yeah, I see -- I know Chris was doing advent of code. If you haven't heard of advent of code, it is -- oh, thank you, Chris, for the sub as well. Advent of code is a challenge in the month of December with some code puzzles that'll help you kind of sharpen up. It's especially useful if you are trying to break into tech. Coding interviews can sometimes be like algorithm challenges or things like that, and these questions are similar to that. It'll help you think in that mode. So if you want to give that a shot, it is advent of code. That's the one. So feel free to hit that. It's a good time. But yeah, thanks, Chris, for joining. Thank you, everybody, for coming along for that raid. You had mentioned before we started that it can take a second to get a project with Redwood initialized. So instead of doing what I usually do, which is talk about Redwood and then initialize a project, let's initialize then we'll talk about it. So can you walk me through what my first step is, if I want to create a new Redwood project?

ANTHONY: Absolutely. Do you have yarn? I'm assuming you do.

JASON: Yeah, I do.

ANTHONY: Great. So the first thing will be yarn, space, create, space, and then Redwood-app.

JASON: Like that?

ANTHONY: Then space, and then dot forward slash, and you'll give the name of your project. You can call it whatever you want to call the project.

JASON: Let's call this one -- what did I name this episode? Let's learn RedwoodJS. Okay.

ANTHONY: So this will do the whole generation of the project. Usually it takes a couple minutes because there's quite a lot to do. It also installs your dependencies.

JASON: So maybe what we can do to start is talk a little bit about what RedwoodJS is. So I've heard the name, but I'll be honest. This is the first thing that I've ever done with RedwoodJS. So I'm brand new to this as a technology. So what is it at kind of the high level?

ANTHONY: Yeah, so we say that RedwoodJS is taking full stack to the Jamstack. The longer version of that is it's a full stack serverless framework for the Jamstack. So there's a lot of buzzwords there. Really, what I want to get across is that it's a project with your front end, which is a create react app, and also a back end, which is like lambda functions, GraphQL type back end stuff. It's about taking all these pieces of modern web development and the way people are wiring up their own kind of full stack projects and formalizing that with conventions.

JASON: Okay. And one way that I've heard this described -- oh, that went so fast. Okay. I am installed. Wow, okay. I was expecting that to take way longer.

ANTHONY: 66 seconds.

JASON: All right. So now that we're in here, I'm just going to open this up. Let's open it up over here, actually. There we go.

ANTHONY: Yeah, so this is great. I find actually looking at a project is much easier than trying to talk about it theoretically.

JASON: Yeah, one second. I got to get init in here so it doesn't dim everything. How about now?

ANTHONY: It will probably be deploying this also.

JASON: Much better. Okay. So now, we've got a project. I'm trying some new VS code settings that I saw mentioned by, I think Birk Hollands, then Nicky pointed me toward this, having the file on the right. Still getting used to, but I think I like it.

ANTHONY: It's very interesting, yeah.

JASON: So now, if I look in here, I can see I've got an API folder, a web folder. Let's look at the top level here. We've got our Redwood.toml. We've got a read me, always great. But today you are my read me. We have GraphQL config, babel config. I assume we're going to have to create a database, right? Like it doesn't do that for us.

ANTHONY: Yeah, so the database question is an interesting one. The way it's set up and the way we'll probably do it today is you create a Heroku database and grab the environment variable and throw it into Netlify where your front end is deployed.

JASON: Got it, cool. So where should we start if we want to kind of pick this apart? Because what you said -- like, it lands with me because I think that Jamstack has so much potential. I think that there is a general perception at first at least that when you build a Jamstack site, there are severe limitations on what you can do with it. But I think as you dig further into that, that becomes less and less true. You can kind of do whatever you want with Jamstack sites because static assets don't necessarily mean static experiences. And so what Redwood is promising then is to take a lot of the question marks out because getting from static assets to a highly dynamic experience with databases and serverless functions and all these things, there are some decisions to be made. You know, serverless functions, that's a whole mental model you have to build. Databases, you got to choose a service, choose a database flavor, you got to figure out how to configure that, decide how you're going to secure it and lock it down. All of those things are -- that's cognitive overhead. So what you're saying is that Redwood is trying to eliminate a lot of that decision making process by giving us a ready-made, like, this is an app-building framework that has made the decisions to give you a good shot of doing this the right way. Is that a safe assumption of how to frame that?

ANTHONY: Yeah, that's a great way to put it. If you look historically, we had really strong conventions around full-stack development with things like Ruby on Rails and single-page JavaScript applications have become more of the main thing. We've gone away from a lot of those conventions, and this is trying to take everything we've learned over the last five years or so with things like React and GraphQL and these new modern web paradigms and stitch those back together into conventions that make sense and that eliminate a lot of the decision fatigue and choices that go along with just how you set up your file structure, how you connect your front end to your back end, how everything speaks together. So yeah, and it's trying to do that in a Jamstack paradigm by taking what we all like about the Jamstack and the benefits of the Jamstack in terms of performance, reliability, and security and extend that to the entire stack.

JASON: Absolutely. Okay. That makes sense. So I have a brand new project. If I want to get this up and running, I assume this is -- is this already ready to serve? Can I just start it?

ANTHONY: Yep, so the first way you would get it going is the main command to start your dev server is yarn, space, rw, which is the alias for Redwood. You can either type Redwood or just rw. Then space and dev.

JASON: Okay.

ANTHONY: So this is going to give us our main splash page. From there, we'll just start generating pages, and that's kind of the first thing that we can get into. I find that just start building out the project incrementally, you learn the different pieces and how they all fit together. If you try to explain the whole thing, there's an architectural diagram. If you go to the homepage real quick, like the RedwoodJS homepage, and scroll down, keep scrolling, yeah, that thing right there.

JASON: Here.

ANTHONY: Yeah, that architectural diagram, that's a picture of the whole project. I find that if you're someone who likes architectural diagrams, that may be really useful, but it's a lot to take in. It didn't really make sense to me until I had already built the whole thing. Then I went back and looked at that picture like, now it makes sense.

JASON: Yeah, yeah. And I mean, what's always interesting about these is this is just about any app is going to look pretty similar to this, whether it's built on Jamstack, on a traditional lamp stack. You have your web bits. You have to deliver mark-up, CSS, and JavaScript to the browser. Then you have your kind of like API or dynamic layer where you're sending requests back to some kind of a server. So in this architecture, and that's the front end/back end split. I like this dotted line to signify that.

ANTHONY: Yeah, I usually tell people start with the left side and that's the actual directory structure, your web folder and API folder. Those are all contained in the mono repo.

JASON: So this all makes sense. I'm excited to see how it actually works, but in the abstract, I get where we're going. Now that I've got this site running, it's over here. We can see we've got Redwood Router. So that's a thing we're going to have to learn. But this looks pretty similar to like React Router or Reach Router if you've used those. So that's good. It feels familiar. So I guess I saw it set up, it said, a prisma client. Does that mean a database or an empty client?

ANTHONY: So right now it's kind of initialize and set up to where you could get your database going really quickly. We haven't quite gotten to that point yet. We'll start off kind of in the front end generating some pages and routing those together. Then we'll get into the Prisma database type stuff. But out of the box, everything is very much ready to go in terms of it's already wired up for development. Then you just have to change a single variable in one of the config file. But this kind of is getting ahead of where we are right now.

JASON: Sure, okay.

ANTHONY: So what we should do is keep your terminal running. Then we should get another terminal open so we can run commands to generate pages and stuff like that.

JASON: Cool. So I'll just keep this one running in the background. We'll use the VS code one for everything else. So I'm ready.

ANTHONY: Cool. So the first one, everything is going to have basically yarn, Redwood, or RW will be the first starting commands. For this one, it's either G or generate. Then page and then space and then all lower case home. Then space and a forward slash. So what this is going to do is this is going to create a homepage for us. It's going to create a route to our main homepage. That's what the slash is for. That's what's going to generate the route, and the home is so it's going to name -- it's going to be like homepage, the name of our component, and it's going to be in a folder called homepage and generate a whole bunch of stuff. So that's the command. So go ahead and run that.

JASON: So to just repeat this back and make sure I get it, we're using yarn so that we don't have to globally install the Redwood CLI.

ANTHONY: Correct.

JASON: And that's because it's installed in our package JSON down here. So when we have Redwood, then we can run this Redwood command. G is for generate. Page is the type of thing we're generating. Home is the name of it, and this is the path, like what we want it to be from the website URL.

ANTHONY: Yep, you got it.

JASON: Got it. Makes sense. All right. So I'm running this command.

ANTHONY: It's also going to generate a couple other things as well. It's going to generate a storybook file and a test file. So it's set up in a way where you can run your storybook automatically. You don't have to configure storybook at all. You don't have to like set up just test. It gives you -- like it sets you up for TDD out of the box. This kind of goes along with the rails influence. They're trying to bring some of the conventions around testing as well into these JavaScript frameworks.

JASON: Got you. Okay. All right. I'm with you. Now that we've created this, we've got a homepage. So I'm going to open this up.

ANTHONY: Then if you go back to where our project is running, it automatically jumped to that. So it gets rid of the splash page. Now this is our homepage. This is our home route. We'll have a routes folder. So this is the actual page we're looking at. If you look at the routes file, yeah, this is your whole route. Part of why Tom built their own router is because he wanted a flat routing structure. He didn't want nested routes. So your routing here is essentially every time you generate a page, it automatically sets up your route. This is kind of like one of the big things you get in contrast to something like Next, which also has zone router. So a lot of these frameworks, like the routing layer is a really big piece.

JASON: Gotcha. Okay. So I'm seeing in this some echoes of like the angular style of setting things up where what this does is we're using the CLI because there are connections throughout the file. So when you create a page, you have to create a homepage. Then you also have to update the routes and potentially some other things as well. So the CLI is kind of making sure that everyone does everything the same way, and that's what you mean by convention over configuration. We know that things are predictable now because in order to work, it has to be done according to this convention.

ANTHONY: Yeah, and to answer the question in the chat, if you're generating 500 pages of your blog, when we do the scaffold command, each blog post gets its own page based on an autogenerated ID. So that's kind of the way that works. But honestly, the blog format is kind of like a good way to understand a Redwood project, but it's not really the sweet spot for Redwood. It's more about creating like data-intensive web applications with multiple clients and less so about generating tons and tons of pages. But that's something you can kind of do with it. So, yeah.

JASON: Sure, okay. Yeah, so now that I've got this homepage, we're set up here. That is cool. It's already got a link, which is also nice. I do like that the examples kind of get done in place. I think that's a good way to -- yeah, this makes me happy to see that sort of stuff. But yeah. Hey, what's up, Payton? Thank you for the host. Then what? What should we do next?

ANTHONY: What I would usually do here is just kind of edit this to make it a little clean. Like give your own title. You can actually take out the link because what we're going to do is create a layout that's going to wrap our pages. That's going to have our links for us in a nav bar.

JASON: Okay.

ANTHONY: Yeah. And then the next thing we would do is we would basically do that previous command again but now with about instead of home. You don't have to specify a path at all because it's going to create a path to about, and it's going to create an about page. It's going to do both of those.

JASON: Gotcha, okay. Doing it.

ANTHONY: It will also generate the same kind of extra files and all that.

JASON: Okay. So now if I go --

ANTHONY: You'll just want to type it in.

JASON: All right. That's intuitive. I get it.

ANTHONY: So this is a lot of the stuff you get for something like Next, where you get very simple page-based routing. You're just creating pages. Your pages are your routes. This, to me, is like a no-brainer. But now what we're going to do is do the yarn Redwood generate command, but instead of generating a page, we're going to generate a layout.

JASON: Okay.

ANTHONY: And then call the layout just blog, all lower case.

JASON: Okay. And I'm assuming that's going to show up in my layouts.

ANTHONY: You got it.

JASON: Yep, blog layout.

ANTHONY: And here, this will be a bit of typing. What we want to do is we want to import. So go up above your component and import. It's going to be link with an upper case L. You got it. Then comma and routes, lower case. That's going to be from at RedwoodJS, slash router.

JASON: Okay. And then here I'm going to -- let's do one of these, and we'll set this up with just some basic semantics, right. We'll make this our main element. Then up above it, we can do like a header, and we'll start -- I guess we can just do like nav, and for each of these -- oh, wait. That's not what we want. We want the link. So remind me what the syntax is here.

ANTHONY: So it will be to equals, then brackets.

JASON: Like this?

ANTHONY: Yeah, brackets, not quotes.

JASON: Okay.

ANTHONY: Not quite. So bracket, not quotes. Inside the brackets, it's going to be routes dot home, then parentheses after.

JASON: Oh, interesting. So now I've got dynamic --

ANTHONY: It's a function. It's a named route function, is what we call them.

JASON: I like this, because now what that means is if I change the path, I don't have to find everywhere in my app that has that path. Interesting. Okay. I'm into that. Then I'm going to set about and about. And what does Redwood say about styles? Like, what's its opinion here?

ANTHONY: It's our unopinionated about styles. There's a tailwind generator to configure it quickly because people love tailwind. You can just do into your index.css file and style it naturally. That's what I do because I'm not a big CSS framework kind of guy.

JASON: So we can just set this up to be like -- okay. So we'll make this kind of obviously -- we should probably go with a darker color than that. Let's make it a dark gray. Then for our main, we will display block and max width, we'll go 90, width of 500. That'll be fine. So that's good enough. But we're not using it yet. So how do I actually use this layout?

ANTHONY: So what we want to do is go to the page you want to use it in. We'll put it into both of our pages. Then you're going to import it. So it's just a component. And it's import, blog layout -- blog and layout are both capitalized. Then from -- so you're not going to do any dashes or slashes. There's no like relative routing between paths. You're just going to do SRC, forward slash, layouts, forward slash blog layout. You give it the exact directory the thing you're importing is from. Then you're going to wrap it like that. And that should do it.

JASON: Okay. So we'll do the same thing here. All right. And aside from the fact that is atrocious -- let's fix that real quick. We'll go up here and do like a margin zero. We'll give this a color of white, and we'll give it some padding of like 2 rem. How about that. Oh, but I need to make my links. How about that? Okay. That looks useful. Then we also want to add a little bit of spacing around these. So let's go display flex and we'll do a gap of 1 rem. Beautiful.

ANTHONY: Nice. This is something that's still a huge gap in my knowledge, just being able to write CSS on the fly. We learned CSS for like a week in our bootcamp.

JASON: So now we've got like a pretty basic setup, right? I'm into it.

ANTHONY: It's killer, right?

JASON: I'm going to do one more thing, just to give us some -- have a shortcut to make that happen. Now we have the default system fonts.

ANTHONY: Just laughing at the chat. Someone is like, wait, how is the file on the right? (Laughter)

JASON: I know, I'm sorry. I upset everyone. There's a really good reason for this, actually. I'll show you why. I like to toggle my files open and closed, and it doesn't bounce the code around anymore. So this is actually enough of a reason for me to do it.

ANTHONY: That makes a lot of sense. That makes a ton of sense. I'm constantly opening and closing my thing on the left to try and get more space. Yeah, that makes a ton of sense.

JASON: So it's just those little things. I didn't even realize that my eyes were tracking that left and right until I wasn't doing it anymore. I was like, oh, that's pleasant. I like that.

ANTHONY: Cool. So what we've done so far is we have generated our pages. We created a layout. We've routed them all together. This is really good stuff that you get from a front-end framework, like Gatsby or Next in terms of having a sensible way to organize your front end. Here's where the really interesting stuff starting happening. Now we're going to get into Prisma. We're going to want to go into our API folder now instead of our web folder. This is now our back end. We're going to have a DB folder. In that we have schema.prisma. Have you used Prisma before?

JASON: I have used Prisma, I think it may have been the previous version.

ANTHONY: Prisma One. Yeah, so Prisma One and Prisma Two are different. Graph cool was originally a back end as a service, and Prisma One was a GraphQL server that also would talk to a database. And Prisma two, the one we're using now, is an agnostic tool towards GraphQL or API. It is simply an ORM query builder.

JASON: Have they steered into ORM?

ANTHONY: Now they're calling it an ORM. They finally caved.

JASON: I'm glad because that's definitely what it is. (Laughter)

ANTHONY: Yeah, it's mapping your objects to relations. It's certainly not no RM, though.

JASON: Yeah, if you're not familiar with what an ORM is, it's just a tool that does this. So, yeah. And actually, let's pull up Prisma. It's, right? Yeah, so this is Prisma. They're bundling Prisma as a tool. So Redwood -- and I think this is worth just calling out again. I think we talked about it a little bit. But Redwood is not new technology. Redwood is an assembly of existing technology to help you make good choices quickly. I guess that's not entirely true because the router is new. But largely, it's an assembly of existing technology -- React, Prisma, things like that.

ANTHONY: Yeah, yeah. It's about taking all the best options that we have available. So they looked at a lot of different clients. Like they've looked at -- so they kind of went through the process of vetting all of the kind of React ecosystem and coming up with their own kind of conventions around it. This is what's interesting. There's other frameworks like Blitz or Bison that are creating full-stack frameworks around similar tool, but they may use React query instead of Apollo. So there's a lot of different assemblages of these tools because it's been such an explosion of all this stuff. There's a lot of really great, amazing technology, but it can be really hard to leverage it, especially for someone like me who's fairly new and having to learn GraphQL and AWS lambdas and some ORM tool. Having to learn all those and put them together yourself can just be daunting. You'll never get anywhere. So they're trying to give you the power of all these modern technologies in a way that's more accessible.

JASON: For sure. Okay. So now, I went ahead and installed the Prisma VS code extension so this file would get syntax highlighting. It's easier to spot now. Up here we're defining a data source. Then we have a generator. And then we have a model. Now, I can kind of figure out what this is. I'm assuming that the generator is the tool that we're going to use to build out like how we interact with the database. Then the model, I'm making an assumption, looks like very similar to a GraphQL schema definition, but I can see it's not exactly the same. There's some magic in here.

ANTHONY: Yep, you pretty much got it. So like you said, it's a model. You're going to be defining essentially your fields and then what the type of your field is. Then you can do some other stuff. Like you have autoincrementing ID there. You can set constraints like unique. This is why we say it's an ORM. It's allowing you to just create this JavaScript object. So that's why it says Prisma client JS because you can create different clients in different languages, and that's like the thing that's translating your objects into your sequel. So we're going to edit this and we can look at it. But I want to answer this question in the chat. RedwoodJS forward view, if it's a thing, if it will be a thing. I don't think it is yet a thing. I absolutely think it will be a thing. I think if you look at the progression of these different libraries and frameworks, we have frameworks and meta frameworks. Next is four years old whereas Blitz is like eight years old but building on top of Next. So I think there's going to be something like Blitz in view to build on top of it. It's like bam, you're halfway there. So I think it's kind of a question of how motivated you are to assemble the pieces yourself and then once you've done that, people can start to agree on what's the best way to do that, and then that gets formalized into a framework. But it takes a long time.

JASON: Meta, meta, meta framework.

ANTHONY: It's been in development for like two years.

JASON: Yeah, and I feel like there are expansions and contractions of the ecosystem. For a long time, what we were seeing is that there were a lot of new JavaScript libraries focused on trying to create new functionality. We saw this huge spread. Now what I'm seeing is the libraries that are coming out are not so much about creating new technologies but about organizing the thing. So we're almost seeing a contraction. We're moving down to a set of standards. And I suspect that we've got a little ways left before the next expansion. It kind of seems to be like a pyramid and then an inverted pyramid where we expand and contract, expand and contract. We want to see what we can do. We get frustrated with the existing tools, so we build a bunch of new ways. Then we get frustrated because we can't decide what to use and come up with conventions. Then we get frustrated and start all over again. I kind of like this. This to me feels like curation. It's a really important part of building anything. So what Redwood is doing is it's curating over creating. For tools, I think that's the right way to go about it. Don't invent things if you don't have to, right? But yeah, so okay. To get off my soap box and get back to writing some code, what should we do next here? So I'm assuming we're going to mess with the model.

ANTHONY: Yes, we're going to rename it to just post instead of user example.

JASON: Post.

ANTHONY: Because that's what we're going to be creating. We're going to be creating a post. You can leave the top line exactly the way it is. Then change email to title. And get rid of the unique constraint. Then change name to body. Then get rid of the question mark, yeah. Add one line, and it's going to be created at, one word, with a capital A for at. And date time, capital D and T. Then at default, and then now with parentheses at the end.

JASON: Nice. And I don't know if anybody was paying attention, but it just auto suggested all of that. So if I were to do something like a slug and then I just hit control-space and it pulled up my options so I can see what's in here. Then I can come out here and hit command-space and it shows me my options here. So that's very cool. And this is one of those things that I think is just a benefit of -- I assume this is all written in type script. So we get a ton of benefits through the tooling that way.

ANTHONY: Yeah, it's amazing.

JASON: So now I've defined a post. We know this is going to be the ID or the primary key. We know that it's going to default to an auto incremented number. Perfect. The date time defaults to now. That makes sense. Title and body. I assume the question mark makes it optional, which is inverted from GraphQL where you need an exclamation mark to make it required. This looks like it's required by default. Is that correct?

ANTHONY: That's funny. I don't know the answer to that question. I don't know what the question mark does, but I think that is correct.

JASON: Yeah, I feel like I had a conversation with the Prisma team when they were doing this. That was a decision that they made, to make these fields required by default. Then if we wanted it to be optional, like an empty body, you can add a question mark. If I'm wrong, please correcting me, chat.

ANTHONY: Yeah, so this is interesting because Prisma is a huge chunk of Redwood, but you don't actually have to use Prisma. I've created projects with Fauna DB and you lose the ORMness you get from Prisma. But it's not tightly coupled. I don't think anyone would ever use rails without active record, but that's not the case here. Most people probably wouldn't use Redwood without Prisma, but I'm a weirdo, so I do.

JASON: And like Chef Brent is in the chat. He's saying, you know, I remember rails. That is kind of what this is, right? One of the things that people miss about JavaScript is the prescriptiveness and the done for you-ness of Rails. That's what made it such a popular development tool at the time. Ruby is great. Ruby on Rails is like you're done so quickly. So I've heard a lot of people independent of the Redwood community say they wish there was a Ruby on Rails for JavaScript. It sounds like Redwood is attempting to fill that gap.

ANTHONY: It is. So is Blitz as well. I think it's really interesting to see the way they go about it because Redwood is all in on GraphQL, whereas you don't use GraphQL equals in Blitz. It's called like a no-API layer. So you're doing RPC calls directly into your back end, which is more kind of a monolithic idea. So I would compare Blitz a little more to a Ruby on Rails, but Redwood is going for the same convention over configuration thing. I find it really interesting because it's also doing it in a way where it's trying to be Jamstacky and decoupled in a way where it's not as tightly integrated. So it's trying to give you the Rails experience without the same drawbacks that people find in Rails in terms of it being like it really tightly coupled monolith. So this is why there's this weird tension in terms of how Railsy it is versus it isn't. It's very interesting.

JASON: Okay. So, sorry. I'm going to fall into all these rabbit holes the whole time. In the interest of actually getting something working, we should probably keep moving. So I've got the post defined here. Then you had mentioned we're going to use something else, right?

ANTHONY: For now we're going to use SQL lite. You want to develop locally with your SQL Lite database. When you're ready to deploy, you can deploy to postgress. SQL Lite and postgress should be compatible. So that's the idea here. What we're going to do is enter a command. So these are going to be Prisma commands, although you're not really going to be able to tell. It seems like a Redwood command. But we're going to do yarn, RW, and then DB. Then save. So this is going to -- since we changed our schema.prisma, it's going to create a migration with the post model we just created.

JASON: Cool. So it prints out the whole data model. Then it says just created your migration. That's coming off of Prisma. Here's the migration.

ANTHONY: With another schema and a read me. The read me will show you all that the SQL generated.

JASON: Nice. Let's take a look at that, actually. Because that's very cool. So this -- oh, nice. Okay. So it's just showing us what's being done and what our changes are. Yeah, I like that a lot. And that's cool because this is the sort of stuff you want when you're doing database changes, but a lot of times people don't do. So having this kind of record of what happened and when and what the changes specifically were --

ANTHONY: Yeah, it eliminates a lot of room for error.

JASON: Absolutely. That's nice. Okay. So now that we've got this, are we done? Is it a blog?

ANTHONY: Almost. (Laughter) Very, very close. We're extremely close. We have two more commands. So yarn, Redwood, DB up, instead of save.


ANTHONY: Now that we generated the migration, this is going to apply the migration to our database. So that SQL we saw, those commands are being run. We have a database with tables and all that kind of stuff. The very last thing we need to do will be yarn, Redwood, generate scaffold. So same as before, like we generated pages and layouts. We're going to do scaffold and do post.

JASON: Post.

ANTHONY: So we're going to scaffold our post interface. And this is going to create a whole bunch of stuff. This is like the famous Rails scaffold command where kind of this is the blog.

JASON: Oh, it just did everything. Okay. So I'm closing this, going out here. We've got --

ANTHONY: Let's not look at any of this yet. Let's go into our browser.

JASON: Here and here.

ANTHONY: Then go to slash posts. And this should be fairly intuitive.

JASON: Hold up.

ANTHONY: Pretty cool, right?

JASON: I'm going to test it.


JASON: Wow! (Laughter)

ANTHONY: This is done with Tailwind.

JASON: That is slick. Okay. Then I'm reloading the page. It's still there.

ANTHONY: You can't really see because of how your CSS is set up, but you can scroll in that table to the right. So like inside the table.

JASON: Oh, yeah. Show, edit, delete. Show, there's my post. Okay.

ANTHONY: And your route right now, see how it's slash one?

JASON: Yeah. That is slick. That is really slick. Okay. Color me impressed. Now, a lot of decisions were made here. Like, I couldn't publish this, of course, because the chat would immediately troll the hell out of us by, you know, entering new posts. So this is all just the generated React component. So in here, we've got all of these kind of editing posts.

You hackers. You dirty hackers.

JASON: Exactly that, you hackers. Okay. So where should I look? I'm kind of poking around. Show me what I should actually be looking at.

ANTHONY: Yeah, this is a great question. I wrote an entire blog post explaining all this code. There's not really a good one place to look at as the access point.

JASON: Where do I find that blog post?

ANTHONY: So this is on -- well, that's kind of funny. I need to actually -- so I have this very long blog series about RedwoodJS called -- so just search "a first look at RedwoodJS."

Hello, brotas. Thank you for the host.

ANTHONY: That's the community form. This is kind of like in flux right now. I'm in the process of redoing this. So this thing is actually not really ready for prime time. It was like a month ago, but everything is in flux right now. It was part four. So if you look at part four, you can see I just explain each one. Like, this is this. Here's the code. Here's what it's doing. So this is why I say this isn't really touched on in the tutorial at all. It's just there, and you can poke around with it, if you want. What we want to do next is actually generate a cell that's going to do our data fetching for this. This is going to help us understand. Yeah, so we're going to do generate cell. Then it will be blog posts, capital B and capital P. Yeah, exactly. So this is going to generate our cell. So go ahead and run that command. This will be in our components folder and our web folder.

JASON: Okay.

ANTHONY: And then -- yeah, so what's happening here -- are you getting an error? You should be. Basically what happens now is depending how your tooling is set up, sometimes it will suggest what GraphQL query you want to be writing. So let's just look at the cell and let me explain what's going on here. So what's happening here is this is kind of Redwood concept that is declarative way to do your data fetching. So if you look at what's happening here, there's a query at the top. If you've ever written a GraphQL query before, that should look totally understandable. You're querying for your blog post, and you're asking for the ID of your blog posts. Then what happens is you have these four different states that your data can be in. It can either be loading, it can be empty, it can have an error, or it can be successful, in which case we're using JSON.stringify to render the blog post ID to the front page.

JASON: Right.

ANTHONY: So what we want to do now is where it says blog posts, the three places, we want to just change that to post, one word, all lower case.

JASON: Like that?


JASON: Okay.

ANTHONY: Then we're going to save that and go back to our homepage and import this blog post cell.

JASON: Okay. What's the --

ANTHONY: You don't need to do the brackets. You're just going to import that whole component. So blog posts cell is what it's called. Source components, blog post cell.

JASON: Wait a minute. How does that work? There is no default export here. So is Redwood doing some magic to bundle all that up?


JASON: Okay.

ANTHONY: It just works. All this just works.

JASON: Then do I just drop it straight in?

ANTHONY: Yep, self-closing tag.

JASON: Interesting. That's fascinating. Okay.

ANTHONY: I know it is. This is where it all comes together.

JASON: So that meant -- I'm not going to lie, that magic puts me a little on my back foot. Like, how do I -- I don't know how to debug that. I don't know how to like -- so you just have to know. It's going to run this query, these are the potential states it's going to hit, but it's going to build the component that shows the appropriate state?

ANTHONY: So, yeah. Basically, any time -- it is able to tell whether you're getting the error or whether you're getting a successful state, whether you have a 200 or 400 or anything like that. This is why this framework took two years to build.

JASON: I get you. I understand.

ANTHONY: It's the magic. It's just people spending the time to figure out how all this stuff works. But if you go to the query at top, so leave everything like that on the homepage, go back to your blog post cell and bring it all in. So add title, body, created at. Then let's just see what that changes.

JASON: I wonder where it's not pulling that in for me. Feels like it should. I probably just need to configure something. But regardless, boom.

ANTHONY: Now we're getting everything. And what we can now do is we can style this -- well, not necessarily style, but we can turn it actually into a component. Yeah, exactly. So where it's returning, you can spit out like the article key equals, then post.title, post.body, and just do them all. Do all that.

JASON: So we'll do one of these. Then we'll get like a key equals And we'll do a link, which means I need to import that, but it'll auto import it for me. Good, good. Oh, here's a question. How do I link to a post? Because it's not going to work with that routes thing.

ANTHONY: Yeah, there's a way to do this. I don't know how to do it off hand, like right now. But there's definitely a very simple way to do this in terms of setting --

JASON: And I'm realizing right now we haven't built a post page. So how about we don't do that just yet. Instead, we'll make post dot title. That's just plain text, right? I'm going to set it at a paragraph. We'll do dangerously set inner HTML and make it the post.body. Down here we'll say created at post.created at. Did I break it? I broke it.

ANTHONY: Possibly. So try getting rid of the dangerously set inner HTML part. Just do like a post body, a regular post body. Try that.

JASON: Where's my error? No error.

ANTHONY: Let's see. It might be the way we're mapping over the posts. Can I shoot you some texts and you can copy/paste it?

JASON: Yeah, send it as a Twitter DM because we're between two computers. And while you're doing that, I just realized I forgot to shout out the sponsors today. Let's do that quickly. Today's show is being live captioned, as always, by White Coat Captioning. We have Rachel in helping us out today. You can see that at The captions are being provided by our sponsors, Netlify, Fauna, Sanity, and Auth0. Thank you to all of them for making this show accessible to more people. Make sure you check that out. Again, it is And we forgot to return this. No, we didn't.

ANTHONY: Yeah, so just --

JASON: Oh, I forgot to return this. Oh, my goodness.

ANTHONY: I knew we were making an error somewhere in the mapping.

JASON: Thank you, chat. Hey, look at it go. First try, right?

ANTHONY: Your viewers are so talented.

JASON: So now we've got -- yeah, we're good. We've got that running. And we go to post and create a new one.

Holy buckets. Did that just work?

JASON: Save that, go back home. There we go.

ANTHONY: So there's your blog.

JASON: That's nice. We've got a blog. So this is slick. We can see how quickly -- you know, setting this up with another service would have taken significantly longer than the one hour -- well, less than one hour given that we've been screwing around and I took ten minutes to get started. But you know, what, 45 minutes worth of work, and we have a fully functioning blog with a database and, you know, this doesn't -- not going to win design awards, but this is certainly functional, right. That's pretty impressive. I can see the appeal of this.

ANTHONY: Yep. So the next place that we could go, probably where we're at now with time, we won't build out the contact form, but if you follow the tutorial, it walks you through creating a contact form that is like name, email, and a message. You set some validation. If you open your thing all the way so you can see the side bar --

JASON: Oh, side bar. Got it.

ANTHONY: So check on the left. We've done everything up to side quest, I think.

JASON: Okay.

ANTHONY: So then you have routing params. That's how to link the different pages from your blog posts. Then everyone's favorite thing to build. We make lots of jokes about forms here. So this will walk you through how to create a contact form. We use React hook form as the form layer underneath because they decided it was the most form shaped of all the forms that were available. I have no idea why they picked React, but that's like you don't have to use it. You can just write straight HTML forms if you want to. But you get a lot of nice error validation and stuff like that out of the box if you're using React hook form.

JASON: That makes sense. Okay. Well, cool. So I think we've got about 30 minutes left, right?

ANTHONY: Yeah, that's plenty of time to deploy the front end and the back end.

JASON: Let's do it. I'm ready. So I'm assuming I need to stop this server. Now I have a bunch of code. Let's open it in VS code. It's easier to see that way. So here I have all of this, and I can't get my -- there we go. So this is everything here. I can just start kind of adding certain things. So there are things I know we need, like if I want to add my web folder, right?

ANTHONY: Hold on.


ANTHONY: So there's just a command that you need to enter that's going to set up all your deploy.

JASON: Come on. Don't, don't. Really?


JASON: All right, okay. All right, Redwood. Now what?

ANTHONY: Yarn, RedwoodJS, generate, deploy, Netlify. It's going to create a Netlify.toml. So check that out.

JASON: Let's go up here. Netlify.toml.

ANTHONY: So it gives you your command, publishes. Very standard kind of build you're going to do on Netlify.

JASON: Gotcha. Okay.

ANTHONY: So now you're going to want to commit this and get this into a repo. Your whole project, exactly the way it is right now.

JASON: Just all of it?

ANTHONY: Actually, there's one more thing. Let's just do this now. Go to your schema.prisma. This is where we're going to flip SQL Lite to postgres. That should be all the configuration we need to do. Bingo, bango. Now get that sucker up on a repo, get your Netlify connect. You'll deploy it, and you'll have all your build commands there. That should be fairly easy to do.

JASON: Okay.

ANTHONY: Asking, will this prevent people from going which route? There's a lot to talk about in terms of deployment. If we're asking about other ways to deploy, we can definitely get into that.

JASON: Going to the new post creation route. Oh, no. I don't think it will. I think when we deploy this, they'll troll the heck out of us.

ANTHONY: Well, we're going to set up an actual database that's going to have an environmental variable that we're going to link up to. So if we hide that, they can't troll us.

JASON: Okay. So I'm going to GitHub repo create, Learn With Jason. I switched over to the gh CLI as opposed to the hub. I'm still learning it. So I think this works. Let's find out.

ANTHONY: This is where the most potential for error comes in when I'm doing this stuff live.

JASON: Done. So now I can get push upstream origin main. Good, good. And that's the wrong tab. You're not helping. There we go. All right. So this is the code. That's up and running here. Now, it didn't -- it configured for Netlify deployment, but it did not deploy, correct?

ANTHONY: Correct. Now we need to go into our Netlify account and connect the repo.

JASON: Okay. So I can do that also. I'm going to Netlify init. And we want to create and configure a new site. I'll put it on my team, and we'll call this let's learn RedwoodJS.

ANTHONY: I always just go to their dashboard.

JASON: Now it's going to get overridden by everything, but I'm just going to go ahead and type that one in to make sure it doesn't explode on us. Okay. So now that we've got that, open in the right window. Always the wrong window. Over here. This is the one. So now we've got our site building, but we're missing that database URL. So this is going to fail.

ANTHONY: Yes. So it's not going to fail. It's going to generate our page and give us an error because our cell knows how to do error handling.

JASON: Okay, all right. I'll take that.

ANTHONY: So your front end is still going to work. It's just not going to render any posts. What we want to do now is do you have a Heroku account?

JASON: I do.

ANTHONY: So just provision a postgres database on the free tier.

JASON: Okay. Let's see how quickly I can get into Heroku. Let me pull this off screen. Actually, I think I have it logged in over here. Yes, I do. I'm just going to pull over this tab, and we're going to create a new app. Okay. And do I need anything else here?

ANTHONY: Now you're going to go to resources. This is all fine. You don't need to touch any of that. Then find more add-ons, on the right. Postgres, that's the one.

JASON: So we can set this up on free. Now, the limitation with the free Postgres is like 10,000 records. So for a lot of folks, that'll be enough. Depending on what you're planning to use this for. I know that because I just hit that limit on another account.

ANTHONY: Yeah, this is going along with that whole serverless idea. Everything starts on the free tier, and it's only when you hit a certain limitation of usage that you start getting charged. For anyone who's just building these projects and is just trying to learn this stuff or get things spun up, you're never going to hit a limit. Then for people who are even building companies with this, if they architect it correctly, they can get a lot for really, really cheap as well.

JASON: Now, I need to pull some settings, right? So I'm going to do this off screen.

ANTHONY: Yeah, you'll want to grab the environmental variables. You don't have to go into this part. This is going to be in your regular Heroku dashboard. Then it'll be under settings, I think.

JASON: Settings.

ANTHONY: Yeah, so then that's what you'll want to take off screen, when you reveal your config variables. Then grab that and plug it into your environmental variables in Netlify.

JASON: Got it. And this is the part that failed, right. Oh, yeah. Because we don't have -- got it. All right. So I'm going to come back here, go to my site settings. Then we'll pull up the environment variables. Let's see. Is my thingy working? It is. I'm going to drop that in here.

ANTHONY: Whoa, that's awesome.

JASON: Database URL. Is that what we called that?

ANTHONY: I believe so, yes.

JASON: So I'm going to save that. Now we can just start --

ANTHONY: Retrigger the build.

JASON: All right. That's basically done. I'll come out here instead. And let's trigger a new deploy.

ANTHONY: Then once that's done, that should be it.

JASON: Okay.

ANTHONY: Did everything correctly. You also have to set like the flavor it was. Netlify had some weird thing where it was like Red Hat or some other version. That was fixed in the most recent version of Prisma, so you don't have to worry about that anymore. That's like the least Jamstack thing I could ever think of.

JASON: And just to repeat this for anybody who is wondering how you do this through Heroku. Once I created this database, if I click this button, it just gives me -- there's an environmental variable called database URL and a value. I just copy/pasted that value over to Netlify. So that's all we need from Heroku, that step. We went to resources. We searched for Postgres here, got Heroku Postgres, clicked settings, reveal config errors, and deploy the settings to Netlify. So nice and slick. I'm into it. So I think what happened, we didn't wait for Netlify to finish the first build. So we didn't get the cache from that one for this one. So we're waiting for it fresh this time. So, you know, time for coffee, I guess. Anyone want to play elevator music for us?

ANTHONY: (Humming) (Music)

ANTHONY: Logs, logs, logs. Let the logs wash over you and through you. (Laughter)

JASON: Yeah, the environment variable masker is something that Sarah put together. Generally speaking, I find Sarah had a hand in it. A general rule of thumb.

ANTHONY: So it's a Chrome extension?

JASON: It is. And Ximena posted a link to it. Let me grab that link.

ANTHONY: That's something I run into doing livestreams, for sure. That's going to be very handy.

JASON: Yeah, so this is the tool. It's called Netlify Masker. You can search for it. I'll include a link in the show notes as well. So, all right. We got through. No errors this time. There's our build command.

ANTHONY: I think it's still building.

JASON: It's working on our functions. And that hopefully will happen nicely.

ANTHONY: So what we can talk about as this is going on is other ways to deploy this. You know, some people know this, some people don't. Tom Preston Warner was a very early investor in Netlify and has kind of been involved in this whole thing since the beginning. Now he's on the board, and Redwood and Netlify, they're very tightly integrated. Redwood was built with Netlify as the original deploy target in mind. But with that said, it's not meant to only work with Netlify. Obviously lots of people talk about lock in and worries about this. This is very much about enabling the ecosystem to build whatever we want. So you can deploy this just as easily as you have here. See how it says empty? It's not going to carry over. It blew away our old database.

JASON: Because it was SQL Lite.

ANTHONY: So now we're going to go back to our posts route in our deployed app and just create some new posts.

JASON: So here's, chat, where you're going to be able to troll us mightily. Let's see. This is a RedwoodJS powered blog. Okay. Now, remember chat, keep it PG-13, or else I'm going to have to take away your fun.

ANTHONY: Yeah, you're right. They can still troll us here. There is an authentication part.

JASON: What up, chat?

ANTHONY: So we skipped the authentication part. We use Netlify identity and implement a log-in form and authentication there. So this would be the kind of final layer. This is the very last part of my multi-part blog series, explaining the authentication layer. You can do magic link. You can do Auth0, as it says there. If you go to the cookbooks on the left, there's a more complicated RBAC. David has done a ton of fantastic work on this. If you want to roll out more sophisticated authentication, there's a lot of stuff in there for you.

JASON: Very cool. Yeah, this is some pretty powerful stuff, right. There's a lot of potential here. A lot of things we can build that are very cool and very powerful. And I love that this makes it so just quickly approachable. That's a lot of power. I also like turtles.

ANTHONY: Wow. That's good stuff.

JASON: I always appreciate your wholesomeness, chat. It's always just good. I just like it. I like you guys. But yeah, okay. So I'm into this. This is fun. From here, I think this is probably as good a place as any to stop because we can't go too much further without running out of time. So where should someone go if they want to take further steps? We've got a list. We've got a link to your first look at RedwoodJS. We'll be on the lookout for your next version of that. We have the tutorial. We've got your Twitter. I'm going to make sure that everyone goes and follows you.

ANTHONY: I used to have it Anthony Campolo with a lower case A, like camel case. I recently learned the term screaming snake case.

JASON: That is one of my favorites. (Laughter) I actually did some screaming snake case fan art. Hold on one second.

ANTHONY: Got a letter from syntax FM. They mentioned it a week or two ago.

JASON: This is my screaming snake case art that I did. But yeah. So, okay, we've got your Twitter. Where else should we go? Where is a good -- oh.

ANTHONY: Dev is linked on my Twitter. It's the same handle. I got the same handle everywhere. So

JASON: So here's a collection of writing.

ANTHONY: So you'll find like 20 or so articles about GraphQL and Redwood and all this stuff. I've written stuff about, you know, cloud flare durable objects. All sorts of stuff.

JASON: Very, very cool. Well, yeah. So this is wonderful. I feel like this was a good testament to the power of RedwoodJS that we finished 15 minutes early. And we built a lot. We created a Postgres database, hooked that up to a website, configured Prisma to act as an ORM over our database to give us a type script auto completing like GraphQL API to use. We were able to stand up routing. We were able to get our pages configured. We were able to create blogs, so create a UI admin for creating our blog posts. We were able to pull those blog posts and write a custom display logic for it. And we were able to deploy all that to Netlify. That's a lot of work. That's a huge amount of stuff that we were able to accomplish in a very short window.

ANTHONY: And I think the real testament is that it is hopefully understandable at every point along that journey. What you're doing and why you're doing it and how it fits into the larger picture. That was what really got me so fired up about this. It made sense to me in a way that learning all this other modern web development stuff didn't, like it wasn't clicking. I couldn't quite figure out how to fit the pieces together in a way that I could get the freaking thing to just deploy and work. So getting brought along on this journey of this thing being built in a way where you're not just copy/pasting stuff, like you actually understand what the code is doing. People are going to hit different limitations in terms of that understanding based on how well they know things like GraphQL or GitHub or Netlify or the command line itself. So it's not set up for someone going from totally zero. But someone from starting almost from zero.

JASON: Yeah. Well, and I think, you know, Redwood is not going to be everybody's thing because it falls on a spectrum of letting decisions get made for you so that you can be quickly productive or making all of your own decisions so that you have a deep-rooted understanding of what's going on. We all fall somewhere on that spectrum, you know. Redwood is not, by any mean, the most prescriptive option. It's just further on that spectrum than other choices out there. What I think is really interesting about this is, you know, it gets introduced on the spectrum depending on the project that you're going to do. If I'm working with a team and that team is not a group that I've had a lot of time to like build a shared language of how we write code or how we think about data or anything like that, something like Redwood is a great way to immediately create shared language. We know that you're going to create cells to interact with data. We know that you're going to use the generate command to build new code. We know that the router is going to work like this. There are a lot of opinions that have already been established. It's sort of like having a really strong ES file. You're saying we've decided as a team that we're going to do things like this. Then you don't need a code cop to go in and police that because the tools are making those decisions for you. That can create a lot of really good team unity. If you're an individual, then it's just down to taste. Do I want to have to think about how this is constructed? Is that fun for me? Am I learning? Or am I trying to get something done and just want to ship it really fast? Depending on where you learn on the spectrum for today's given project -- and I'll tell you, I land all across that spectrum depending on the day of the week and how busy I am. And I can definitely see this being a really valuable tool when we just need to get something out the door, when we want something that we know is going to work, that we know has that error handling, that we know is built on tools that are going to function well together. That seems like a really strong -- this seems like a great starting place. So with that being said, do you have any parting words for the chat today?

ANTHONY: Just thanks for having me. Thanks for giving me a chance to kind of talk about Redwood and why I'm so passionate about it. I really just like introducing people to it. It's really cool introducing it to you because you're a very experienced developer. You know how all this stuff works. I figured you'd pick up all this stuff quickly. For me, someone who's learning it, it took me a month or two to make it all the way through this whole tutorial. That's why I wrote the article series as I was going, you know. So the article series is basically me documenting my journey through doing the tutorial. What I've been able to go back to now, to really help me continue to refine these things, I've been kind of rerunning it. Like I said, I'm kind of redoing it and making it compatible with the current version. But I just recommend people check out the forms and the discord. It's a really awesome community. I've had a ton of help from the core team. Something that isn't going to be officially announced until Thursday, but I can give you the scoop on, is I'm being invited on to the core team as like a core member now.

JASON: Oh, cool. Congratulations.


JASON: And I just dropped a Discord link, if you want to join. Yeah, geez. So that's very cool. And I saw that you're on the hunt for work. So somebody hire this guy. But yeah, with that being said, make sure that -- you know, a little extra shout out for our captioning today, provided by White Coat Captioning. Thank you so much. That's made possible by the sponsors, Netlify, Sanity, Fauna, Auth0. All of them kicking in cash to cover the cost of captioning, which makes the show more accessible to more people, which I very much appreciate. Also make sure you take a look at the schedule. We've got Vue. We're not doing an episode this Thursday. This Thursday -- I'm taking it a little easy during the holidays. Not going to do an episode this Thursday, but we're back next Tuesday with Ben Hong. He just joined my team at Netlify. He's brilliant and funny. We're going to learn a lot. We're going to do an introto Vue3. Super, super pumped. We're going to do a whole separate episode in January about the composition API. We're going to do two episodes.

ANTHONY: I just talked about the composition yesterday.

JASON: Oh, nice. So yeah, I'm super pumped about that. We also have Emma Bostian coming back to the show. We're going to make fun of her taste in tacos. I'm really looking forward to that. I have a few more that are going to get added to the schedule soon. Keep your eyes peeled. Definitely mark your calendars. Remember, we have a Google calendar you can join that I think is here. Not there. Here? Help. I don't know. Schedule? Oh, my god. Does anyone remember? I forget what the command is to share the calendar. Well, whatever. We'll find it later. It's a Twitch chat command I've forgotten. But yeah. I think we've got a calendar here. This is the calendar. Let me copy that link address. We're going to drop that in here. Thank you very much for finding that for me. And I think that's as good a place as any to call it done. Anthony, thank you again for taking the time to hang out with me today and teach us all about Redwood. This was a lot of fun. Chat, thank you so much for hanging out. It is always a pleasure. I love that this is a wholesome chat that we can just leave an unsecured database open to and nobody does anything terrible. Stay tuned. We're going to raid, and we'll see you next time.

Closed captioning and more are made possible by our sponsors: