Redwoodjs is an opinionated serverless web framework for Javascript, that allows you to create applications using the JAMstack.
Since it make you follow some code conventions (that will explore in this post) while coding, the developer experience is really lovely and I'm pretty sure after reading this post you will like to try it out!
First Impressions
Topics like: ReactJS, Apollo (GraphQL), Prisma, Storybook, Babel, Webpack, are really popular among web developers, and there's a good reason for that:
They all make the developer experience great!
Don't forget to mention that each tool resolves many other issues on it's own environment
So now, imagine someone describe to you, their React frontend app, statically delivered by CDN, that talks via GraphQL to their backend running on AWS Lambdas around the world, all deployable with just a git push
command?
Really cool uh?, Well this last paragraph it's exactly how RedwoodJS is officially defined, so no more presentations needed, let's start digging into it!.
JAMstack
If you already understand what's the JAMstack you can skip this section and just jump ahead to the Going through the web side section.
JAM stands for JavaScript, API & Markup, and like you can imagine, it means that your app (if it applies) should be just about Markup (HTML) and Javascript (Using the API of the browers) and nothing else.
It's just a way to think about the workflow of an app development, by removing a backend server.
Nowadays the web need apps that are high-performant, have less security risk, and of course, there's a less cost when trying to scale them.
How do you achieve this? By making it small and simple from the begging, which happens to be one features of the JAMstack.
One of the many ways to define the JAMstack that I like more is the one from Mathias Biilmann, CEO & Co-founder of Netlify:
"A modern web development architecture based on client-side JavaScript, reusable APIs, and prebuilt Markup"
It's very likely that you already have interact with one of these apps, or even work in one if you have use any of the static site generators that are out there.
For obvious reasons, the JAMstack doesn't fit for all the apps, so how we can know if our app applies to this stack? Easy, if your app doesn't relies on a tight coupling between client and server, then you are on the right path.
Also, if you are using a server-side CMS (Drupal, Wordpress, etc..) or building a monolithic app with Node, Ruby, etc... the JAMstack is not what you are looking for.
So now you have a basic idea of what the JAMstack is, now let's check RedwoodJS.
Going through the web side
RedwoodJS uses node
and yarn
, so you will need those to be installed on your computer. Check Prerequisites Here
To create an app just run:
yarn create redwood-app ./my-app
Let's start talking about how RedwoodJS organize the code.
Code Organization
By default, a RedwoodJS app has 2 sides: web
and api
my-app
|__api/
|__prisma/
|__migrations/
|__src/
|__functions/
|__graphql/
|__services/
|__dev.db
|__web/
|__public/
|__src/
|__components/
|__layouts/
|__pages/
|__index.css
|__index.html
|__index.js
|__Routes.js
Inside the api
folder, we have 3 more directories:
-
prisma/
: Contains the database schema and the seeds to pre-populate your database. -
migrations/
: This folder will be created after running your first migration, and it contains snapshots of your database. -
src/
: This directory contains another 3 directories inside:-
functions/
: This is where all the labmda functions live, you will find some other files to configure your Graphql API. -
graphql/
: Contains Schema Definition Language (SDL) files of your grapql schema -
services/
: Here you'll find all the business logic of your app, but in an abstract way, so it can be consume across all your app (web & api) in a consist way.
-
Now for the web
folder, we have a more simple structure:
-
public/
: This folder is for static resources -
src/
: This is where all the code for our React app will live:-
components/
: Besides your regular React components, RedwoodJS stores theCells
components in this directory too (we'll explore them later) -
layouts/
: Not much to say here, just use it for all the layouts that your app could have -
pages/
: This pages components, usually are wrapped by a layout component, and it's what the user see when they land on a url.
-
RedwoodJS call these top-level folders (web & api) sides, but underneath they are Yarn workspaces, and of course there are a couple of features around using them.
The CLI
RedwoodJS comes with a handy CLI app that let you run some task, like create an scaffold, create a database, running a migration, and so on.
Here is the list of some of the commands I use more:
-
yarn redwood dev
oryarn rw dev
: Starts a development server -
yarn rw test
: Run test suite using Jest -
yarn rw db save
: Create a new migration -
yarn rw db up
: Run any new migration and update the schema -
yarn rw db seed
: Seed the database -
yarn rw generate <resource>
: A generator for the given resource:cell
component
layout
page
scaffold
sdl
service
We'll look more into the resources later.
Alias Path
Like I've mention, following some code conventions allows us to increase the developer experience and we can see an example with how RedwoodJS handle the routes.
Let's create a new page using the cli:
yarn rw generate page hello /
As you can see, the page generators accepts 2 parameters, one is the name of the page, and the other one is the url of that page.
After running this command, you'll see a new directory inside web/src/pages/
called HelloPage
, as you can see RedwoodJS automatically takes the name you specified, capitalize it and append Page
to create the folder name.
Let's see how our page component looks like:
const HelloPage = () => {
return (
<div>
<h1>HelloPage</h1>
<p>Find me in ./web/src/pages/HelloPage/HelloPage.js</p>
</div>
)
}
export default HelloPage
As you can see, we no longer need to import React at the top of each component so we have more cleaner components.
You'll also notice that all the react component are functional component, and this is because they use React Hooks a lot, you still can use class though, but they don't recommend it.
Now let's take a look to the Routes.js
file:
import { Router, Route } from '@redwoodjs/router'
const Routes = () => {
return (
<Router>
<Route path="/" page={HelloPage} name="hello" />
<Route notfound page={NotFoundPage} />
</Router>
)
}
export default Routes
As you can see, there's a new Route
component that points the /
url to the HelloPage
component. We can also notice a few other things happening on this file:
-
<Router>
&<Route>
component are coming from redwoodjs. -
HelloPage
&NotFoundPage
components are not imported anywhere in this file.
So how does it know where to find this component? Well, since pages are what the user see when they land on a url, its gonna be obvious that all the pages component would need to be imported here, so RedwoodJS automatically import it for you at build time, so you don't need to have a huge Routes.js
filled with import ...
statments.
This feature also works with nested folders, but there's a rule: the have to be uppercase
'src/pages/HomePage/HomePage.js' -> HomePage
'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage
Named routes
Run this to create another page:
yarn rw generate page about
This time we just specify the resource name without the route, when we do this, Redwood automatically uses that same name, to define the route:
const Routes = () => {
return (
<Router>
<Route path="/about" page={AboutPage} name="about" />
<Route path="/" page={HelloPage} name="hello" />
<Route notfound page={NotFoundPage} />
</Router>
)
}
RedwoodJS uses the <Link>
component to handle the navigations between pages, let's take a look on how to use it:
import { Link, routes } from '@redwoodjs/router'
const HelloPage = () => {
return (
<div>
<h1>HelloPage</h1>
<Link to={routes.about()}>See about</Link>
</div>
)
}
The <Link>
components accept a prop called to
, and as you can see we passed it a function from the routes
object. This is a named route function calling this function will generate the correct url, and maybe you already figure it out, but the name of the property inside the routes
object is coming from the name
prop that we set on the <Route>
component:
<Route path="/about" page={AboutPage} name="about" />
Additionally, the to
prop also accepts a string, but a great thing about having this name route, is that we can change the url any time and all the links will be updated without having to do anything else.
Cells
Another good thing about Redwoodjs are Cells
components.
When you work with React, is very common to step into the next scenario:
You have to implement a simple listing page, that fetch data from your server, and then renders a list of items, easy!, but then you start asking yourself, what should I show when there's no items, or when there's been an error while fetching the data, or event what you show to the user while fetching this data!?
All the sudden you have to implement a single listing component that handle up to 4 different states.
The bad part is when you have this listing page for each resource of your app.
Well RedwoodJS found this use case to be very repetitive and that's why they create a Cell
component.
Cells provide a simpler and more declarative approach to data fetching.
That's how RedwoodJS define a Cell
component. Okay, let's see them in action.
The next example would probably looks weird since we have not talk about the api side, but for now, you can just think that there's a grahpql model called post
and all the backend works outta of magic.
We'll talk about the api side on the next post
export const QUERY = gql`
query {
posts {
id
title
body
createdAt
}
}
`
export const Loading = () => <div>Loading...</div>
export const Empty = () => <div>No posts yet!</div>
export const Failure = ({ error }) => (
<div>Error loading posts: {error.message}</div>
)
export const Success = ({ posts }) => {
return posts.map((post) => (
<article>
<h2>{post.title}</h2>
<div>{post.body}</div>
</article>
))
}
Ok, I know there's a lot going on here, so lets just untangle all:
- There's a variable called
QUERY
been exported from this file and it holds a gql query - There are 4 components been exported also:
Loading
,Empty
,Failure
andSuccess
, each of one render a different UI representing the state of the component.
So what happens here, once React has made the render, RedwoodJS will execute the grahpql query from the QUERY
variable while it renders the Loading
component, once the call is done, it will render either Empty
, Failure
or Success
component based on the query response.
Additionally you could use the beforeQuery
and afterQuery
lifecyles to do modify the props before they go to the Success
component.
You can omit the Empty
and Failure
components, and after an empty data response, the Success
component will be rendered or if there's any error will be outputted to the console.
Wrapping up: Web Side
Even though there a lot of libraries and frameworks out there, TBH RedwoodJS looks really unique to me when it comes to a fast development, great developer experience and huge performance for develop web apps using the JAMstack.
The sad part? RedwoodJS is still in alpha version, so it's not production-ready yet, but they have an interesting roadmap, so feel free to try it out or even better contribute to the framework
On the next post we'll look into the API side of the framework and check what RedwoodJS offer us!
Thanks for reading! 👋🏻👋🏻
Top comments (0)