Authenticated Serverless CRUD with Netlify Functions and FaunaDB Part 3

Vaibhav Sharma

·

January 22, 2021

Authenticated Serverless CRUD with Netlify Functions and FaunaDB Part 3

In the last parts, we have built a foundation of a Netlify Functions serverless project and set up our Fauna DB Collections, Indexes and Roles. We have also written two APIs, Sign Up and Sign In. And if you have followed the last parts, your project folder should look like this:

.
├── node_modules/
├── db/
│   └──  bootstrap.js
├── functions/
│      ├-- sign-in.js
│      ├-- sign-up.js
│      └──  hello-world.js   
├-- netlify.toml
├-- package.json
└── yarn.lock

In this part, we’ll finally be writing the CRUD APIs. Oh, I hope you have saved the User ID and the Secret Token returned after signing in. If not, go ahead sign in again and save the new ones, as we’ll be needing them.

Create My Cat

The big idea is to use the secret token we saved earlier as a bearer token and on the server-side, we use it as a regular FaunaDB access token, to initialise the Fauna Client and run queries. In this particular case a query to create a cat.

First, we create a file called create-my-cat.js in our functions folder:

cd functions/ && touch create-my-cat.js

Then, we take the authorisation header and retrieve the bearer token to initialise our Fauna Client.

const faunadb = require('faunadb')

const q = faunadb.query

module.exports.handler = async (event, context, callback) => {
    const payload = JSON.parse(event.body)
    let authorisation = event.headers.authorization
    authorisation = authorisation.split(" ")
    const token = authorisation[1]

    const client = new faunadb.Client({
        secret: token
    })
}

Finally, we write the query to create an entry of a cat, from the payload, we also save a reference to the user who created the entry.

    try {
        const response = await client.query(
            q.Create(
                q.Collection('cat_breeds'), {
                    data: {
                        name: payload.name,
                        image: payload.image,
                        breed: payload.breed,
                        userRef: q.Ref(q.Collection('users'), payload.user_id)
                    }
                }
            )
        )

        callback(null, {
         statusCode: 200,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "POST, OPTIONS",
         },
         body: JSON.stringify(response),
      })
    } catch (err) {
        console.error(err)

        callback(null, {
         statusCode: 400,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "POST, OPTIONS",
         },
         body: JSON.stringify({error: err}),
      });
    }

To test the API run the dev server:

yarn run dev

Then, authenticate the user we created earlier, using a POST request to:

http://localhost:8888/api/create-my-cat

With the following payload:

{
  "user_id": "285249298598199809",
  "name": "mr snugglepants",
  "image": "https://example.com/cat_1.jpg",
  "breed": "maine coon"
} 

And, the authorisation header:

{
  "authorization": "Bearer fnED_83xz1ACAAPy2UP8gAYBHFYA7Xw0l-0EDv_oWF4fj28gX9I"
}

Make sure to make a few entries before proceeding to the next step.

Retrieve My Cats

Retrieving the cats is similar, but we’ll be using the Index we created in the last part - cats_by_users. We’ll also be using a FaunaDB Lambda function, which will process the list of cat ids retrieved from the Index to their entire document.

First, create a file retrieve-my-cats.js in the functions folder:

touch retrieve-my-cats.js

Then, write the following code in the file:

const faunadb = require('faunadb')

const q = faunadb.query

module.exports.handler = async (event, context, callback) => {
    const payload = JSON.parse(event.body)
    let authorisation = event.headers.authorization
    authorisation = authorisation.split(" ")
    const token = authorisation[1]
    const user_id = payload.user_id

    const client = new faunadb.Client({
        secret: token
    })
    
     try {
        const response = await client.query(
            q.Map(
                q.Paginate(
                    q.Match(
                        q.Index('cats_by_users'),
                        q.Ref(q.Collection("users"), user_id)
                    )
                ),
                q.Lambda('catBreedsRef', q.Get(q.Var("catBreedsRef")))
            )
        )

        callback(null, {
         statusCode: 200,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "POST, OPTIONS",
         },
         body: JSON.stringify(response),
      })
    } catch (err) {
        console.error(err)

        callback(null, {
         statusCode: 400,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "POST, OPTIONS",
         },
         body: JSON.stringify({error: err}),
      });
    }
}

To test the API, restart the dev server. Then, make a POST request to the API, with the following payload. (Don’t forget the Authorisation Token in the Header)

{
  "user_id": "285249298598199809",
}

For the next part, make sure to save one of the Cat’s ID.

Update My Cat

For updating data we’ll use the Cat ID we saved earlier.

First, create a file update-my-cat.js in the functions folder

touch update-my-cat.js

Next, write the following code in the file.

const faunadb = require('faunadb')

const q = faunadb.query

module.exports.handler = async (event, context, callback) => {
    const payload = JSON.parse(event.body)
    let authorisation = event.headers.authorization
    authorisation = authorisation.split(" ")
    const token = authorisation[1]
    const cat_id = payload.cat_id
    const data = payload.data

    const client = new faunadb.Client({
        secret: token
    })
    
     try {
        const response = await client.query(
            q.Replace(
                q.Ref(q.Collection('cat_breeds'), cat_id),
                {
                    data: data
                })
        )

        callback(null, {
         statusCode: 200,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "PUT, OPTIONS",
         },
         body: JSON.stringify(response),
      })
    } catch (err) {
        console.error(err)

        callback(null, {
         statusCode: 400,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "PUT, OPTIONS",
         },
         body: JSON.stringify({error: err}),
      });
    }
}

Then, to test the API, restart the dev server and make a PUT request to the API, with changes in the payload. (Don’t forget the Authorisation Token in the Header)

{
  "cat_id": "288175339623940608",
  "data": {
    "breed": "tabby"
  }
}

Delete My Cat

Just like in the last script we’ll use the Cat ID we saved earlier.

First, create a file delete-my-cat.js in the functions folder

touch delete-my-cat.js

Then, write the following code in the file.

const faunadb = require('faunadb')

const q = faunadb.query

module.exports.handler = async (event, context, callback) => {
    const payload = JSON.parse(event.body)
    let authorisation = event.headers.authorization
    authorisation = authorisation.split(" ")
    const token = authorisation[1]
    const cat_id = payload.cat_id


    const client = new faunadb.Client({
        secret: token
    })
    
     try {
        const response = await client.query(
            q.Delete(
                q.Ref(q.Collection('cat_breeds'), cat_id))
        )

        callback(null, {
         statusCode: 200,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "DELETE, OPTIONS",
         },
         body: JSON.stringify(response),
      })
    } catch (err) {
        console.error(err)

        callback(null, {
         statusCode: 400,
         headers: {
             /* Required for CORS support to work */
             "Access-Control-Allow-Origin": "*",
             "Access-Control-Allow-Headers": "*",
             "Access-Control-Allow-Methods": "DELETE, OPTIONS",
         },
         body: JSON.stringify({error: err}),
      });
    }
}

Then, to test the API, restart the dev server and make a DELETE request to the API. (Don’t forget the Authorisation Token in the Header)

That’s it for this tutorial series on making serverless authenticated CRUD with Netlify Functions and Fauna DB. Hope, you make something awesome.


Links

Link to Part 1: Authenticated Serverless CRUD with Netlify Functions and FaunaDB Part 1

Link to Part 2: Authenticated Serverless CRUD with Netlify Functions and FaunaDB Part 2

Copyright © 2018-2024 The Leaky Cauldron Blog. All Rights Reserved.