1

I am building a like-counter in a NextJS ReactJS app, which stores a persistent incrementable count value. The max-count of clicks / likes is to be set to 100. The button will not increment the count any further than this value. The like-counter value is stored in a FaunaDB.

I can render the value stored in the db on the front-end, but I can't get the onClick handler to update the value stored in the db.

Here is the (commented) code so far:

Here, State is declared via a useState hook.

const [likes, setLikes] = useState([]);

The updateLikes function

async function updateLikes() {
   await fetch("/api/newLikes", requestOptionsLikes)
      .then(() => getLikes())
      .catch((e) => console.log(e))
}

The PUT method is instantiated:

  const requestOptionsLikes = {
    method: "PUT",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ likes })
  }

A useEffect hook:

useEffect(() => {
   getLikes();
   }, []);

The click function:

const handleSubmitLikes = (e) => {
   e.preventDefault();
   updateLikes();
  }

The JSX that renders the clickable LIKE button and displays the value from the db.

<div>
   <div onClick={handleSubmitLikes}>LIKE</div>
      </div>
      {
        likes.map((d) => (
          <div>
            <div>{d.data.like}</div>
          </div>
      ))}
</div>

The code is correctly fetching the likes from the DB so I think that there is no need to display the getLikes API. However the newLikes API is returning a 500 Internal Server Error, so I display it below:

const faunadb = require("faunadb");

// your secret hash
const secret = process.env.FAUNADB_SECRET_KEY;
const q = faunadb.query;
const client = new faunadb.Client({ secret });

console.log("CALLLED API")

module.exports = async (req, res) => {
  const formData = req.body.data;
  console.log("API CALL");
  try {
    const dbs = await client.query(
      console.log("API UPDATE"),
      console.log(formData),

      q.Update(
        q.Ref(q.Collection('Likes'), '305446225474224651'),
        {
          data: {
            like: formData.likes[0].data.like + 1
          },
        }
      )
    );
    // ok
    res.status(200).json(dbs.data);
  } catch (e) {
    // something went wrong
    res.status(500).json({ error: e.message });
  }
};

Basically, I can't update the Database via the PUT method.

WΔ_
  • 1,229
  • 4
  • 20
  • 34
  • 1
    Is `console.log(formData)` displaying the expected output? – juliomalves Aug 01 '21 at 10:37
  • Hi. Basically no. It cannot find the variable. Basically, I think the error (so far) is with the call to `await fetch("/api/newLikes", requestOptionsLikes)`. It returns a 500, but even if I set a console.log within the getLikes API call (as edited above as console.log("API CALL")), it fails to register on console. – WΔ_ Aug 04 '21 at 20:21
  • 1
    Make sure to look for `console.log("API CALL")` in the terminal where you started the Next.js server and not in the browser's console. Do you still not see it there? – juliomalves Aug 04 '21 at 20:39
  • Yes! I see it there. It's in the browser console - both the console.log("CALLED API") and the console.log("API CALL"). – WΔ_ Aug 04 '21 at 20:56
  • However `console.log(formData)` is returning "undefined". I think it mean the `PUT` query is improperly specified. I don't think the 'code' is 'asking' the database to update the value of like - it's currently set to `3` in the `db`. – WΔ_ Aug 04 '21 at 21:02
  • 1
    What's the output of logging `e` in the `catch` block of your API route? If `formData` is undefined then this line `like: formData.like + 1` will error. – juliomalves Aug 04 '21 at 21:07
  • The output in the browser is: `Failed to load resource: the server responded with a status of 500 (Internal Server Error)`. There is no other error message - either in browser or in the terminal. – WΔ_ Aug 04 '21 at 21:10
  • 1
    Try replacing your `formatData` declaration with: `const formData = req.body;`. – juliomalves Aug 04 '21 at 21:13
  • Great idea. `console.log(formData.likes)` returns the following output in the terminal: `[ { ref: { '@ref': [Object] },ts: __ref_code__, data: { like: 3 } }]`. If I `console.log(formData.likes.data.like)` however, it returns no output to the console. – WΔ_ Aug 04 '21 at 21:24
  • This string: `formData.likes.data.like + 1` is failing to increment the value in the database. – WΔ_ Aug 04 '21 at 21:26
  • 1
    `formData.likes` is an array, if you want to access the first item you need to do so with `formData.likes[0].data.like` – juliomalves Aug 04 '21 at 21:34
  • Yep that accesses the correct value: If I `console.log(formData.likes[0].data.like + 1)`, then I receive `4`, however, within the Update function `data: { like: formData.likes[0].data.like + 1, },` is not rendering an update to the `db` value - neither is: `like: like + 1`. – WΔ_ Aug 04 '21 at 21:54
  • Any ideas how to update the like count from within the Update query? – WΔ_ Aug 05 '21 at 16:09

2 Answers2

2

Your demonstrated code appears to increment that value provided in the PUT request, which potentially allows the current client to specify any value. There is also no evidence that you are capping the count at 100.

You can perform the counter increment entirely in FQL with a query like this:

q.Let(
  {
    counterRef: q.Ref(q.Collection('Likes'), '305446225474224651'),
    counterDoc: q.Get(q.Var('counterRef'),
    counterVal: q.Select(['data', 'like'], q.Var('counterDoc')
    newCounterVal: q.If(
      q.GTE(q.Var('counterVal'), 100),
      100,
      q.Add(q.Var('counterVal'), 1)
    ),
    update: q.If(
      q.Equals(q.Var('counterVal'), q.Var('newCounterVal')),
      null,
      q.Update(
        q.Var('counterRef'),
        { data: { like: q.Var('newCounterVal') } }
      )
    )
  },
  q.Var('newCounterVal')
)

This uses Let to assign some named values to make it easy to re-use values. newCounter value is set to the incremented counterVal only when counterVal is less than 100. Updating the counter only happens when the counter actually changes (e.g. for values less than 100). The query returns the value of newCounterVal.

Ideally, you'd embed this query in a user-defined function (UDF). Whenever your rule to cap counts at 100 needs to change, only the UDF needs to be updated, and not every location in your application code where 100 might appear.

eskwayrd
  • 3,691
  • 18
  • 23
  • 1
    I ended up capping the counter functionality on the front-end in JSX - since the app is written in `NextJS` / `ReactJS`. This simplifies the `API` call. Thank you for your response! – WΔ_ Aug 19 '21 at 12:55
0

This was my solution. I found the FaunaDB forums build issues like this and the related. The below solution will increment the value by 1.

q.Update(
            q.Ref(q.Collection('Likes'), '__ref_id__'),
                {
                    data: {
                        like: q.Add(
                            q.Select(
                                ['data', 'like'],
                                q.Get(
                                    q.Ref(
                                        q.Collection('Likes'),
                                        '__ref_id__'
                                    )
                                )
                            ),
                            1
                        )
                    }
                },
            )

The counter button itself, written in NextJS / ReactJS, caps the functionality of the button to a null onClick for values beyond 100.

<div onClick={d.data.like < 100 ? handleSubmitLikes : null}

handleSubmitLikes access the API.

WΔ_
  • 1,229
  • 4
  • 20
  • 34