More about the Javascript Document
class
The data you provide to create a Document is fundamentally different from the Document itself.
Are you familiar with GraphQL? There is a parallel here with how there is a distinct difference between type
and input
. Don’t want to get to stuck on introducing more concepts, though…
The JS Document
class is not meant to represent objects that only exist in user-land. That is, you should not create a Document
class instance and then try to use that to create itself in FQL. Creating an instance directly with ts: TimeStub.fromDate(new Date())
is like asserting that it already exists in the database.
You would not, for example provide a Document when creating another document, but it is very similar to what you are trying to do.
// invalid FQL
let thing1 = Thing.byId('1')
Thing.create(thing1)
You would instead create a new input from an existing Document
// invalid FQL
let thing1 = Thing.byId('1')
Thing.create(thing1.data) // 'data' will not include ts, coll, or ttl
The driver is not attempting to provide you with an ORM implementation for your types. The primary reason for the provided types is to ensure values received from the database are sent back in the same shape. In the case of Documents, they are wrapped in an object in a @doc
field. By sending back to fauna with the same encoding, you can do things like call methods on the document, or anything else you would do with a document:
${jsDocument}.update({...})
${jsDocument}.delete({...})
Thing.create({
relationship: ${jsDocument}
})
Thing.byRelationship(${jsDocument})
Note that the JS Document
class fields are marked readonly. It might also be a good idea, now that I think about it, to wrap the whole type in the TS utility ReadOnly<Document & T>
.
Workaround
focus on your own domain type
I would recommend creating an instance of your own Domain instance, here User
, then create it with an id if necessary.
const newUser: User = {
email: 'leroy.jenkins@epic.com'
}
const response = client.query<Document<User>>(fql`
User.create(${{
id: '1'
...newUser
})
`)
I think it should also be okay to include the id
in your User
type
type User = {
id: string;
email: string;
name: string;
ttl?: TimeStub;
};
const newUser: User = {
id: '1',
email: 'leroy.jenkins@epic.com'
}
const response = client.query<Document<User>>(fql`
User.create(${newUser})
`)
Or you can add an additional type to your app which is something like UserInput
type UserInput = {
email: string;
name: string;
id?: string;
ttl?: TimeStub;
};
use the data
field on documents
const newDocument: Document = new Document({
coll: 'User',
id: '1',
ts: TimeStub.fromDate(new Date())
});
const newUser: DocumentT<User> = Object.assign(newDocument, {
email: 'leroy.jenkins@epic.com'
});
const response = client.query<Document<User>>(fql`
User.create(${newUser}.data)
`)
note how .data
is called in FQL, not JS.
use delete
or assign fields to undefined
This could give you more flexibility to define whatever input you want
const response = client.query<Document<User>>(fql`
User.create(${{ ...newUser, coll: undefined, ts: undefined }})
`)