DEV Community

Cover image for How to create a job board with Fauna and Redwood.js
Amolo
Amolo

Posted on • Updated on

How to create a job board with Fauna and Redwood.js

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
Enter fullscreen mode Exit fullscreen mode

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.

Alt Text

In the api folder.

Alt Text

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:

  1. web directory for the application frontend.
  2. 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
Enter fullscreen mode Exit fullscreen mode

After installing the Fauna Shell with npm, log in with your Fauna credentials:

$ fauna cloud-login

Email: email@example.com
Password: **********
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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]
}
Enter fullscreen mode Exit fullscreen mode

Importing Schema.

Alt Text

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
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
Enter fullscreen mode Exit fullscreen mode

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
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
  }}
Enter fullscreen mode Exit fullscreen mode

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: ...
# }
Enter fullscreen mode Exit fullscreen mode

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]
}
`
Enter fullscreen mode Exit fullscreen mode

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']
}

Enter fullscreen mode Exit fullscreen mode

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 /
Enter fullscreen mode Exit fullscreen mode

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>
 )
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

6. Testing App.

To preview if our app is working, we will need to run the following command in your terminal.

yarn rw dev
Enter fullscreen mode Exit fullscreen mode

Then visit http://localhost:8910/ you should be greeted with a preview as shown below.

Alt Text

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

Alt Text

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.

Alt Text

Upon loading, you will see the job listings.

Alt Text

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

Discussion (15)

Collapse
jiftuq profile image
chris

Great explanation

Collapse
amolo profile image
Amolo Author

Thanks. Much appreciated.

Collapse
jiftuq profile image
chris

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?

Thread Thread
amolo profile image
Amolo Author

Could you paste the whole error message.
I also hope you are running using

yarn rw dev
Enter fullscreen mode Exit fullscreen mode
Thread Thread
amolo profile image
Amolo Author

Alternatively you can follow the tutorial using this repo
github.com/brianraila/rw-fauna

Thread Thread
jiftuq profile image
chris

In rw-fauna/api/src/lib/db.js
you changed 'Bearer ' to Bearer ${process.env.FAUNA_KEY}

Thread Thread
amolo profile image
Amolo Author

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.

Thread Thread
jiftuq profile image
chris

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 |

Thread Thread
jiftuq profile image
chris

Did you add the rw-fauna/api/src/graphql/listings.sdl.js file?

Thread Thread
Collapse
jiftuq profile image
chris

Can you show how to create a page to input the data(jobs) with the graphql?

Collapse
amolo profile image
Amolo Author

Great, I will make some time to do that.

Some comments have been hidden by the post's author - find out more