The talk was streamed on #PrismaDay | June 26th, 2020
Prisma Day is a community-focused online conference on modern application development and databases.
https://prisma.io/day
Subtitles for this video have been professionally transcribed. Prisma can provide the subtitles for your tech video as well - get in touch with us at [email protected]
- Hello today, we're gonna learn about Redwood JS. I'm Tom Preston-Warner, you can find me online @mojombo on GitHub and Twitter. You might know me best as the creator, co-founder, and CEO of GitHub. I did that for many years and now I'm working on another startup called Chatterbug, the best place to learn a foreign language online. You may also know me as a creator of such projects as Jekyll, Semantic Versioning, TOML, and Gravatar. Now today, I'm gonna talk to you about a new project I'm working on and that's called Redwood JS. So what is Redwood JS? Well, the easiest way to say it is that Redwood JS is bringing full-stack to the jam stack. So what does that mean? Well, what I'm gonna do is show you the code and how a web application, built using Redwood, comes together. And as I show you the code, I'll explain how it fits together. The first thing I'm gonna do is pull down a repository online that lives at github.com/redwoodJS/exampletodo. It's an implementation of a simple to-do application with Redwood JS. And down here, you can see the install instructions. So I've already pulled that down locally and I've already run Yarn in order to get everything installed for it. So the next step is going to be to get the database set up and I'm gonna use Yarn Redwood DB up in order to do that. That's gonna use Prisma to migrate the database and get me going. And the next thing I'm gonna do is run Yarn Redwood, I can just type RW for short, "Yarn Redwood dev," and that's going to start both sides of a Redwood application. Redwood has two sides; an API side, that is an implementation of a Graph QL API, and a web side that is a React client that then can be distributed via a CDN or something like Netlify. So the idea here is that you should be able to deploy a Redwood application using Netlify by just pushing to your GitHub repository and let Netlify or other similar providers take over and do all of the rest. So here I have this up and running, so I'm gonna switch to local host eight, nine, 10, and now I have my to-do application set up and running. So let's say I want to go shopping for some groceries and I want to buy some milk and some cheese, and some eggs. I can add those. I can check them on and off. And this is all at the front end, the React client front end, written using Redwood JS, communicating to the Graph QL API via Graph QL to do all of this. Now, the reason that we're using Graph QL to communicate between the front end and the backend is that we envision a future where your application might have than just a web application front end. So you might start that way but in the future, if you wanna create a mobile application, you're probably going to use Graph QL and in that case, you would prefer to have just built a Graph QL API that you can already consume that is already the main effort of your business logic, instead of having written it in something like Rails, where you've done things in the Rails way, and now you decide that you have to add an additional Graph QL API after the fact, and that's a bunch of additional work that you now have to do. If you start with Graph QL, you're preparing yourself for the future. So let's look at the code for how this works over here in my editor. I'll start on the API side and that lives in an API directory. We use a mono repo approach here. There's actually two Node projects and API project that's going to be deployed to a server or a Lambda. So with Netlify, Netlify can deploy your Graph QL API to AWS Lambdas for you and then the front end, which is a statically deliverable React client. So on the backend, we can start by looking at the Prisma schema. So we use Prisma as the way to as the way to demonstrate how to connect your backend to the database itself, right? So we use Prisma to... It's been super amazing. It makes it so easy. You know, when we started working on Redwood, more than a year ago, the landscape was a lot different when it comes to query builders or ORMs and we were looking for something that felt right, that felt amazing, and there just wasn't anything out there that we could find and then we came across Prisma. And coming from a Rails background, working with Active Record, which is a tremendous accomplishment, everything in the JavaScript world and the TypeScript world felt mediocre, but the way that Prisma approaches the world, we felt was really nice. The way that you create relationships between tables and just the ease of setup and the built-in migrations, all of that's available to us. And so we've integrated that and made it a fundamental part of what a Redwood JS app is. So let's look at the model here. So in the Prisma schema file, we have a to-do model and it has an ID, that's just gonna be the default, auto-incrementing integer ID. We have a body that's a string, we'll make sure it's unique. And then we have the status, that's a string. It will default to off and it can have other states. So let's now look at how the Graph QL part comes together. So we're looking in API, source, Graph QL, todos.SDL.JS. SDL is the schema definition language for Graph QL. So you're just very simply writing the SDL directly for your Graph QL. It makes it really easy to understand what your Graph QL API looks like. So we have a to-do type with those same fields and we have a query, a to-dos query, that will return a list of to-dos. And then we have a few mutations. Create to-do, update to-do status and rename to-do. And I want you to pay attention to these names. To-dos, create to-dos, update to-do status and rename to-do because when it comes to the implementation of this Graph QL API, that happens in this services to-dos, to-dos JS file, and you'll see that we're just creating functions that have the same names and exporting them. And Redwood JS is going to map your Graph QL SDL to these functions directly. So instead of creating a resolver map, like you would normally with Apollo, which is what we use Apollo server, we'll do all that work for you. And it also makes these functions more reusable. You can see that these are just functions that take normal sets of parameters and then we'll return promises in this case. Apollo knows how to consume those promises but this means that you can call these from other parts of your backend. And the way that we organize the backend is through services, right? And a service... When I say service, I don't mean a microservice or anything that's running separately, it's all running together in the same runtime. These are just chunks of your backend that you're creating as a sort of API, an internal API. And so, services can call into other services and each of those services would be responsible for a set of tables. And that's how the backend works the API, so let's take a look at the front end, real quick. Everything starts in source routes. We've written our own router, we call it Redwood router, and it's a single place for all your routes to exist. It makes it really easy to trace the path of where your code lives and it's just mapping URLs to components that end up being your pages, and then you can give them a name, which is useful in the name of route functions that I'll show you in a little bit. We can look at a page, like the homepage here, and see, it's just a normal React component. We're using styled components here and we're doing some data fetching with Redwood cells, and I'll show you how to do data fetching with a Redwood cell shortly, and then just putting these things on the page. So let's get into adding a little bit of functionality. So the first thing that I want to do is create a new page. So I'm going to create a new page for you here. I'm gonna open up the routes file so you can see what it does. And in Redwood, we have a bunch of generators that make it really easy to do a lot of things. One of those things that you can do is create a page, so I'm going to, down here, type, "Yarn, Redwood, generate page" and I'm just gonna call it "Count." I'm gonna create a page that's going to tell me how many to-do items I have, so I'm just gonna call it "Count." And now you'll see that I get this count route in here and it's named, "Count." And if I go to that page/count in my browser, then you'll see that that page exists and I can open up that page here, and you'll see that these are the same. So that's where my page is gonna live and now, let's work on the Graph QL implementation for that. So I'm gonna start in my SDL and to define this, this is going to be a Count, but I'm gonna call the query to-dos Count just to make sure it has its own namespace, and it's going to return an integer and it's always gonna return an integer. Another nice thing that comes with Redwood via Prisma is a Graph QL inspector. So I can come in here and I can run this query now. And I'll see in the docs here that I already have this available, but there's no implementation yet. So what I'm gonna do is implement it and that's as easy as going into the services file for to-dos and implementing a Count. So I'll do that here. I'm going to export const, to-dos Count and that's going to be a function with no parameters that's going to return the count of records in the database. So I have that saved and now, I'm gonna come over here and I'm going to run to-dos count as a query, and we'll see that we get the results. So that's how easy it was. Just a few keystrokes, I've been able to implement that whole Graph QL backend. That's a lot of what Redwood is doing for you. So let's continue with that and on the front end here, I'm going to use a cell to do the fetching, and I'll show you what a cell is. So Yarn, Redwood, generate cell, and I'm just going to call it the "to-dos count cell." And this is gonna generate for me, this to-dos count cell right here. A cell is a declarative way to do your data fetching in Redwood. So it's gonna guess what query you want, in our case, this is just a scalar query, so it'll look like that, and what we're doing for you is running this query and then passing the results, or the different life cycle methods, and you just declare what these components look like. So you have a loading state, an empty state, a failure state and then your success state. So when you're writing your success component, you can really just focus on it being the correct results from this query. So this should be all we need to do, whatever you call here is gonna be passed into here, and it's just going to be JSON stringified and output. So let's now use that cell in our count page. So we'll do "import to-dos count cell "from source components to-dos count cell," and then we'll be able to use it here. To-dos count cell. And if we come back to this page and we save this, then we'll see that that loads and indeed, it is pulling that data from the Graph QL API and it would have shown a loading there if it had taken longer to load. So that's front-to-back, end-to-end how you would create a simple page in Redwood. So okay, that's cool but that's pretty simple, so let's try to do something a little bit more challenging. So what I want to do now is implement a page that's going to list out the contents of each of these to-dos, kind of a single to-do view page. So in order to do that, I'm gonna start on my back end here, with the Graph QL, and I'm gonna say, "what do I need?" I want to have a query that's gonna return just a single to-do and it's going to take an ID and that's going to be of an integer type. And that's going to return a to-do item and, possibly, that to-do item is null. And then I'll work on the implementation here and that's going to be a to-do. So I'll do export, const, to-do and that's going to take an ID parameter and return a db.todo.find one. I'm using Prisma here, it makes this whole thing really easy. And I'll give it a "where" clause, and that's just going to say "ID is ID." So we can come over to our playground here and let's run a to-dos query with the ID being one, and I'll get back the ID and the body, and we'll run that. And now, we can see here that we have the first item that we entered, milk. Okay, great, so the Graph QL side is implemented and now on the website, I'm going to need a page. So I'll come back and I will Yarn, Redwood, generate page and I'm gonna just call this the to-do page. So now I have my to-do here and if I load that up in my browser, then I can see that that page exists. And what do I need to do here? Really, what I need to do is pass in a parameter. And so this, I'll show you how to do route parameters with Redwood, use curly braces. You give it a name for whatever's gonna be in this part of the URL, and then you can even tell it what type. And because this is going to be the integer of the ID, then I can specify that I want it to be in an int, and that will not only make sure that I'm only matching things with integer IDs in this part here, but it will actually parse this and turn it into an integer in my code, making that all ready for me. So I'll make sure this page is saved. And now, you can see that this page is not found because it no longer matches this route. There's no ID on it. But if I get it a slash one, now it matches again, an ID becomes available. And that becomes available in my to-do page. And that's just going to be passed in to the component as a prompt. So I can say that I want to receive an ID prompt and now, I can use that in my component. So in order to get data from the backend, I need to do some data fetching, which means I need a cell. So I can say Yarn, Redwood, generate cell and this one is just gonna be called "to do." So then we call it the "to do cell" and the to do cell looks like this. We need to be able to pass in the parameters here and so I'll give this query a name and I'll say the ID is gonna be an integer. This is just the same as in the SDL. And then in the query itself, I'll say that the ID is that ID that's being passed in. And I'm going want back the ID, the body and let's say, the status. So now, this is my query and whatever this is called will be the same name of the variable that's received here, and we'll just stringify that when it comes out. Redwood's going to do the mapping for you of the... Of whatever comes in here. It's gonna put the parameters from the prompts that you send in to this cell that is representing itself as a normal React component, we do that for you with a higher order component but that all happens behind the scenes. And so I just call to-do cell, like a normal cell. So on my to-do page, I will import to-do cell from source components. To-do cell. And you can always reference things just from the source directory anywhere in a Redwood app. It just makes it really easy to know where things are and how to get them. So to-do cell. And then, I can use that right here. I'm gonna say, "to-do cell," and it now takes a parameter. It takes that ID parameter. So ID is going to be ID, right? That's just what's coming in here and we'll close that, and we'll save that and here we go. There we go. Now, it's reaching out to the backend. It's asking for it to execute that query, the to-do query that I specified, it's getting back the data for that query. Now, the next thing that I want to do is link up the items on the homepage so that I can click them and go to that to-do page. Okay. So we'll start with the to-do item, which is each one of these things is represented by this to do item component. And in here, I'm going need a few things from the router. So I'm gonna import link and routes from Redwood JS router. And then right here is where the body is. This is the line that prints these out, and I'm gonna replace the body with something that's gonna generate a link. So I'm just going to create a new function called "to-do link," and it's going to take an ID and a body, and it's going return a link. And the way that we create links in a Redwood application, we say "link to" and then we can use this routes, from the router, and then we can use the name. We just use the name of the route, which is why we give the routes names. And then, these are actually functions and I can pass in the ID that I want to use to fill in that piece of the route parameter and it will generate the URL for me, so I don't have to hard code the URLs. And the body of it is just going to be body. And so now, I should be able to, down here, make a call to "to-do link" and pass it the ID and the body that are coming from up here, ID and body that are being passed in already. So I'll save that and now these are linked up, and if I click on one of them, then it goes to my to-do page. So there's a little bit of how you do the routes. Okay, that's all well and good. Let's do something even harder. So next, I want to add authentication, right? Because my application right now is open to the world. Anyone can come on here and be like, "Hey Tom, you should` buy some Twinkies," right? But I don't want anyone in the world to be able to do that. I want to have to log in so that I only have access to this. So let's add "auth." And in the Redwood way, we've made this super easy. So I'm gonna start with the generator, of course, so I can type, "Yarn, Redwood, generate off." And right now, we support Netlify Identity, Auth0, MagicLinks and we have some fire based support that is sort of half done. I should mention the Redwood is in alpha, maybe nearly beta status. We're hoping to have a 1.0 out before the end of the year but this is all still very much in progress. So here we go, "Yarn, Redwood, generate off." I'm gonna choose Netlify, to make my life really easy, use Netlify Identity. And I'm gonna run this, this will take a minute to run, and what it's doing is pulling down some packages and adding bits of code here and there in my application that I'm gonna be able to use to do my authentication, and that's gonna provide me everything I need to do that. And you can see where it does this, actually, is in the index JS page and it's added this auth provider that just says it's Netlify, but we do all the hard work for you. And so that all I need to do is have a place to actually log in. So I'm gonna create a page to do that. I'm just gonna call it "Yarn, Redwood generate page." I'm gonna call it a login page. And so now, if I go to slash login, I have a page here. I'll open the page. And in order to do the authentication now, I have access to Redwood JS auth and a use auth function. So to get that I'm gonna import use auth from Redwood JS auth. And then, in my component, this is a simple hook so I'm gonna use it like a hook here. So I'm gonna say const. I'm gonna destructure some stuff and that's gonna be coming out of use auth. Okay. And then the things that I can get out of there are a login function, a logout function. I can get the current user, if there is one, and I can get an is authenticated Boolean that I can use. Okay, great. So I have those and now, I can just create a simple button. So button on click equals a log in. So I want a login button and that'll just be called "login." And I want a logout button. That'll be called "log out." Okay. So I can get those on the page but really, I want to know if I'm logged in or not, so let's use is authenticated. And when I'm authenticated, I'm gonna print out the currentuser.email. That's a field in there. And if I'm not authenticated, then I'm gonna just say "logout." Okay. So here we go, I've saved that and indeed, I am logged out. Now, the magic here is that these buttons are hooked up to all of the guts of Netlify identity and the Netlify identity widget, so all I need to do is click login and I'm fully implemented. Everything is connected for me. And I already have the setup in a local development. You need to connect it to your URL here, that you get out of the Netlify application. I've already done that and so, I'm just going to login and now you'll see my email address. So I've successfully logged in, I am authenticated. Okay. That's cool, right? I have a user, I can tell who that user is but now I need to secure my application. So the problem is if I log out again and I go back to the homepage, I'm still able to see the to-do app, right? I wanna secure this. So I'm gonna go to my routes file and I'm gonna use a component called private, and I'm going wrap all of the routes that I want to be private. So that's everything from here, the to-do page, the count page and the homepage. The not-found page will be outside of what's authenticated and the login page, obviously, needs to be outside of what's authenticated. And then I'm gonna say the unauthenticated, when I'm not authenticated and I go to any of these pages, I want that to redirect to the login page, where I'm just using the same name that's right here. And now, if I save that, you can see, it redirected me over to the login page. Okay, and I'll show you that again. If I go to the root page, it redirects me over. So now, I'm gonna log in and get an authenticated user here. And now when I go back to the homepage, there we go. Now I have access. Now, we also have some things on the backend that make this really easy too because I can still run the Graph QL query. This is still gonna work. I can still run this, right? But I need this to work on the backend as well. And so we've added, for you, in the API side, this lib off directory here and you get access to this require auth function. So in your services implementation, then you can simply import... Hold on, require auth from source lib auth. And then, in any of your functions here, you could just make a simple call to use require auth and that will check that you have an authenticated user. I'm out of time so I'm not gonna show you exactly how to do that, but you would just edit right here and that would secure the backend. So that's what I wanted to show you today. And if you would like to learn more about Redwood and everything that we can do, you go to Redwoodjs.com. We have a forum, we have a chat, we have a newsletter, we have a really extensive tutorial. We have documentation, cookbook recipes, everything that you need to get started with Redwood. And I should say that if you want free stickers, then we will send you anywhere in the world on 100% for free, a set of three stickers. All you have to do is go to redwoodjs.com/stickers and fill out the form. Thanks so much for watching.