Bring Full-Stack to the JAMstack with RedwoodJS

RedwoodJS uses React, GraphQL and Prisma

Nathan Sebhastian
Bits and Pieces

--

The JAMstack architecture has created a new way of thinking about development workflow. It is a huge leap forward in how developers can write web applications that are easy to write, deploy, scale, and maintain.

Redwood is an alpha-stage React framework that takes Jamstack architecture to the next level by providing a standardized workflow with the ability to deploy your application with a simple git push command.

Built on React, GraphQL, and Prisma, Redwood works with the component-based approach, and added simple conventions and helpers to make your experience even better.

Redwood is opinionated, which means it has its own way of organizing code into files and how to name things. It also comes with its own CLI commands that you can use to make development even easier.

Features of Redwood includes:

  • Opinionated defaults for formatting, file organization, Webpack, Babel, and more.
  • Simple but powerful routing (all routes defined in one file) with dynamic (typed) parameters, constraints, and named route functions (to generate correct URLs).
  • Boilerplate-less GraphQL API with Apollo construction.
  • Cells: a declarative way to fetch data from the backend API.
  • CLI Generators for pages, layouts, cells, SDL, services, etc.
  • Scaffold generator for CRUD operations around a specific DB table.
  • Forms with easy client- and/or server-side validation and error handling.
  • First-class JAMstack-style deployment to Netlify.

A small side-note: Many of us are used to building our React apps using the “Container-Presentational” pattern, to make our components easier to reuse. This pattern is not always advisable.

In the case of JAMstack, you may find yourself looking to reuse “smart components” that both fetch data, from one constant endpoint and display it.

When publishing your components to Bit, consider publishing smart components, as well as UI components, to reuse in your JAMstack websites.

Example: exploring React components published to Bit.dev

How can full-stack be a JAMstack?

A Redwood application is split into two parts: a front-end and a back-end. The front-end part of the application is stored inside /web directory, while the back-end side lives inside /api directory. They are represented as two projects in a single repo that you can deploy separately.

For example, you can deploy the front-end part into Netlify and the back-end part into AWS Lambda. And if you’re building a fully static site that doesn’t need the API layer or database, you can deploy only the front-end part without any problem at all.

Next, let’s see how you can start building application with Redwood

Getting started with Redwood

To develop an application with Redwood, you need to have knowledge about how React, GraphQL, and JAMstack works. You also need to have Node version 12 or later and Yarn version 1.15 or later installed on your machine, or the installation might throw an error.

Once you’re ready, create a new Redwood application with Yarn command:

yarn create redwood-app ./redwoodApp

Once the installation process is finished, you’ll find auto-generated files and directories that make up the structure of a Redwood application:

A Redwood application file structure

At the very top, we have the /web directory and the /api directory. Redwood refers to them as workspaces, and if you need to install a new package, you have to specify where you want to install the package. For example:

yarn workspace web add marked
yarn workspace api add better-fs

There are two directories inside /api directory:

  • The prisma directory which contains the plumbing for the database
  • And /src directory which contains the GraphQL and other back-end related code to handle your business logic

Next, here’s the rundown of /web directory:

  • The /public directory which holds static assets to be copied over to the build
  • The /src directory which holds your React components, layouts, pages, and other front-end related code such as the router and the styling.

Running Redwood application

Now that you’re familiar with Redwood’s file structure, let’s run your Redwood app from the terminal. Move into the newly created redwoodApp directory and run your application using Redwood’s development server:

cd redwoodApp
yarn redwood dev

Once the process is finished, open your http://localhost:8910 to see your Redwood starter page:

Redwood app starter page

Now that you understand how to start developing applications with Redwood, let’s look at Redwood’s top five unique features that make web application development easier for you.

Redwood routing solution

Rather than using React Router or any other readily available solutions, Redwood created its own routing solution that’s partly inspired by Ruby on Rails.

Redwood Router (RR for short) is designed to list all routes in a single file, without any nesting. This means you can easily track which routes map to which pages.

To use RR, you need to import theRouter and Route component from @redwoodjs/router package. RR expects at least one Route with a notfound prop, which will be rendered into view when no other Route match the path prop:

// Routes.js
import { Router, Route } from '@redwoodjs/router'
const Routes = () => (
<Router>
<Route notfound page={NotFoundPage} />
</Router>
)
export default Routes

To create a Route in RR, you need to pass three props into the Route component: path, page, and name:

<Route path="/" page={HomePage} name="home" />

The path prop specifies the URL path to match. The page prop specifies the component to render when the path is matched. The name prop is used to call the Route component from Link component, for example:

const SomePage = () => <Link to={routes.home()} />

Just like with other routing solutions, you can put dynamic parameters into your path prop:

<Route path="/user/{id}" page={UserPage} name="user" />

Then, you can extract the parameters using the useParam hook:

import { useParams } from '@redwoodjs/router'const UserComponent= () => {
const { id } = useParams()
// ...
}

RR also offers a convenient method to convert your parameters. JavaScript will always extract parameters as strings by default. With RR, you can specify route parameter types right in the path props:

<Route path="/user/{id:Int}" page={UserPage} name="user" />

By using :Int annotation on the code above, The Route parameter will only match digits by using regex (/\d+/) and then use Number() to convert the parameter into a number.

Redwood’s routing solution can also be used outside of a Redwood application. If you’re interested to know more, be sure to check out Redwood Router documentation to learn about its full feature.

Data fetching with Redwood Cells

On a traditional web application, a framework typically retrieves data from the API synchronously, which means the view layer will only be processed after the data fetching process is finished. The process is sequenced from the back-end to the front-end.

By contrast, modern web application retrieves data asynchronously. Both the front-end and back-end parts of your application will process things independently from each other. This approach enables the front-end to load without waiting for the data fetching process.

For most modern web applications, you need to display some sort of loading indicator until a response is received from the API.

Redwood attempt to standardize the sequence of retrieving data from an API, showing a loading placeholder, and then displaying the result into a simpler and reusable code pattern.

This simpler approach to data fetching is named Cells. When you create a Cell, you export several named constants, and Redwood will take the process from there.

A single Cell file typically will look like this:

export const QUERY = gql`
query {
blogPosts {
id
}
}
`
export const Loading = () => <div>Loading...</div>export const Empty = () => <div>Empty</div>export const Failure = ({ error }) => <div>Error: {error.message}</div>export const Success = ({ blogPosts }) => {
return JSON.stringify(blogPosts)
}

The Cell contains the GraphQL query, loading, empty, error, and success states, each one rendering itself automatically depending on what state your cell is in.

Finally, you just need to include the Cell into your page component in order to use it:

import BlogLayout from 'src/layouts/BlogLayout'
import BlogPostsCell from 'src/components/BlogPostsCell'
const HomePage = () => {
return (
<BlogLayout>
<BlogPostsCell />
</BlogLayout>
)
}
export default HomePage

To learn more, you can visit the Redwood Cells documentation.

Structured business logic with Services

To keep things neat and structured, Redwood puts all of your business logic inside the /services directory on the back-end site. A typical Service file is a regular JavaScript file for interacting with the database:

// api/src/services/posts/posts.jsimport { db } from 'src/lib/db'export const posts = () => {
return db.post.findMany()
}
export const post = ({ id }) => {
return db.post.findOne({
where: { id },
})
}
export const createPost = ({ input }) => {
return db.post.create({
data: input,
})
}
export const updatePost = ({ id, input }) => {
return db.post.update({
data: input,
where: { id },
})
}
export const deletePost = ({ id }) => {
return db.post.delete({
where: { id },
})
}

The Service file can be used by GraphQL Schema Definition Language (SDL) which is then consumed by the Cells. Here is a simple way to map the process:

Interaction between service and SDL: Source

Redwood will automatically import and map resolvers from the corresponding services file onto your SDL, so make sure the name of your SDL file and your service file is the same (like posts.sdl.js and posts.js)

For full documentation on how Redwood works with data, you can visit the documentation.

Redwood command line Generators

Inspired by Rails command line tools, Redwood has its own command line tools to help you create the right file inside the right directory.

The Redwood CLI has two entry-point commands:

  1. redwood (alias rw), which is for developing an application, and
  2. redwood-tools (alias rwt), which is for contributing to the framework.
Some of Redwood’s Terminal command

The Redwood CLI tool is used to let the computer do generic repetitive tasks for you, such as generating files (pages, components, cells, etc.) or migrating the database so you can work on what makes your application special.

Working with forms in Redwood

Redwood knows that working with forms in a React application is tear-jerking. That’s why it provides several helpers to make your life easier when working with forms.

With Redwood’s form wrapper, you simply need to import the right input fields from the module and put the necessary styling and validation as props:

// A sign in form import { Form, Label, TextField, PasswordField, FieldError, Submit } from "@redwoodjs/web"export default () => {
const onSubmit = (data) => {
console.info(`Submitted: ${data}`)
}
return (
<Form onSubmit={onSubmit}>
<Label name="username" errorStyle={{ color: "red" }} />
<TextField
name="username"
errorStyle={{ borderColor: "red" }}
validation={{ required: true }}
/>
<FieldError name="username" errorStyle={{ color: "red" }} />
<Label name="password" errorStyle={{ color: "red" }} />
<PasswordField
name="password"
errorStyle={{ borderColor: "red" }}
validation={{ required: true }}
/>
<FieldError name="password" errorStyle={{ color: "red" }} />
<Submit>Send</Submit>
</Form>
)
}

The Form component provided by Redwood is simply a wrapper around react-hook-form, and if you find Redwood’s form helpers aren’t flexible enough, you can always use react-hook-form directly, or any other form builder that works with React like Formik.

Conclusion

Even though Redwood is still on the late alpha stage and isn’t suited for production use yet, it has additional goodies for developers aside from the top five features we’ve explored above. For example:

Going forward, we can expect Jamstack architecture to be the standard way of building web applications. Redwood believes in the future of Jamstack, and it provides a framework to help developers realize that future sooner by introducing a set of standards and conventions.

If you’re interested in learning more, check out the official Redwood documentation

Learn More

--

--

Web Developer and Writer. Sharing what I learn on productivity and success.