INTRODUCTION
Redwood.js & Full Stack
Redwood is an opinionated, full-stack, serverless web application framework that enables developers to build and deploy Jamstack applications with ease.
One of the main Redwood’s philosophies is power in standards, so it makes decisions about which technologies to use, organizing your code into files, and naming conventions.
Fauna
Fauna is a powerful and fully featured serverless database. With a web-native GraphQL interface that supports custom business logic, consistent data and total freedom from database operations since its fully managed.
By using Fauna in this application’s architecture, we are able to simplify code, reduce costs and ship faster.
The Application.
In this tutorial, we will be developing a simple job board.
Getting started.
We'll use yarn (yarn is a requirement) to create the basic structure of our app:
yarn create redwood-app ./job-board
You'll have a new directory “job-board” containing several directories and files. Change to that directory.
Redwood File Structure
Let's take a look at the files and directories that were created for us (config files have been excluded for now):
In the web folder.
In the api folder.
From the above structure, it's clear how Redwood makes decisions for you about which technologies to use, how to organize your code into files, and how to name things.
It can be a little overwhelming to look at everything that’s already been generated for us. The first thing to pay attention to is that Redwood apps are separated into two directories:
- web directory for the application frontend.
- api for the backend
Setting Up Fauna.
Create Fauna Account
To hold all our application’s data, we will first need to create a database. Fortunately, this is just a single command or line of code, as shown below. Don’t forget to create a Fauna account before continuing!
NOTE:
Fauna offers a generous free tier for you not only to test your app but you can also use this to build your small hobby apps.
Fauna Shell
Fauna’s API has many interfaces/clients, such as drivers in JS, GO, Java and more, a cloud console, local and cloud shells, and even a VS Code extension! For this article, we’ll start with the local Fauna Shell, which is almost 100% interchangeable with the other interfaces.
npm install -g fauna-shell
After installing the Fauna Shell with npm, log in with your Fauna credentials:
$ fauna cloud-login
Email: email@example.com
Password: **********
Creating the Database
Now we are able to create our database.
To create your database enter the fauna create-database command and give your database name.
fauna create-database job-board
To start the fauna shell with our new database we’ll enter the fauna shell command followed by the name of the database.
fauna shell job-board
GraphQL on Fauna.
One of the many great features of Fauna is its first class support for GraphQL.
One big advantage of using GraphQL on Fauna is that it allows you to define a schema and it will do its magic in the background to ensure your entities, collections and their relationships are created. All you need to provide is a schema.
Writing Schema.
We will use the below schema for our application.
Create a file in the your directory and name it job-board-schema.gql
type Listing {
title: String!,
description: String!,
location: String!
company: String!
}
type Query{
listings: [Listing]
}
Importing Schema.
On your database page on Fauna, go to the GraphQL tab on the left side bar. You will see a page similar to the one above.
Click on “Import Schema” then select the sample schema from your application directory.
This will automatically create the required collection, FQL queries to fetch and create data together with indexes that will be required for query and mutation capabilities.
Testing the schema (Graphiql) and Writing Queries.
Now that we have successfully imported our GraphQL schema definition, we can now test if our GraphQL API is working by running the following mutations to seed initial data to our database.
mutation {
createListing(data:{
title: "Python Developer",
description: "Experience Python developer with experience in Flask and FaunaDB",
location: "Kisumu",
company: "Dala Systems"
}),
{
_id
title
}
}
Then
mutation {
createListing(data:{
title: "Node.js Developer",
description: "Outstanding Node.js with 3+ Years experience in developing serverless apps",
location: "Cape Town",
company: "Sugar Systems"
}),
{
_id
title
}
If the above mutations execute successfully, they should return the _id
and title
of the newly created job listing.
Moreover, you can further confirm this by running the following query should list all the available jobs posted to the collection.
query {
listings {
data{
title
description
company
}
}
}
4. Setting up Redwood to work with Fauna.
When generating the project using yarn, the default database is an instance of the PrismaClient. Since Prisma doesn’t support Fauna currently, we will make use of graphql-request to query the GraphQL API we created above.
yarn add graphql-request graphql
In the api/src/lib/db.js
add the following code.
import { GraphQLClient } from 'graphql-request'
export const request = async (query = {}) => {
const endpoint = 'https://graphql.fauna.com/graphql'
const graphQLClient = new GraphQLClient(endpoint, {
headers: {
authorization: 'Bearer <FAUNADB_KEY>'
},
})
try {
return await graphQLClient.request(query)
} catch (error) {
console.log(error)
return error
}}
To access our Fauna database through the GraphQL endpoint we’ll need to set a request header containing our database key as provided by Fauna.
Obtaining your Fauna database key.
Head over to your database’s Fauna Shell and create a key using the following command.
CreateKey({
name: "redwood-app",
role: "server"
})
# Example result.
# NOTE: Make sure you copy and store the secret!
# {
# ref: Ref(Keys(), "280185139463529993"),
# ts: 1603464278974000,
# role: 'server',
# secret: 'fn…………………………………………………..',
# hashed_secret: ...
# }
After this you will also be required to create a api/src/graphql/listings.sdl.js
file and add the following contents.
import gql from 'graphql-tag'
export const schema = gql`
type Listing {
title: String!,
description: String!,
location: String!,
company: String!
}
type ListingsPage {
data: [Listing]
}
type Query{
listings: ListingsPage
jobs: [Listing]
}
`
Redwood.js Services
In our api/src/services directory we'll create a listings directory with a file called listings.js. Services are where Redwood centralizes all business logic. These can be used by your GraphQL API or any other place in your backend code. The listings function is querying the Fauna GraphQL endpoint and returning our posts data so it can be consumed by our ListingsCell.
// api/src/services/listings/listings.js
import { request } from 'src/lib/db'
import { gql } from 'graphql-request'
export const listings = async () => {
const query = gql`
{
listings{
data {
title
description
location
company
}
}
}
`
const data = await request(query, 'https://graphql.fauna.com/graphql')
return data['listings']
}
5. Writing the application.
Now that our application is already set up, we can start creating pages. We’ll use the generate page command to create a home page and a folder to hold that page. The commands for generating a page is ‘generate’ although we can use g to save some typing.
yarn rw g page home /
On this page we will list all available job listings posted in our Fauna collection.
Redwood.js Cells.
In Redwood.js, cells provide a simpler and more declarative approach to data fetching. They contain the GraphQL query, loading, empty, error, and success states, each one rendering itself automatically depending on what state your cell is in.
Create a folder in web/src/components called ListingsCell
and inside that folder create a file called ListingsCell.js
.
Inside that file add the following code.
// web/src/components/ListingsCell/ListingsCell.js
export const QUERY = gql`
query LISTINGS {
listings {
data {
title
description
location
company
}
}
}
`
export const Loading = () => <div>Loading Job Listings...</div>
export const Empty = () => <div>No Job Listings yet!</div>
export const Failure = ({ error }) => <div>Error: {error.message}</div>
export const Success = ({ listings }) => {
const {data} = listings
return (
<div style={{maxWidth:"50%", margin:"auto"}}>
{data.map(listing => (
<>
<div style={{border: "black 1pt solid", padding: ".7em"}}>
<h4>{listing.title}</h4>
<p>{listing.description}</p>
<p>Organization: {listing.company} <br/>
Location: {listing.location}</p>
</div>
</>
))}
</div>
)
}
Now in our web/src/pages folder we will see a HomePage folder with a file named HomePage.js.
In this file add the following code.
import ListingsCell from 'src/components/ListingsCell'
const HomePage = () => {
return (
<>
<h1>Fauna Redwood.js Job Board</h1>
<ListingsCell />
</>
)
}
export default HomePage
6. Testing App.
To preview if our app is working, we will need to run the following command in your terminal.
yarn rw dev
Then visit http://localhost:8910/ you should be greeted with a preview as shown below.
7. Deploying to Netlify.
Redwood was created to make full-stack web apps easier to build and deploy on the Jamstack. Now that we have seen what building a Redwood app is like, how about we try deploying one?
To make the app ready for deployment, we can take advantage of the Redwood generator. To do this, run the following code.
yarn rw g deploy netlify
This creates a file at /netlify.toml which contains the commands and file paths that Netlify needs to know about to build a Redwood application.
Next, we need to ensure our application is committed and pushed to Github.
git init
git add .
git commit -m 'First commit'
git remote add origin ...
git push -u origin master
After this, we are going to link Netlify directly to our git repo so that a simple push to main will re-deploy our entire application.
In your Netlify account select the option “New Site from Git” and follow the prompts to link your repo and it should deploy automatically.
At this point, you will discover that the posts aren’t loading. The reason for this is the lack of a Fauna key in your Netlify environment. To resolve this, go to your application page.
In the Build & deploy tab, scroll to the Environment section and add your Fauna database key with the key FAUNA_KEY and save.
Now your application is live and working. When you visit the application URL you should see a similar page.
Upon loading, you will see the job listings.
Conclusion
From this tutorial, we can see how simple it can be to develop a full stack application with Redwood and Fauna which is indeed a powerful database; with a web-native GraphQL interface, which supports custom business logic and integration with the serverless ecosystem. This enables developers to simplify code and ship faster.
Combined together with Netlify, we can have a truly serverless and fully managed full stack application. The advantages we get from this architecture is a great boost to productivity as we can focus on implementing features that make our application and UX unique, instead of getting hung up on the DevOps of servers and databases.
I hope you find Fauna to be exciting, like I do, and that you enjoyed this article. Feel free to follow me on Twitter @theAmolo if you enjoyed this!
BONUS:
All code written for this tutorial can be found in the following Github Repo
Top comments (15)
Great explanation
Thanks. Much appreciated.
I get this error when I localdeploy
Failed to compile.
./src/pages/HomePage/HomePage.js
Module not found: Error: Can't resolve '../../components/ListingsCell' in
Can you help me?
Could you paste the whole error message.
I also hope you are running using
Alternatively you can follow the tutorial using this repo
github.com/brianraila/rw-fauna
In rw-fauna/api/src/lib/db.js
you changed 'Bearer ' to
Bearer ${process.env.FAUNA_KEY}
Oh yes sorry about that.
You should set the env variable FAUNA_KEY to your Fauna database key. But that is specified in the tutorial.
However, the code sample works seamlessly. You can use it as a reference.
Now i get this error
POST /graphql 400 4.578 ms - 1572
api |
api | GraphQLError: Cannot query field "listings" on type "Query".
api |
api |
api | 1 Object.Field
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\graphql\validation\rules\FieldsOnCorrectTypeRule.js:46
api |
api | 2 Object.enter
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\graphql\language\visitor.js:323
api |
api | 3 Object.enter
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\graphql\utilities\TypeInfo.js:370
api |
api | 4 visit
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\graphql\language\visitor.js:243
api |
api | 5 Object.validate
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\graphql\validation\validate.js:69
api |
api | 6 validate
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\apollo-server-core\src\requestPipeline.ts:510
api |
api | 7 anonymous
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\apollo-server-core\src\requestPipeline.ts:296
api |
api | 8 fulfilled
api | C:\Users\christophe\Desktop\Nouveau dossier - Copy\job-board\node_modules\apollo-server-core\dist\requestPipeline.js:5
api |
Did you add the rw-fauna/api/src/graphql/listings.sdl.js file?
And you missed a } on the second mutation
Thanks for the heads up. I will take a look at that. But all the Github code is up to date on that.
Also kindly confirm that you imported the schema to your Fauna database.
I imported the schema and the github code works.
Its just that i can't run it locally.
What exactly is the issue now?
Can you show how to create a page to input the data(jobs) with the graphql?
Great, I will make some time to do that.
Some comments have been hidden by the post's author - find out more