Tired of rebuilding your React-based web framework from scratch for every new project? You're in luck! RedwoodJS is a full-stack web application framework (think Rails but for JS/TS devs) based on React, Apollo GraphQL, and Prisma 2. We do the heavy integration work so you don't have to. We also beautifully integrate Jest and Storybook, and offer built-in solutions for declarative data fetching, authentication, pre-rendering, logging, a11y, and tons more. Deploy to Netlify, Vercel, or go oldschool on AWS or bare metal. In this talk you'll learn about the RedwoodJS architecture, see core features in action, and walk away with a sense of wonder and awe in your heart.
I know what you're thinking: "I want to make a web app. So, I'm gong to need React. I'm going to need Prisma on the back-end to talk to a database. I'm going to need Apollo to talk from the front-end to the back-end. I'm going to need Jest so I can write some tests. I'm going to need a Storybook so I can design, code, and test my components in isolation. I'm going to need Webpack to glue this whole thing together, and I'm going to be Babel so I can compile, transpile ES6 down to ES5. That's the web app, but I'm going to need to figure out how to deploy this thing. Maybe it could go to Netlify, they're really into Jamstack. Maybe it could go to Vercel, they've got a cool triangle logo. What about Render? Maybe then I could use containers, and get long running requests and GraphQL subscriptions. Or maybe I need the full power of AWS? I haven't decided yet.
[01:10] In any case, I'm going to need some kind of authentication, so I could use Auth0 for that. I'm not going to write my own. Got to go further than that though, I also need authorization. So, I'm going to need some role based access controls. And what about logging? I'm going to need really good logging, send that to the provider of my choice. I'm going to have to code split this thing, so the browser doesn't get too much code at any one time. I'm going to need OG Tags so this thing will unfurl on Twitter and Facebook. Got to have pre-rendering, probably in order to do that. And I'm going to need accessibility right out of the box, so this thing works in screen readers. All right, let's do this thing."
That's not really how it works, is it? It's more like this: all right, Google, my old friend. Create React app. Okay, there you are. What's the... There we go, NPX, create React app. All right. Oh, what am I going to call this thing? Glubnox? That works. All right, we'll let that do its thing for a while. Let's learn how to use Prisma. Okay Prisma, here we go. Nope, not that one. Nope, not that one either. Aha, found you. Yep, here we go. Prisma docs, quickstart. All right. "In this page, you will learn how to send queries to a..." Okay, great. All right, this is going to take the rest of my life.
[03:00] But despair not. Because I'm here today to tell you about RedwoodJS, the full stack React app framework of your dreams. Now, I know what you're wondering: "Who's this bozo telling me what to do?" Well, I'm Tom Preston-Warner. You can find me online @mojombo on Twitter and GitHub. In the past, I've created such companies as GitHub and Chatterbug, the best place to learn a foreign language. I am also very active in open source. I created Jekyll, I wrote the SemVer, the semantic versioning specification. TOML, the configuration language used a variety of packages, and Gravatar, the avatars that follow you around.
How RedwoodJS works
[05:07] But enough chit chat, let's see some code. Okay. I have here on the right, opened github.com/redwoodjs/exampletodo. This is a simple to-do application written in Redwood that I thought I'd run you through a few things and show off how Redwood works, and how it thinks about the world. So I've cloned this down to my local machine, I've run Yarn, you can see the instructions here, and I've initialized the database so it's all set up and ready to go. So, the first thing that I'm going to do is run Yarn, Redwood dev to spin up the web server. This is going to spin up both sides that I talked about before, both the web side and the API side, the front-end and the back-end, link them together and fire up a new browser window with my to-do list.
[05:55] So, I'll show you how it works. I can say, I need to order some milk and some cheese and some eggs, and I can toggle these things on and off. And this is talking to the back-end via GraphQL. You can see here in the logs, the posts to the GraphQL back-end.
So how does that work? Well, in the editor over here, you'll see the web folder and the API folder. This is a monorepo structure, so these are each separate packages within a single git repository. So let's start where everything starts, which is with the routes.
So in the routes file here, you can see that we have a router, and within it we have a route for the homepage and a route for a not found page. This is essentially the 404. So, this is our homepage, and in Redwood we have another directory for pages, and it's very easy to find things, very organized, the directory structure. So this is the homepage, this app is using style components, which you'll see here. And it's just standard React, so this should look very familiar if you know React. So, here we have a wrapper, we have the title, To-Do List, and then we have a to-do list cell.
[07:13] Now, Redwood has a really special concept called cells. And cells are way to declaratively do your GraphQL data fetching. So, let's go look at that.
To-do list cell, is in source components to-do list cell. So, source components, to-do list cell, and here's the file. And how this works is you simply export a certain set of variables from here, constants, including the query. So you start with your GraphQL query, you just type it out here as a GraphQL query. Or you could have a mutation in here as well, which you can actually see here. And then you have different states, and you just export these states as components. You have a loading state, and a success state. You can also define a failure state and an empty state. Right here, I've just done a loading, it very simply says, "Loading," and a success state.
[08:16] And so what this does, is really simplify and focus you on what you're building. So in this instance, when this query completes whatever the query name is, the GraphQL query that you're doing, in this case to-dos, that will be sent in as a variable, as a parameter into the success component. And then within that, you can do your work. So this is doing some optimistic updating, which you don't have to worry about too much for Apollo. But right here, you can see that it's just taking the to-dos, and mapping over them and creating to-do items, which represent each of these items here. And that's what cells are, so really just a simple way to think about your data fetching, so you don't have to do a lot of conditional logic. It really organizes the code around that.
Now, when you get to the back-end, to the GraphQL side, let's see what that looks like. So here's the query, to-dos. I'm going to look in the API side here, I'm going to look in source, GraphQL, and to-dos. Now, these we call services, so you break up your Redwood application into different services. This app is very simple, it only has one service, this to-dos service. A larger application, you might break it down into different services for the different responsibilities that your application has.
So, let's look for what we were talking about. To-dos is going to return an array of to-do items, which are defined here. And when you're normally writing Apollo, you have to think about writing your resolvers, and there's a specific way that you do that, and they all end up in the same file and it can feel very messy. It feels like a lot of boiler plate. So, we've eliminated all of that in Redwood and said, "Well, let's just match things up name." And so, if you come into the services directory and look at services, to-dos, to-dos JS, what you're going to see here is a constant, to-dos, this is the same name as the GraphQL query, being exported. And Redwood is smart enough to match that up and say, this is the implementation, this is the resolver for that GraphQL call. And in it, we're going to use Prisma, which we just import here. DB represents the Prisma database, and now we can do any of our Prisma calls on that. In this case, we're just doing a find many.
[11:19] So, a really fun way that we can explore that as you may be already used to, is with a GraphQL playground. And Redwood of course, ships with one of those, and that's going to be at local host 8911. And we can try this out, so I already have loaded up the same query that we find in to-do list cells, just the to-dos query, ID body status. And if we run that, then we can see that we get back exactly what we expect: milk, cheese, eggs. And everything is as it should be, and really that's all the code that it takes to do the front-end to the back-end.
Building from scratch with RedwoodJS
So, all right, "That's cool," you say, "What about doing this from scratch? Having it all there right now is one thing, but what if we want to do something from scratch?" Well, let's give that a try.
[12:13] I'm going to close all of these, and I'm going to switch over here. And what should we implement? I think it would be fun to have a page that just showed me the count of to-dos. Maybe something else needs that for some reason, it's a simple thing to implement. So, we have the ability to generate certain things in Redwood. So I'm going to run Redwood, sorry Yarn, Redwood, generate, or just G for short, page. And I'm going to call it the count page. I'm going to run that, and what I'm going to see here now in my routes file is a new route has been generated, slash count, takes that from what I typed in here. And it has created a count page for me, so let's go look at that count page.
It's also creating stories file for Storybook, this is integrated out of the box, and a test, this is the Jest test. So, we're beautifully integrating all of these different elements. Right now, we can go look at this in slash count. And here we go, this is just the generated page, this is what it looks like. If we make any changes in here, then they will be immediately reflected over on the website. So, you can go back and forth very simply and do this.
[13:37] Okay, very cool. Now, we need to get some data from the back-end, so let's switch over to the API side now. And we're going to modify our SDL file to create a new GraphQL query, and we're going to call it to-dos count. And it's going to return an integer, always an integer.
I have a Redwood extension installed in VS code here, and it does some nice things. So, here you'll see that it can tell me that this service is actually not implemented. I don't have an implementation for this specific query. And so this is just a very nice way because we do some things automatically for you, we want to also help you see when some of those automatic things aren't going to work properly. So, this isn't implemented. Well, let's go implement it.
[14:28] That's in services, to-dos JS, we're going to create something called to-dos count. We'll do that right here, export const, to-dos count. That is going to be a function, and it's going to just be DB.todo.count.
Okay, well, let's see if that worked. We should be able to run it and see if it worked, and here we go. All right, there we go. See? I added two lines of code and I created all of my GraphQL infrastructure that's necessary in order to get the back-end working. So, there we go. We have a new GraphQL query defined, ready to return some data and be consumed the front-end on this count page.
[15:15] Well, we're going to need a cell. We're doing data fetching, so we need a cell, so let's create a cell. Yarn, Redwood, generate, cell, to-dos count. We'll let that run, and we're going to find this in components, to-dos count cell, right here. Now, it's generated this and it's guessed what I'm going to write as a GraphQL query, but it's a little bit wrong. Let's make it correct. It's just going to be to-dos count, like that. The loading state is fine. The empty state, this is what I was talking about before, these are all predefined, we just change these as we like, an error, failure state. And the success state is just going to default, just do a JSON stringify on whatever comes out of here, which is just going to be an integer. And that's fine for now, we like that.
And now, we're going to use this in the count page. So, let's see. In here, why don't we say to-dos count is going to be the to-dos count cell? And then we need to import this, we're not using that anymore. So, import to-dos count cell, from source components, to-dos count cell. And if we come back here before we save this, then there we go.
[16:52] And that's really it, we did all of that in a couple of minutes. If I wasn't talking so much, I could have done that a lot faster even. But that's front to back, all the way on the React side, to the GraphQL, all the way to the backside, Prisma, to the database. The whole thing, with just a few lines of code, a few generator commands. And that's really how easy Redwood tries to make everything that you do.
Now, I mentioned before that we have Storybook integration as well, so I'd like to show that off real quick. In order to get the server, the Storybook server running. I'm going to run Yarn, Redwood, Storybook, and that'll take a few seconds to spin up. And when it's done, it's going to launch this page. And you'll see here, we have the Storybook stories for the things that I've generated. We have the count page, which is generating, and check this out. It actually some data for the count that's coming back. But that's interesting, 42, where did that come from? Why does it say ID there? That's a little bit weird. Well, the reason that that's happening is that it's pulling it from here. So the count page, that whole page is pulling from this count cell. And one of the things that we do for you, if you were paying attention, over here when we created this and I came into this directory, is you'll see this mock file.
[18:17] And this mock file is going to make it really easy for you to see your cells in Storybook, because mocking out your data fetching in Storybook is one of the real challenges to getting Storybook working really nicely with your application. Again, Redwood tries to make it really easy for you, and so we have the concept of mocking your data fetching. And in this case, we've just said, we think the to-dos count is going to return an object with an ID. You might remember that that was what we guessed you were going to do. But it's very easy to come in here and just say, oh, it's actually just returning a number, and save that. And now you'll see that the success state of this updates, and indeed the page itself updates. All of these components can use these mocks, the cells. And that means that when you have a page, or you have a cell or a component that has a cell in it, anytime you have one of those, if you use it in your Storybook, it's cool. It's just going to pull this mock data and it's going to include that.
So, it's still very easy to visualize your components and work with them without the hassle of trying to think about how you're going to get that data mocked out in there. And again, you can very easily now work on the loading states of these things. So, to-dos count cell has these various states, I might want my loading state to just say, "Just wait." I can save that. And now, instead of trying to be in my app and clicking refresh, and being like, "Oh, does it look right? Oh, I see it. It's there for like a hundred milliseconds or something," even less. That's kind of a pain, and I can go to my inspector and I can make my page really slow, and that's annoying. But that's why we have Storybook. Now I can come in here, and I can stare at it for as long as I want, and I can make it work.
[20:10] Or the empty state, what does that look like? What happens when I get an error? If I want to make an error happen in my application, I have to go into my implementation probably, and add some kind of a syntax error so that it's going to throw some kind of error. No, don't screw around with that. That's why we have Storybook. And Redwood is all about making the entire thing, the entire application development process, end to end, as smooth as it can possibly be with as little amount of work, as little amount of boiler plate, as we can possibly make it for you. We have spent so many hours reducing boiler plate, and doing integrations of these products, integrating Jest integrating Storybook, removing Webpack.
You'll notice that in here, you really don't see Webpack at all, you don't see Babel at all. Of course, we have some config files for you in case you need to dig into them, but you never have to touch them, really. If you're just doing normal things, this is not a part of your job anymore. Let the framework do the heavy lifting for you, so that you can focus on your business logic.
What else could Redwood do for you?
[21:14] Well, what else could Redwood do for you? Well, I'd like to show you something that we released recently, that is pre-render. The ability to take any of your pages and specify that they be built at build time, that they be rendered at build time and then statically shipped. So, how's that going to work?
Well, let's say you have a splash page, some kind of a marketing page that really doesn't have a lot of dynamic content, and you just want to build it, render it at build time and make it available. So, let's do that. Let's create a page that is really just static.
[21:49] So I can run Redwood Yarn, Redwood generate page, I'll just call it splash. And we'll get that splash page, and let's go to it here so that we can see it. And let's find it, web source, pages, splash page. And in here, we're going to make this pretty simple, let's just go with this content. But what I want to show you is how the hydration is going to work when we do this, and how easy we make that as well. So, let's make this a button instead, and give it an on click that is just going to be a function that runs an alert, "Hi."
[23:34] So how do you do that in Redwood? How do I tell Redwood to take this page and pre-render it at build time? Well, let me show you. You might expect that it's easy, and you'll be right. In fact, it's one word easy. All I do is come to my routes file, I find the route that I want to pre-render, and I just add pre-render onto it, and that's it. I'm done, that's the only thing that I need to do. Can that really be true, can it be that easy? Well, let's find out.
So, I have this specified now and what that means is during the build, it's going to pay attention to that, and build a static file for that. And so, I'm going to show you how to run a build, and then you can inspect it. And in Redwood, you just run Yarn, Redwood, build, and that's going to do all the steps necessary to actually deploy this. This is really what happens during the deploy process. It goes through the website, it goes through the API side, and then it has a pre-rendering step. And during the pre-rendering step, it's going to look for any routes that has been specified to be pre-rendered, and it's going to render them.
So, let's see if we can make that work. Well, I can show you how this works going into the web dist directory. And I'm just going to run serve, which is just web server. It's going to spin that up, it's going to copy the URL to my clipboard, and it's going to send me into my app. And unsurprisingly, the homepage doesn't work, because it is a full app and there's no back-end. And the error here is, I can't find my GraphQL. Okay, we expect that.
[27:34] Remember this ridiculous screen that I assembled before? Redwood has a solution for every single one of these things. All of the stuff in the application, authentication providers from Auth0, to Netlify to many others, a bunch of deployed targets, including Netlify, and Vercel, and Render, and the ability to deploy your API side to AWS. We have so much more in the works to make your life easy. The whole point of Redwood is that we do the beautiful integration for you, so that you can focus on your product and what makes your business valuable.
If you want some stickers, you can get those on the website. We'll ship them to you free anywhere in the world. I'm really happy you joined me today, and I hope you come and join our community and find this stuff really useful. Completely open source, MIT-licensed, free for you to use. We do this to try to make your life easier. Thanks for watching.
[28:33] Catalin Miron: So, you're asking everyone what kind of infrastructure do you deploy your web apps on? And it seems that Virtualized won, it's the most voted. And after that, it's followed Container, which is Render, Begin, Fargate, Cloud Run. And on the third place is JamStack. I have to admit that in my opinion, I'm using Virtualized just because I can get my hands on the virtual machine. But what are your thoughts about those votes?
[29:16] Tom Preston-Werner: Well, I think this is really interesting, also because there isn't one clear winner. There isn't one that really super dominates the rest. And I think that's super interesting. So, an interesting fact about Redwood, when I started working on it, it was really envisioned to be a full stack web application framework for JamStack. But as we've been working on it, we've found exactly these results are kind of the case of how people are thinking and operating. And so, we need to adjust our homepage and our marketing now, but really the goal is to target all of these equally, and follow you where you want to go. Depending on your application and what your use cases are, you might choose different ones for different reasons. Like you might choose to go on to Netlify for the ease of deployment, and it's just so simple, but you might need more control. You might need certain things around a Container-ized approach, or just Virtualized straight on, EC2 so that you have total control. And Redwood is happy living in all of them, there's no vendor lock-in, we're not specific to any given vendor. And we try to make it really easy, and have built in deployments for a lot of these where you just say, Redwood Yarn, Redwood deploy, you give it a target, and we'll do most of the rest for you. You'll have to make some choices, but we try to make that Redwood easy.
[30:43] Catalin Miron: Great. If we are going to talk more than two minutes having this slide up, I think it's going to be evenly distributed for the first three places. So yeah, it's pretty obvious. It seems that EC2 are like Visualized, but as you said, it's morphing based on your needs, right?
Tom Preston-Werner: Yeah.
[31:14] Catalin Miron: Let's see, what are the questions? Let me grab a couple of them from... We had lots of questions for you, that's for sure. The first one, it's coming from Dermuhammad. He's saying: 'What's the difference, the main difference between Redwood and Blitz JS?
So for instance, we do our own routings, we have a custom routing solution that allows you to put all your routes into one file. Some of these things are inspired from what I really loved about Rails, and that was a big one for me, was the simplicity of the routing layer, and how that allowed you to trace your code trivially. And it also gives us a lot more flexibility around how we want to approach the situation in general. I think Blitz has sensed this a little bit, they've had to fork Next in order to do some of the things that they want to do. So I guess our choices of what our baseline build point was, is a little bit different, and that expresses in different ways. In some ways, it's really beneficial for Blitz to have such a solid foundation to work on. And you get all of the things that Next already has out of the box, that we've had to really think about building from scratch. But to me, I really like that. I like having a more blank slate to work from, I don't like to be pinned in some of these choices.
Another big thing is that we use a GraphQL layer very specifically to talk from the front-end to the back-end, and I know a lot of people were wondering about that. The reason that we do that, one of the biggest reasons that we do that is because we've envisioned Redwood as a multi-client framework, so that it becomes more than just a web app framework, it becomes a cross-client framework. So, oftentimes when you start working on a new product, you're going to start with a web app. That's pretty normal that you're going to have one, and we love React for that on the front-end. And then later on, you'll decide, "Oh, I need a mobile application." And at that point, what usually happens with frameworks like Rails or if you were using Blitz this may happen, then you say, "Okay, well I need my client to talk to my back-end, so now I guess I'll re-implement some kind of an API so that I can talk from my mobile app to my back-end." And now, you're duplicating work that you've done before.
And so what we said is, let's just start with the assumption that you're going to be multi-client, or eventually want to be multi-client, and create the abstraction between the front-end and the back-end, and use a protocol that is really efficient, very well known. There's clients for everything that you would ever want and interface with. So that when you decide that you want to do a command line interface for your project, guess what? Your application is already just a GraphQL API on the back-end, and so there is no additional work to do. It's already completely engineered from the very beginning, to serve a client that ends up being sort of abstracted. But you still have full control over that, you're building the GraphQL API yourself.
So you have both the flexibility of GraphQL in its best parts, but also the flexibility to use it across multiple clients. So, that's really the crux of why we chose GraphQL for that. So, those are two of the big differences, and of course there are many. Just our approach to lots of things are slightly different. It's just different, we both think differently about things. And I encourage you to explore both, to see which one might serve your use case better.
[35:18] Catalin Miron: Yeah, it's really fantastic. You took inspiration from Ru on Rails, which also have generators, RedwoodJS also have generators. You have services like the layer, that communicates with things, and the back-end. It's fantastic. I have a question, since you also touch the Next JS part, the question from Vlad F is: ’How is Redwood handling SSR? Since Next JS is handling SSR, how RedwoodJS is handling SSR?'
[36:05] Tom Preston-Werner: Yeah, I think Next has really made itself a real winner in this area, so super relevant question. These are questions that we've had to kind of re-imagine from a Redwood perspective. Thinking about... What we've really tried to do is think from first principles, what do you need as an application developer? And so, we're starting to really think about these problems from what is the problem that you have, and how can Redwood solve them for you in the most elegant way possible? And in the talk, I showed our pre-render support, so that's one portion of what SSR might do for you. You have this problem where you need to have content generated that you don't want to generate it at build time, but you'd like to have it cashed and served.
And you might do that for various reasons. One of those reasons might be because you need to have OG tags, and support for that. And so, this is where pre-render, and pre-render is just the first of many features that fall into a category, we call Redwood at scale. Because all of these things, if you think about them, all of these strategies, things like SSR are really about caching, they're really about performance at scale. And that almost always boils down to some form of caching. And so, when we think about caching in Redwood, pre-render is the first implementation that we started along that path. Where we say, "What do people often need? They need the ability to statically generate marketing pages, because there's no reason to do them dynamically. But it would also be nice if they could be rehydrated, so that any small bits of interactivity on the page will still function."
And that's what pre-render is. And as you saw, it was a single word in order to enable that across multiple different deployment platforms. And so, that's really what we want to do for the rest of it. So we will eventually have solutions for something that looks a bit more like SSR, or you saw Netlify today, just came out with their solution for this, that maintains atomicity around deploys and can make it a lot more easy to reason about. So, we'll leverage those things on these different platforms, and make them really easy to engage with hopefully as little work as possible in the meantime. So, our approaches will be different, I think, than Next. They'll look a little bit different, but they're going to be really specifically aimed at solving problems that web application developers have in the deployment and scaling part of application development.
[38:39] Catalin Miron: Great. Imagine Wagons is wondering: 'How about TypeScript?'
[39:37] Catalin Miron: Speaking about 1.0, Reddit hasn't released a 1.0 version yet. When will that happen?
[39:48] Tom Preston-Werner: We're getting close. It turns out building a web application framework is more work than I expected. I already expected it to be a lot, but it's actually a lot more than I even anticipated. So, it has taken us a little longer than we hoped, but it's because we really feel strongly that you need a certain set of features to be considered 1.0, for a full stack application framework these days. And we want to make sure that's all there, and that you can rely on it and trust it. And that there's going to be no surprises, and we're not going to be breaking a bunch of stuff afterwards.
And so, we're looking to hopefully do a release candidate for 1.0 in the next couple of months. I think that's totally realistic. We're really on the last set of features that we need to work through. There's some stuff around securing the GraphQL API in a really easy way, that we're working through right now. Full TypeScript support end to end, and a couple of other things, but there's really, we've knocked out most of the big things that we wanted in there. So, I would look forward to an RC one in the next few months, and then a 1.0 will probably come mid year. It'll take a few months to go through the RCs, and make sure it's really stable. There's bugs to be fixed as more people start using it and trying it out in different environments and things.
So, it'll be another five, six months probably before there's a 1.0 released at this point. But it's pretty stable. And if you check out our release notes, we spend a huge amount of effort on our release notes, making sure that you know exactly what you need to do in order to upgrade. And you can look back and see how we've been doing those, and it's just very easy to follow the upgrade path. So you can get started right now, it's pretty solid in most regards, and know that you'll have a really smooth upgrade path along the way to 1.0.
[41:28] Catalin Miron: Oh, thank you so much, Tom. Thanks for sharing everything about RedwoodJS. It's been a pleasure to have you here. Again, thank you so much for taking your time, it was a great Q & A session, but also a great talk. Thanks, and goode, e. Thank you.
Tom Preston-Werner: Thanks so much for having me, appreciate it.