Why creating a new document with a ref requires the "History Write" permission?

Typically we use this to create new documents:

Create(Collection("MyCollection"), {...})

But sometimes it’s necessary to know the id beforehand to create relations between docs so we do:

Create(
  Ref(Collection("MyCollection"), "300055538762225803"),
  {...}
)

When creating a document with a ref it requires a “History Write” permission for the role, otherwise it throws an authorization error.

Can anyone comment on the reason for this?

Personally I find it annoying to give this permission to users just to create documents with ids…

Hi @pier I am trying to dig into this.

1 Like

Thanks @ptpaterson !

When you do the “typical” Create Fauna can generate an ID that it knows with all certainty that there is no history for that document. But that’s not the case if you provide the ID.

Consider that when you Delete a Document, it doesn’t outright remove it, rather it adds a “remove” event to the history. Later, you can Create with that same ID. When doing so, it doesn’t just create a new document, it appends to that Document’s history with a “create” record. If there is no previous history, then it will be the only history record, but it is still reaching in to the history mechanism to perform the action.

Does that make sense?

Thanks, it makes sense. Although I wish there was a way to create new documents with an id that didn’t require users having the “History Write” permissions.

When needing to create documents with references to other documents I’ve resorted to this pattern:

Let(
  {
    thingDoc: Create(Collection('Things'), {...}),
    thingRef: Select(['ref'], Var('thingDoc')),
    otherThingDoc: Create(
      Collection('OtherThings'),
      {
        thingRef: Var('thingRef')
      }
    )
  },
  {
    thingDoc: Var('thingDoc'),
    otherThingDoc: Var('otherThingDoc')
  }
)

Which works for many cases.

In some other cases I’ve needed document properties based on the document id. In this case there’s no other way than to resort to using NewId(). For example when modeling a file in an object storage system:

Let(
  {
    id: NewId()
  },
  Create(
    Ref(Collection('StorageFiles'), Var('id')),
    {
      data: {
        objectStorageKey: Concat([Var('id'), '.jpg'])
      }
    }
  )
)

If you write to a document more than once in a single query it only uses up one Transaction Write Op (Compute adds up if you do it a lot). But that means you can do this with only “create” and “write” privileges, and still only costs 1 TSO

Let(
  {
    newFile: Create(Collection('StorageFiles')),
    newFileRef: Select('ref', Var('newFile')),
    newId: Select('id', Var('newFileRef'))
  },
  Update(
    Var('newFileRef'), 
    { data: { objectStorageKey: Concat([Var('newId'), '.jpg']) } }
  )
)

Still need something additional than “create” privileges, but perhaps “write” is better than “write-history”.

1 Like

Kinda went crazy with it to test.

1 Like

Oh great idea!

Why didn’t I think of that? :slight_smile:

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.