
Building Fullstack Jamstack Applications with RedwoodJS - JS Monthly
Anthony Campolo outlines the origins of RedwoodJS and the concepts it introduces by demonstrating how to create and deploy a blog in less than 20 minutes
Episode Description
Anthony Campolo demonstrates how to build and deploy a full-stack blog application using RedwoodJS, covering its architecture, generators, and deployment to Netlify.
Episode Summary
Anthony Campolo walks through building a full-stack web application using RedwoodJS, a framework created by Tom Preston-Werner and Peter Pistorius that combines React, GraphQL, and Prisma into a cohesive monorepo structure. He begins by explaining Redwood's architecture — a web folder for the React frontend and an API folder for the backend — and introduces key concepts like pages, layouts, cells (a Redwood-specific pattern for handling data fetching states), and the schema definition language for GraphQL. The live demo follows the creation of a blog application from scratch, starting with generating pages and layouts via Redwood's CLI commands, then defining a Post model in Prisma, running database migrations, and using the scaffold generator to produce a full CRUD interface. He shows how cells manage loading, empty, failure, and success states, and how to customize the GraphQL query to display post data on the homepage. The session concludes with deploying the app to Netlify for the frontend and Heroku Postgres for the database, including setting environment variables and connection pooling. During a Q&A segment, Campolo distinguishes Redwood from Next.js and Gatsby by emphasizing its built-in database conventions via Prisma, discusses its ongoing TypeScript migration, and addresses questions about incremental builds and the roadmap toward V1.
Chapters
00:00:00 - Introduction to RedwoodJS and Its Architecture
Anthony Campolo introduces himself and RedwoodJS, describing it as a full-stack serverless framework for the Jamstack — or more simply, a program for generating web apps quickly. He covers the framework's origins with Tom Preston-Werner and Peter Pistorius, who developed it out of their experience building Chatterbug, and mentions the expanded core team of twelve contributors.
The talk then moves into the high-level architecture of a Redwood project: a monorepo containing a web folder and an API folder. Campolo explains the frontend structure, comparing it to familiar tools like Create React App and Gatsby, and introduces the generator commands that scaffold pages and layouts. He also introduces the concept of cells, Redwood's approach to declarative data fetching with four distinct states: loading, empty, failure, and success.
00:04:56 - Backend Architecture: Prisma, GraphQL, and Services
Campolo shifts to the backend, starting with the Prisma schema file where the database provider and data models are defined. He walks through a Post model with fields for title, body, ID, and creation timestamp, explaining how Prisma acts as an ORM to handle database interactions without writing raw SQL.
He then covers the GraphQL schema definition language, showing how queries and mutations map to CRUD operations, and explains how Redwood's services layer wires resolvers to Prisma. The key takeaway is that most backend boilerplate is auto-generated, so developers primarily focus on the frontend and let Redwood's generators handle the wiring between GraphQL and the database.
00:07:36 - Live Demo: Creating Pages, Layouts, and Setting Up the Project
The live coding portion begins with Campolo initializing a new Redwood project, starting the development server, and generating a homepage and an about page using CLI commands. He demonstrates how each generated page includes companion files for Storybook and Jest testing.
He then generates a blog layout component containing navigation links, imports it into both pages, and shows the resulting navigation working in the browser. Along the way, he sets up a Git repository and pushes the project to GitHub, laying the groundwork for the deployment step later in the talk.
00:13:07 - Building the Blog: Data Model, Migrations, and Scaffold
Campolo defines the Post model in the Prisma schema and runs database migration commands to set up the SQLite tables. He then runs the scaffold generator, which produces an entire CRUD interface for blog posts — including pages for listing, creating, editing, and deleting posts — in a single command.
He demonstrates the scaffolded interface in the browser, showing the loading and empty states of a cell before creating a sample post, editing it, and deleting it. He then generates a custom BlogPostCell, imports it into the homepage, expands the GraphQL query to include title, body, and creation date, and applies basic styling to display the posts cleanly on the home page.
00:18:41 - Deploying to Netlify and Heroku Postgres
With the blog functional locally, Campolo sets up deployment by running Redwood's Netlify deploy generator, which creates the necessary build configuration in a netlify.toml file. He switches the database provider from SQLite to Postgres and pushes the updated code to GitHub.
On the hosting side, he walks through creating a new Netlify site linked to the GitHub repo and provisioning a free Heroku Postgres database. He copies the database URL into Netlify's environment variables, appends a connection pool limit, and retriggers the build. Once deployed, he confirms the live site renders correctly with the empty state on the posts page and a working homepage.
00:22:52 - Q&A: Redwood vs Next.js, TypeScript Support, and Incremental Builds
During the build wait, audience members ask how Redwood compares to Next.js and Gatsby. Campolo explains that Redwood's key differentiator is its built-in database conventions through Prisma, offering a true full-stack experience rather than just server-side rendering without database opinions. He positions Redwood as adding more structure on top of React for developers who find its flexibility overwhelming.
Questions about TypeScript support reveal that the framework is nearly fully converted from JavaScript, with ongoing piecemeal migration work. On incremental builds, Campolo notes this is planned for the V1 release targeted for Q1, alongside pre-rendering capabilities. He also mentions that some developers are experimenting with hybrid setups, using Redwood's API layer paired with a Next.js or Gatsby frontend.
Transcript
00:00:00 - Anthony Campolo
Building Full Stack Jamstack Applications with RedwoodJS. My name is Anthony Campolo, and if you want to find me online, my general handle is AJCWebDev — on Twitter, Dev.to, or GitHub. Redwood is what we call a full-stack serverless framework for the Jamstack.
00:00:26 - Anthony Campolo
There are a lot of buzzwords there. The simpler definition I like to use is that Redwood is a program for generating web apps — it's for interactive, data-driven applications that can be built really quickly. It was created by Tom Preston-Werner and Peter Pistorius. They were originally working together on Chatterbug, a language learning platform, using GraphQL and React and struggling with how to architect an application that way. This framework emerged out of conversations between them about what kind of architecture they were looking for. They brought on Rob Cameron, who has helped with the tutorial and many other parts of the framework — he's also working on repeater.dev. David Price is the general community manager for the project. That was the original Core 4, but now there's a larger expanded team of twelve people. The project is growing, with a lot of people who are involved and really passionate about it. This is the big picture view of what Redwood is.
00:01:55 - Anthony Campolo
The really important stuff is on the left, where it says "Redwood codebase," and you have a frontend and a backend. Your project is a monorepo with two main folders: a web folder and an API folder. Looking at the web folder first — if you've ever used Create React App, Gatsby, or Next.js, it's a similar setup with pages and layouts, and there's an index.js and index.html to load your single-page application. Pages are the main way you'll organize your project. With each page there's a command shown underneath — that's what generates the page you see here. yarn redwood — that's what RW stands for — is how all the commands start, and g is short for generate. We'll see a lot of different generate commands. We can also generate layouts — a layout would be something like a navigation bar you want to persist as you move from page to page, or a footer with a copyright notice at the bottom.
00:03:31 - Anthony Campolo
That's the idea. And then there's a cell. This is a newer idea that Redwood has introduced, based around data fetching. We're using a lot of GraphQL here, and in the top right we're writing a GraphQL query for our posts — specifically for the ID of each post. We write that query and then there are four different states our data can be in: loading while we're waiting to receive it, empty if there's no data, failure if there was an error, and success, where you get the posts and can do whatever you want with them.
That was the frontend. The backend is where we have Prisma. Prisma is an ORM, or a query builder — a way to talk to our database and handle a lot of the SQL for us. We also have a Functions folder for serverless functions and a GraphQL folder for our SDL. We're going to create a whole backend that speaks GraphQL.
00:04:56 - Anthony Campolo
This first defines our database as SQLite, sets it to an environment variable called DATABASE_URL, generates a Prisma client, and creates models. We have a Post model here representing a blog post — it has a title, a body, an ID that's assigned automatically starting at one, and a creation timestamp. If you've never used GraphQL before, some of Redwood might be a little foreign, but it's actually a good way to get introduced to GraphQL concepts — the schema definition language being one of the biggest ones.
This is our schema. Just as we defined the Post model earlier, we're also defining it here with ID, title, body, and created_at. We're also defining our query: it's for posts and returns an array of each individual post we have. You can also query for a single post by providing its ID.
00:06:16 - Anthony Campolo
That's for our mutations. Not only can you query for data with GraphQL, you can also create, update, or delete things. It can handle all the CRUD capabilities through GraphQL.
Now, services. This is how the backend resolvers are written and how they talk to Prisma. We're importing db, which is the Prisma client, and then using db.post.findMany — that's the Prisma command to get all posts — or findOne. The same applies for write operations: create, update, and delete. This is what wires up all of your backend into one GraphQL handler. Most of this backend stuff you don't really have to worry about — it all gets generated for you. You don't write any of this code; I'm just showing it for context. Most of what we'll be writing will be in the frontend, and the generators create a lot of things for us.
00:07:29 - Anthony Campolo
Cool.
00:07:29 - Anthony Campolo
So that's the slides. Now let's take a look at the project.
00:07:36 - Anthony Campolo
First off, let me hide some stuff on my screen. There we go. Okay.
00:07:44 - Anthony Campolo
All I've done so far is create this project. I ran yarn create redwood-app ./ — the ./ is the project name. The setup gives you some info about the community and where to find resources.
00:08:07 - Anthony Campolo
This is our project. Let's get our development server started.
00:08:16 - Anthony Campolo
We run yarn redwood dev and our development server starts on localhost 8910. It's set up to watch our project, so changes will automatically update. First we'll get the splash page saying "Hello, this is Redwood," and then we'll run a command to
00:08:47 - Anthony Campolo
create our first homepage. There we go — welcome to RedwoodJS.
00:08:55 - Anthony Campolo
Now that we've got this going, let's also set up a Git repository, because we're going to deploy this. This will initialize our Git repository, add everything, and commit. We're also going to create a
00:09:21 - Anthony Campolo
repository over here, which will be our RW JS Monthly.
00:09:31 - Anthony Campolo
This is just going to be a blank repository that we're going to
00:09:35 - Anthony Campolo
push our project into. We'll set the remote and push to origin. If we go back and refresh, we'll see our project.
00:10:00 - Anthony Campolo
This is just a blank Redwood project that we've generated. Now let's start building something. The first command we'll run is yarn redwood generate page, and we're going to generate a homepage at the root route, which
00:10:25 - Anthony Campolo
is the forward slash. And then it's going to remove
00:10:28 - Anthony Campolo
this page and automatically update it with our first homepage.
00:10:35 - Anthony Campolo
This was created in our web folder at src/pages/HomePage.
00:10:49 - Anthony Campolo
So if we make a change and
00:10:51 - Anthony Campolo
we save, we'll see the change on the right. Let's generate another page.
00:10:59 - Anthony Campoloyarn redwood generate page about. This time we don't need to specify the route like we did with the forward slash, because we want it to default to the about route. You'll also notice we're creating a couple of other files — each page comes with a stories file for Storybook and a test file for Jest. This is now our about page. If we go to /about, we
00:11:35 - Anthony Campolo
have our about page.
00:11:37 - Anthony Campolo
We want to create a layout that will wrap both pages and allow navigation between them. We run yarn redwood generate layout and we're generating a blog layout. It will not be in pages — it'll
00:11:53 - Anthony Campolo
be in layouts/BlogLayout.
00:11:57 - Anthony Campolo
This layout will have links to Home and About. Once you've created it, you have to import the blog layout into your pages.
00:12:20 - Anthony Campolo
So let's go back to our homepage
00:12:23 - Anthony Campolo
and we're importing the blog layout and wrapping our content with it. We have <BlogLayout> and then </BlogLayout>. For the about page we have to import the blog layout there as well, and then we'll be able to navigate between the two.
00:12:53 - Anthony Campolo
About page. If we click that, go to Home, go to About — there we are. Pretty cool.
00:13:07 - Anthony Campolo
Now we're going to create our Post model in schema.prisma.
00:13:18 - Anthony Campolo
We'll delete this and delete that.
00:13:24 - Anthony Campolo
Our Post model is what we looked at on the slides. Once we do that, we run yarn redwood db save and
00:13:35 - Anthony Campolo
then create posts in quotes.
00:13:38 - Anthony Campolo
This creates a migration — the series of SQL steps that will be applied to the database to set up tables for our blog posts. This creates a migrations folder you can look at if you're interested. To apply the migration, you run yarn redwood db up. This gets your database up and ready to go. The yarn redwood generate scaffold command will create essentially our entire blog interface, allowing us to create, edit, and manage posts. Once this finishes, we will run
00:14:31 - Anthony Campolo
the scaffold command. Let's go back to Home real quick, just
00:14:40 - Anthony Campolo
to show what came out of this — it gives you exactly what was created. It shows the data model and the commands that were run. It shows you where the migration is
00:14:55 - Anthony Campolo
and all that stuff.
00:14:58 - Anthony Campolo
This creates a lot of files — too much to go into in the span of this talk. But I highly recommend poking around in it; it's a good way to understand how a project is structured. What's nice is you can just run the command and get it all set up. Now we're
00:15:22 - Anthony Campolo
going to navigate to /posts and see our blog posts interface.
00:15:34 - Anthony Campolo
When I was showing you the cells, I mentioned the different states your data can be in. Right now you see it says "loading" — it's checking the database to see if we have any posts. Once it figures out whether we do or not, it'll say "no posts,
00:15:52 - Anthony Campolo
do you want to create one?" Let's enter "JS Monthly." When we do that, we
00:16:02 - Anthony Campolo
now have our post. And if we want to edit the
00:16:07 - Anthony Campolo
post, we can edit it. And if we want to delete a
00:16:13 - Anthony Campolo
post, we can do that as well. It'll give you a little warning asking
00:16:18 - Anthony Campolo
if you really want to delete the post. Let's get one more post up here.
00:16:27 - Anthony Campolo
Now we want to create a cell that will render all of our blog posts on the homepage.
00:16:40 - Anthony Campolo
This will go into the components folder as BlogPostCell.
00:16:51 - Anthony Campolo
We want to query for posts, not blogPosts, because that's what our schema uses.
00:17:00 - Anthony Campolo
So if we do that — posts and posts.
00:17:07 - Anthony Campolo
Right now we won't see anything, because just like the blog layout, once you've created it you have to import it into the page you want it in.
00:17:21 - Anthony Campolo
We'll import this into our homepage, like so.
00:17:31 - Anthony Campolo
Now we have our blog layout, and we have BlogPostCell.
00:17:38 - Anthony Campolo
Get that right there. Okay.
00:17:45 - Anthony Campolo
What we're seeing now is only the ID, and the type of object, which is a post. The reason is that in our cell, we're only querying for the ID. We also want the title and the
00:18:05 - Anthony Campolo
body and the created_at.
00:18:12 - Anthony Campolo
Now we can see everything. But if we want to style it a bit, we can do that here. In the Success state, this maps over each post, creates a title in an H2, puts the body in a paragraph, and creates a small div
00:18:38 - Anthony Campolo
for the creation time.
00:18:41 - Anthony Campolo
There is our blog. Now what we really want to do is put this blog on the internet. This sets up our deployment for Netlify — there's also a similar command for Vercel. This is really nice because it gives you the build steps automatically in a netlify.toml. While that runs, the only other configuration you need is to change your database from SQLite to Postgres.
00:19:22 - Anthony Campolo
That's all you gotta do.
00:19:25 - Anthony Campolo
This is the netlify.toml that was created — it has a build command, sets up our database URL, and has a folder
00:19:33 - Anthony Campolo
to publish your assets and redirects. Now that is all set up,
00:19:39 - Anthony Campolo
you don't want to forget to
00:19:42 - Anthony Campolo
push all this. We'll commit and push that up to the
00:19:49 - Anthony Campolo
repo we created at the beginning. We'll use Netlify to deploy
00:19:58 - Anthony Campolo
the frontend and Heroku for the Postgres database.
00:20:05 - Anthony Campolo
All this is on the free tier,
00:20:06 - Anthony Campolo
which is pretty nice. We'll go to New Site, select
00:20:12 - Anthony Campolo
GitHub,
00:20:15 - Anthony Campolo
type in your repo and select
00:20:22 - Anthony Campolo
the repo. It will automatically include the build command from the netlify.toml. That'll take a little while to deploy.
00:20:35 - Anthony Campolo
While that's going, let me switch over here. Create new app, call it "RedwoodJS Monthly DB," create the app. Once you've created the app — I need to re-authenticate.
00:21:02 - Anthony Campolo
Once you've created the app, go to Resources and then
00:21:07 - Anthony Campolo
enter "postgres" in the Add-ons search.
00:21:16 - Anthony Campolo
There are a lot of different options.
00:21:18 - Anthony Campolo
We can pick the Hobby Dev free tier and submit the order form.
00:21:24 - Anthony Campolo
To connect the two, we'll use environment variables. These are keys you want to guard carefully — after these presentations, I tear down these projects. It generated a DATABASE_URL
00:21:42 - Anthony Campolo
environment variable with the Postgres URL. You go to your site settings and
00:21:51 - Anthony Campolo
go to Build & Deploy, scroll down, and you'll see the Environment section with Environment Variables.
00:22:02 - Anthony Campolo
Type in the name DATABASE_URL.
00:22:07 - Anthony Campolo
There's one other thing you don't want to forget: set the connection pool limit. This ensures your app isn't opening multiple connections to your database.
00:22:24 - Anthony Campolo
We're going to set the connection limit to one.
00:22:31 - Anthony Campolo
You append that right to the end of the database URL.
00:22:36 - Anthony Campolo
Now we're going to save.
00:22:38 - Anthony Campolo
That sets our environment variable.
00:22:42 - Anthony Campolo
Let's see if this is still deploying. That is still going.
00:22:52 - Anthony Campolo
Are there any questions for me while this is building out?
00:23:00 - Audience Member
Yeah, I have a couple, Anthony.
00:23:02 - Anthony Campolo
Cool, hit me with them.
00:23:09 - Audience Member
I suppose a couple of broad questions. Why this project over tools like Next.js and Gatsby that already exist? I can see it has a few nice-to-haves, but is there a single selling point you would give someone?
00:23:37 - Anthony Campolo
I would say it's giving you the full-stack experience. With something like Gatsby or Next.js, they're really incredible tools and you can create very sophisticated client-side applications. But with Redwood, you get conventions for talking to your database that are coherent throughout your whole project. A lot of people who have tried to learn React struggle with the general lack of structure, because React is very flexible and minimal — it's mostly just the view layer. I see these different frameworks as each building out more and more abstractions on top. If you want to go beyond the static content you'd create with Gatsby, or beyond what you can do with Next.js — because Next.js still doesn't really have any opinions about the database. Even though people say it's "full stack," it has a server side because of universal rendering, but it doesn't really have a database opinion.
00:24:50 - Anthony Campolo
Whereas with Redwood you get Prisma, which is a full ORM — kind of like Active Record. That's the difference as I see it. Right now our site is live, but we're going to have to retrigger it.
00:25:02 - Anthony Campolo
I'll take one more question real quick.
00:25:05 - Anthony Campolo
Right now the site will be broken because we need to retrigger the build to get the environment variables to take effect. We'll do that, and you want to wait for it to finish the first time because of the cache.
00:25:24 - Anthony Campolo
Do you have other questions?
00:25:27 - Audience Member
I noticed a lot of the files you were demonstrating were JS. Does it support TypeScript?
00:25:37 - Anthony Campolo
Yeah, it's getting to the point where almost the entire framework has been converted over to TypeScript. It's been a gradual process because it was originally written in JavaScript, and a lot of people wanted TypeScript support — both for writing their apps and for contributing to the framework. So it's been piecemeal, breaking pieces off and making them TypeScript compatible. That work is ongoing, but we're getting pretty close. If you're into TypeScript, we're almost across the finish line in terms of being fully compatible. But right now you can write TypeScript and it'll work.
00:26:21 - Audience Member
Does it support incremental builds?
00:26:24 - Anthony Campolo
That's a good question. Right now it does not, because with incremental builds it's a question of whether they need to be in the framework or at the provider level, and how much integration there is between frameworks and providers in terms of who's doing the build. For Gatsby, they built their own cloud to handle their own incremental builds. Right now we're not doing any incremental build process, but that — along with pre-rendering and other things — is being worked on for V1. We're aiming for V1 in the first quarter, and that's where we'll iron out those last bits. Right now it's about stabilizing the technology and getting all the pieces to fit together. In terms of getting a project up and running fast, it's really good. Some people are combining it with a Next.js frontend.
00:27:32 - Anthony Campolo
It's really interesting — they use the Redwood API and attach a Next.js or Gatsby frontend. There are these different hybrid solutions being created.
00:27:48 - Anthony Campolo
This looks like it's good to go. If we check out our deployment and go to /posts, we should see it working.
00:28:04 - Anthony Campolo
Now it says "empty" instead of giving us that error.
00:28:13 - Anthony Campolo
If we go back to our homepage, there is your blog.