Help understanding "create_fweet" UDF from fwitter example

In the setup scripts all the UDFs for the database are created among other things. Trying to understand how these UDFs are written I dug down into the create_fweet UDF.

This is the javascript function:

“GetFweetsWithUsersMapGetGeneric” is:

I fail to understand what this GetFweetsWithUsersMapGetGeneric tries to accomplish and why it’s necessary and makes it seem extremely daunting to write just a simple create function. Any insights?

Definitely, I’ll provide you with some additional insights :slight_smile:

I wrote that example and the example is definitely advanced. We figured that most of our users will easily find their way to create a simple Todo app and are looking for something more advanced. To make it accessible for newer users though it’s meant to be accompanied by a guide that goes from A to Z from which the first introduction article is here. I did not find the time yet to write the complete guide though so I’ll explain some more here :slight_smile: .

A simple create does not need to be complex, as the article which I linked explains, a simple create consists of:

 Create(Collection('fweets'),
  {
    data: {
     message: message,
     likes: 0,
     refweets: 0,
     comments: 0,
     created: Now()
    }
  })

The above example does the following:

  • Create a fweet

  • Return the fweet

However, often your application requires more than a simple create. You might want to create something else at the same time you create your entity (in this case a fweet) and when you created it, you might return something more complex than just the fweet. Doing multiple things in one transaction is actually where FQL shines. This example is for people who require such an advanced use-case.

In our fwitter application, the requirements of creating a fweet were more advanced

  • Create the fweet
  • If there are hashtags in the fweet, create the new ones and/or find existing hashtags and link the fweet to them
  • Link the fweet to the author

And then the part that confuses you is that if our application shows a list of fweets, it is not enough to just show the fweet, we also want to know:

  • Did I already like that fweet?
  • Is this the original fweet or is this a refweet, if it’s a refweet get the original fweet.
  • Retrieve the author so I can show the name of the author
  • Show the statistics (how many people liked/commented/refweeted)
  • Get the comments, for each comment, get the author.

The Fauna Query Language (FQL) is made to be able to do such complex queries in one query and that is exactly what is shown here. If your application requires only a simple ‘get’ or a simple ‘create’ then that’s fine but if it needs to get many other related resources then you can. In this case we preferred to do that since after creating the fweet, we will immediately display it and insert it in your existing list of fweets (those have a certain format so we wanted it to be the same format). Instead of doing two separate calls (create & then get the new list of fweets again) we immediately get the fweet in the exact same format that we would get it when we just get the list (our feed) of fweets.

And that’s why this function is called ‘Generic’. Whenever we need to display a fweet, we want it to be in the same format, whether we get our daily feed, the fweets for a certain tag, the fweets for a certain user, the return result of a creating a new fweet. It’s not necessary, it’s just a good idea not to have different formats of the same resource in an application so we reused the same function for each app. For create_fweet that might seem strange since there are no comments etc yet but that doesn’t really matter, it will just return a list of empty comments.

The advantage? Our frontend only needs to know one format of fweets, multiple queries that have a different format of a specific resource will generally complicate your implementation. But this is definitely not something you should worry about when just learning the basics of FaunaDB :slight_smile:.

Could it have also been done by calling a UDF instead of inlining the logic? So instead of fweetWithUserAndAccount: GetFweetsWithUsersMapGetGeneric([Select(['ref'], Var('newFweet'))])

you would have
fweetWithUserAndAccount: Call(Function('get_fweets_with_users_map_get_generic'), Select(['ref'], Var('newFweet')))

where get_fweets_with_users_map_get_generic is a UDF with the same logic in it.
If it would have been functionally equivalent, is there a reason not to do it this way?

Absolutely it can be done like that. That’s something a lot of users actually do.
I personally didn’t see the need since I like the composability of FQL in the host language a lot.
It’s opinionated on what you preferI assume :slight_smile:.

There are complex things permissions based that you could do with functions that call other functions (e.g. biota does that iirc)
On the other hand someone else might find it more confusing in terms of permissions.