“Best” way to return fauna data as json

Hey,

I’m building a nextjs application with a FaunaDB database. The application exposes the data via a api that basically calls the FaunaDB database using the js driver. Atm I’m returning the data by stringifying it and then parsing it back to json to turn the ref into json. Is this the best way or is there some built in function for this in the driver? I did some digging but couldn’t find anything. Also, if if stringify it, is there some good way to parse the ref back into its class form to be able to send it back to fauna?

In the driver package, at src/_json.js there is a parseJSON function, which is not exported. I believe it is very intentionally an internal detail that we shouldn’t be relying on. It is what you are looking for, though. You’d have to fork the driver in order to use it from Vercel or Netlify or whatever.

But I would NOT recommend that. I can suggest 2 other options.

  1. Use the native JSON response in the client. These value (even if not in the “class” form) can be used directly in the driver for future responses (if called directly from the client).
  2. format the requests and responses in the API to work with ID’s like they are part of the data. So, similar to how GraphQL makes _id and _ts seem part of the document.

For an example used in netlify + CRA app:

Third Alternative?

another option is to get the client to make calls directly. This has all sorts of security implications, though, so it would almost certainly require a big rework of your app, .

1 Like

Cheers @ptpaterson. Do you know if I can use the json version of the ref directly when storing something in a collection? For example I might have a product that references a category and it would be nice if I could store the json ref (which I fetched earlier) instead of fetching it once more when creating/updating the product.

Yes, you should absolutely save the ref.

One-to-many relation example

store ref in Post (the “many” side of relation)

Create(Collection('Post'),  {
  data: {
    owner: Ref(Collection('User'), '1234')
  }
})

retrieve User from Post

Let(
  {
    postRef: Ref(Collection('Post'), '5555'),
    postDoc: Get(Var('postRef')),
    userRef: Select(['data', 'owner'], Var('postDoc')),
    userDoc: Get(Var('userRef'))
  },
  Var('userDoc')
)

retrieve all Posts for a User

This requires an Index. It will take as a term the Ref of the User.

CreateIndex({
  name: posts_by_user,
  source: Collection('Post'),
  terms: [
    {
      field: ["data", "owner"]
    }
  ]
})

Then we can use the index in the query.

Let(
  {
    userRef: Ref(Collection('User'), '1234')
    userDoc: Get(Var('userRef')),
    postRefsPage: Paginate(Match(Index('posts_by_user')))
    postDocsPage: Map(Var('postRefsPage'), Lambda('ref', Get(Var('ref')))),
  },
  Var('postDocsPage')
)

I was thinking more about the recieving end. For example I might have a collection called categories and one called product that has a reference saved for the category it belongs to. Something like

{
  "ref": Ref(Collection("categories"), "269756182088909300"),
  "ts": 1593518393440000,
  "data": {
    "name": "Jeans"
  }
}

{
  "ref": Ref(Collection("products"), "269756182088909400"),
  "ts": 1593518393440000,
  "data": {
    "name": "Grey jeans",
    "category": Ref(Collection("categories"), "269756182088909300")
  }
}

Say that I then have a form in my front end where a user can select which category from a dropdown where the values in the dropdown are populated by the categories collection. In my API I would return the categories according to your option 1 above, like

[
  {
     "ref": { @ref: { id: "269756182088909300", ... }},
     "ts": 1593518393440000,
     "data": {
       "name": "Jeans"
     }
  },
  ...
]

Then when I select one of the categories in the front end the product object would look something like

{
  "ref":  { @ref: { id: "269756182088909400", ... }},
  "ts": 1593518393440000,
  "data": {
    "name": "Grey jeans",
    "category":  { @ref: { id: "269756182088909300", ... }}
  }
}

I would then post this data back to my API to store it in FaunaDB and I’m a bit unsure if there some way to transform the object version of the ref into the class version before sending it, or if I need to do a lookup for each ref and set them again before saving?

Oooooh. I THINK You have a couple of options. I’m not sure what Fauna folks would recommend, to be honest.

You can rebuild the ref using the id value and the fact that you know the collection name.

Or you could also use the ref object directly using Expr. For example, the following works.

    const ref = {
      '@ref': {
        id: '269905835417666048',
        collection: {
          '@ref': {
            id: 'categories',
            collection: {
              '@ref': {
                id: 'collections'
              }
            }
          }
        }
      }
    }

    const category = await client.query(q.Get(new Expr(ref)))

Check out the source for the Ref function.

Awesome, just what I was looking for. Thx a lot for the help :slightly_smiling_face: