Skip to main content

Resolvers

About#

Resolvers are functions that read/write data from your database based on parameters that we pass in.

You can read more about them here.

Please note that mutation resolvers that create entities are not provided IDs, since most databases generate IDs for you. If your database does not generate IDs, then you must do this manually in your resolver before you write to your database.

Every resolver function accepts context as the final parameter. See the context guide for more info.

info

If you are using the default database, then you can skip this step by using the default resolvers. More details here.

If you are using the default database, you can skip to the authentication guide.

info

If you are using an SQL-like database, we recommend using the SQL Resolver Generator package for easier and faster implementation.

warning

The examples below use a fictional ORM to make queries to the database. You can use any method you want to make database queries, as long as your resolver function returns the correct data.

Timestamps#

By default, any field called createdAt, updatedAt or lastRead will be set to whatever value you return from your getNow callback.

Any field called annotationCreatedAt will be set to a unix timestamp in MS, which you must convert to your databases format.

See the timestamps guide for more info.

Query resolvers#

Query resolvers are passed in via the Query property on the resolvers object.

import CollabServer from "@pdftron/collab-server";
const server = new CollabServer({
resolvers: {
Query: {
...yourResolvers,
},
},
});

Most query resolvers come with a set of filters that you must apply to each query.

user#

user(id, context): Promise<User>

  • id (string) the id of the user to fetch
  • context (Context) context about the user. See the context guide for more info.

Used to get a user entity by its ID.

Must return a promise that resolves to a user, or null if a user is not found.

Example

const server = new CollabServer({
resolvers: {
Query: {
user: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const user = await db
.select('id, user_name, email, type, created_at, updated_at')
.from('users')
.where('id', '=', id)
.get();
return user;
},
},
},
});

userWithEmail#

userWithEmail(email, context): Promise<User>

  • email (string) the email of the user we need to fetch
  • context (Context) context about the user. See the context guide for more info.

Used to get a user entity by their email. This is used for invites.

Must return a promise that resolves to a user, or null if not found.

Example

const server = new CollabServer({
resolvers: {
Query: {
userWithEmail: async (email) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const user = await db
.select('id, user_name, email, type, created_at, updated_at')
.from('users')
.where('email', '=', email)
.get();
return user;
},
},
},
});

userByIdentifier#

OPTIONAL - This resolver is only required if using the offline annotation sync feature

userByIdentifier(identifier, context): Promise<User>

  • identifier (string) either the email or username of the user
  • context (Context) context about the user. See the context guide for more info.

Used to get a user entity by an unknown identifier. Your resolver should query by both email and username.

Must return a promise that resolves to a user, or null if not found.

Example

const server = new CollabServer({
resolvers: {
Query: {
userByIdentifier: async (identifier) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const user = await db
.select('id, user_name, email, type, created_at, updated_at')
.from('users')
.where('email', '=', identifier)
.orWhere('user_name', '=', identifier)
.get();
return user;
},
},
},
});

annotations#

info

Please read this spec carefully and make sure to follow all the guidelines provided!

annotation(query, context): Promise<Annotations[]>

  • query (object) an object containing data and filters to query with
    • ids (string[]) a list of annotation IDs to fetch. If this is not provided, fetch all annotations that match the rest of the criteria.
    • annotationIds (string[]) if provided, only fetch annotations that match one of the provided annotationId
    • documentId (string) if provided, only fetch annotations belonging to this document
    • pageNumbers (number[]) if provided, only fetch annotations that match one of the provided page numbers
    • inReplyTo (string) if provided, only fetch annotations whose inReplyTo field matches this ID
    • filters (Filters) an object containing filters to apply to the query.
  • context (Context) context about the user. See the context guide for more info.

Used to get a list of annotations.

Must return a promise that resolves to an array of annotations.

Example

const server = new CollabServer({
resolvers: {
Query: {
annotation: async (query) => {
const {
ids,
annotationIds,
documentId,
pageNumbers,
inReplyTo,
filters = {}
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('annotations');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(annotationIds) {
query = query.where('annotation_id', 'IN', annotationIds)
}
if(pageNumbers) {
query = query.where('page_number', 'IN', pageNumbers)
}
if(documentId) {
query = query.where('document_id', '=', documentId)
}
if(inReplyTo) {
query = query.where('in_reply_to', '=', inReplyTo)
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const annots = await query.get()
return annots;
},
},
},
});

documents#

info

Please read this spec carefully and make sure to follow all the guidelines provided!

documents(query, context): Promise<Document[]>

  • query (object) an object containing data and filters to query with
    • ids (string[]) a list of document IDs to fetch. If this is not provided, fetch all documents that match the rest of the criteria.
    • userId (string) if provided, only fetch documents that this user belongs to (requires a query to document members - see the example below)
    • isPublic (boolean) if true, only return documents that are public
    • filters (Filters) an object containing filters to apply to the query.
  • context (Context) context about the user. See the context guide for more info.

Used to get a list of documents.

Must return a promise that resolves to an array of documents.

Example

const server = new CollabServer({
resolvers: {
Query: {
documents: async (query) => {
const {
ids,
userId,
isPublic,
filters = {}
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('documents');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(isPublic) {
query = query.where('is_public', '=', true)
}
if(userId) {
query = query.where(
'id',
'IN',
`
(SELECT document_id FROM document_members WHERE user_id = '${userId}')
`)
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const docs = await query.get()
return docs;
},
},
},
});

annotationMembers#

info

Please read this spec carefully and make sure to follow all the guidelines provided!

annotationMembers(query, context): Promise<AnnotationMember[]>

  • query (object) an object containing data and filters to query with
    • ids (string[]) a list of document IDs to fetch. If this is not provided, fetch all members that match the rest of the criteria.
    • annotationId (string) if provided, only fetch members that belong to this annotation
    • documentId (string) if provided, only fetch annotation members with this document ID
    • userId (string) if provided, only fetch memberships that belong to this user
    • filters (Filters) an object containing filters to apply to the query.
  • context (Context) context about the user. See the context guide for more info.

Used to get a user's membership to an annotation.

Must return a promise that resolves to a list of annotation members.

Example

const server = new CollabServer({
resolvers: {
Query: {
annotationMembers: async (query) => {
const {
ids,
annotationId,
userId,
filters = {},
documentId
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('annotation_members');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(userId) {
query = query.where('user_id', '=', userId);
}
if(annotationId) {
query = query.where('annotation_id', '=', annotationId);
}
if(documentId) {
query = query.where('document_id', '=', documentId);
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const members = await query.get()
return members;
},
},
},
});

documentMembers#

info

Please read this spec carefully and make sure to follow all the guidelines provided!

documentMembers(query, context): Promise<DocumentMember[]>

  • query (object) an object containing data and filters to query with
    • ids (string[]) a list of document IDs to fetch. If this is not provided, fetch all members that match the rest of the criteria.
    • documentId (string) if provided, only fetch members that belong to this document
    • userId (string) if provided, only fetch memberships that belong to this user
    • filters (Filters) an object containing filters to apply to the query.
  • context (Context) context about the user. See the context guide for more info.

Used to get a user's membership to a document.

Must return a promise that resolves to a an array of document member.

Example

const server = new CollabServer({
resolvers: {
Query: {
documentMembers: async (query) => {
const {
ids,
documentId,
userId,
filters = {}
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('document_members');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(userId) {
query = query.where('user_id', '=', userId);
}
if(documentId) {
query = query.where('document_id', '=', documentId);
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const members = await query.get()
return members;
},
},
},
});

mentions#

info

Please read this spec carefully and make sure to follow all the guidelines provided!

mentions(query, context): Promise<Mentions[]>

  • query (object) an object containing data and filters to query with
    • ids (string[]) a list of mention IDs to fetch. If this is not provided, fetch all mentions that match the rest of the criteria.
    • annotationId (string) if provided, only fetch mentions that belong to this annotation
    • userId (string) if provided, only fetch mentions that belong to this user
    • documentId (string) if provided, fetch all mentions that belong to this document
    • filters (Filters) an object containing filters to apply to the query.
  • context (Context) context about the user. See the context guide for more info.

Used to get a user's mention to an annotation.

Must return a promise that resolves to a list of mentions.

Example

const server = new CollabServer({
resolvers: {
Query: {
mentions: async (query) => {
const {
ids,
annotationId,
userId,
filters = {}
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('mentions');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(userId) {
query = query.where('user_id', '=', userId);
}
if(annotationId) {
query = query.where('annotation_id', '=', annotationId);
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const mentions = await query.get()
return mentions;
},
},
},
});

annotationCount#

annotationCount(query, context): Promise<number>

  • query (object)
    • documentId (string) query the number of annotations for this document
    • since (number - timestamp in ms) query the number of annotations that were created after this time
  • context (Context) context about the user. See the context guide for more info.

This is a very important query that is used to calculate the number of unread messages for a user. It needs to return the number of annotations belonging to the specified document that were created AFTER the since parameter.

Note: it should only query annotations where the author ID is not null.

Example

const server = new CollabServer({
resolvers: {
Query: {
annotationCount: async ({ documentId, since }) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const query = db.select('COUNT(DISTINCT id)')
.from('annotations')
.where('document_id', '=', documentId)
.where('created_at', '>', since)
.whereNotNull('author_id')
const count = await query.get();
return count;
},
},
},
});

annotationMemberCount#

annotationMemberCount(query, context): Promise<number>

  • query (object)
    • documentId (string) query the number of annotation members for this document
    • userId (string) query memberships for this user
    • since (number - timestamp in ms) query the number of annotation members where "annotation_created_at" is greater than this timestamp
  • context (Context) context about the user. See the context guide for more info.

This is another important query that we use to calculate unread count for a document. It needs to return the number of annotation members where annotation_created_at is greater than the since parameter.

In other words, this query returns the number of annotation members whose corresponding annotation was created after since.

Example

const server = new CollabServer({
resolvers: {
Query: {
annotationMemberCount: async ({ userId, documentId, since }) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const query = db.select('COUNT(DISTINCT id)')
.from('annotation_members')
.where('document_id', '=', documentId)
.where('user_id', '=', userId)
.where('annotation_created_at', '>', since)
const count = await query.get();
return count;
},
},
},
});

snapshots#

This resolver is only required if you are using the snapshots feature

snapshots(query, context): Promise<Snapshot[]>

  • query (object)
    • ids (string[]) if provided, fetch only the snapshots with these IDs
    • documentId (string) if provided, fetch snapshots only belonging to this document ID
    • filters (Filters) an object containing filters to apply to the query.
  • context (Context) context about the user. See the context guide for more info.

Used to get all the snapshots for a document.

Must return a promise that resolves to an array of Snapshots.

Example

const server = new CollabServer({
resolvers: {
Query: {
snapshots: async (query) => {
const {
ids,
documentId,
filters = {}
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('snapshots');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(documentId) {
query = query.where('document_id', '=', documentId);
}
if(filters.createdBefore) {
query = query.where('created_at', '<', filters.createdBefore)
}
if(filters.createdAfter) {
query = query.where('created_at', '>', filters.createdAfter)
}
if(filters.updatedBefore) {
query = query.where('updated_at', '<', filters.updatedBefore)
}
if(filters.updatedAfter) {
query = query.where('updated_at', '>', filters.updatedAfter)
}
if(filters.orderBy) {
query = query.order(
filters.orderBy === 'updatedAt' ? 'updated_at' : 'created_at',
filters.orderDirection
)
}
if (filters.limit) {
query = query.limit(filters.limit)
}
const snapshots = await query.get()
return snapshots;
},
},
},
});

snapshotAssets#

This resolver is only required if you are using the snapshots feature

snapshotAssets(query, context): Promise<SnapshotAssets[]>

  • query (object)
    • ids (string[]) if provided, fetch only the snapshot assets with these IDs
    • snapshotId (string) if provided, fetch snapshot assets only belonging to this snapshot ID
  • context (Context) context about the user. See the context guide for more info.

Used to get all the snapshot assets for a document.

Must return a promise that resolves to an array of snapshots assets.

Example

const server = new CollabServer({
resolvers: {
Query: {
snapshotAssets: async (query) => {
const {
ids,
snapshotId,
} = query;
// get a reference to our fictional ORM
const db = getDatabaseConnection();
let query = db
.select('*')
.from('snapshot_assets');
if(ids) {
query = query.where('id', 'IN', ids)
}
if(snapshotId) {
query = query.where('snapshot_id', '=', snapshotId);
}
const snapshotAssets = await query.get()
return snapshotAssets;
},
},
},
});

Mutation resolvers#

Mutation resolvers are passed in via the Mutation property on the resolvers object.

import CollabServer from "@pdftron/collab-server";
const server = new CollabServer({
resolvers: {
Mutation: {
...yourResolvers,
},
},
});

Mutation resolvers that create entities are required to return the created entity.

warning

Since most databases generate IDs for new entities, we do not pass an ID field to the mutation resolvers. If your database does not generate IDs for you, then you need to do this manually in your resolvers.

addUser#

addUser(user, context): Promise<User>

  • user (User) The user entity to write to the database
  • context (Context) context about the user. See the context guide for more info.

Used to create new users in the database. This is mainly for creating anonymous users when a person without an account is invited.

Must resolve with the written user entity.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addUser: async (user) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newUser = await db
.insert('users')
.values({
type: user.type,
email: user.email,
user_name: user.userName,
created_at: user.createdAt,
updated_at: user.updatedAt
});
return newUser;
},
},
},
});

addAnnotation#

addAnnotation(annotation, context): Promise<Annotation>

  • annotation (Annotation) the annotation to create
  • context (Context) context about the user. See the context guide for more info.

Used to save an annotation to the database.

Must resolve with the saved annotation object.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addAnnotation: async (annotation) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newAnnot = await db
.insert('annotations')
.values({
xfdf: annotation.xfdf,
annotation_id: annotation.annotationId,
document_id: annotation.documentId,
page_number: annotation.pageNumber,
created_at: annotation.createdAt,
updated_at: annotation.updatedAt,
in_reply_to: annotation.inReplyTo
});
return newAnnot;
},
},
},
});

batchAddAnnotations#

OPTIONAL - This resolver is optional, but highly recommended as batch inserting data can greatly increase write speeds. If this resolver is not provided, we will instead loop over the addAnnotation resolver.

batchAddAnnotations(annotations, context): Promise<Annotation[]>

  • annotations (Annotation[]) An array of annotations to create
  • context (Context) context about the user. See the context guide for more info.

Used to save multiple annotations to the database.

Must resolve with an array of annotation objects.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addAnnotation: async (annotations) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newAnnots = await db
.insert('annotations')
.values(annotations.map(annotation => ({
xfdf: annotation.xfdf,
annotation_id: annotation.annotationId,
document_id: annotation.documentId,
page_number: annotation.pageNumber,
created_at: annotation.createdAt,
updated_at: annotation.updatedAt,
in_reply_to: annotation.inReplyTo
})));
return newAnnots;
},
},
},
});

editAnnotation#

editAnnotation(id, editAnnotInput, context): Promise<Annotation>

  • id (string) the ID of the annotation to edit
  • editAnnotInput (object) an object containing the fields to edit. Can contain the following (both optional)
    • xfdf (string) The annotation's new XFDF (optional)
    • pageNumber (number) The annotation's new pageNumber (optional)
    • updatedAt (number) the timestamp of when the entity was updated at
  • context (Context) context about the user. See the context guide for more info.

Used to edit an annotation. This is called when an existing annotation is moved, changed, etc.

Must resolve to the new annotation entity.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
editAnnotation: async (id, editAnnotInput) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const editedAnnot = await db
.edit('annotations')
.values({
xfdf: editAnnotInput.xfdf,
page_number: editAnnotInput.pageNumber,
updated_at: editAnnotInput.updatedAt,
})
.where('id', '=', id)
return editedAnnot;
},
},
},
});

deleteAnnotation#

deleteAnnotation(id, context): Promise<{ successful: boolean }>

  • id (string) the id of the annotation to delete
  • context (Context) context about the user. See the context guide for more info.

Used to delete annotations from the database.

Must resolve to an object with a successful boolean, indicating if the operation was successful.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
deleteAnnotation: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
await db.delete('annotations').where('id', '=', id)
return { successful: true };
},
},
},
});

addDocument#

addDocument(document, context): Promise<Document>

  • document (Document) the document to write to the database
  • context (Context) context about the user. See the context guide for more info.

Used to add a document to the database.

Must resolve with the new document object

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addDocument: async (doc) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newDoc = await db
.insert('documents')
.values({
author_id: doc.authorId,
is_public: doc.isPublic,
name: doc.name,
created_at: doc.createdAt,
updated_at: doc.updatedAt,
});
return newDoc;
},
},
},
});

editDocument#

editDocument(id, editDocInput, context): Promise<Document>

  • id (string) the id of the document to edit
  • editDocInput (object) the fields to edit on the document (all properties are optional)
    • name (string) the document's new name
    • isPublic (boolean) whether or not the document is now public
    • updatedAt (number) the timestamp of when the entity was updated at
  • context (Context) context about the user. See the context guide for more info.

Used to edit a document.

Must resolve with the updated document entity.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
editDocument: async (id, editDocInput) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const editedDoc = await db
.edit('documents')
.values({
name: editDocInput.name,
is_public: editDocInput.isPublic,
updated_at: editDocInput.updatedAt,
})
.where('id', '=', id)
return editedDoc;
},
},
},
});

deleteDocument#

deleteDocument(id, context): Promise<{ successful: boolean }>

  • id (string) the id of the document to delete
  • context (Context) context about the user. See the context guide for more info.

Used to delete a document from the DB.

Must resolve with an object with a successful property indicating if the operation was successful.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
deleteDocument: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
await db.delete('documents').where('id', '=', id)
return { successful: true };
},
},
},
});

addDocumentMember#

addDocumentMember(member, context): Promise<DocumentMember>

  • member (DocumentMember) the document member to write to the database
  • context (Context) context about the user. See the context guide for more info.

Used to write a document membership to the database.

Must resolve with the created document member entity.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addDocumentMember: async (member) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newMember = await db
.insert('document_members')
.values({
user_id: member.userId,
document_id: member.documentId,
created_at: member.createdAt,
updated_at: member.updatedAt,
last_read: member.lastRead
});
return newMember;
},
},
},
});

editDocumentMember#

editDocumentMember(id, editMemberInput, context): Promise<DocumentMember>

  • id (string) the id of the document member to edit
  • editMemberInput (object) the properties to edit on the member
    • lastRead (number) the timestamp of when the person read the document
    • updatedAt (number) the timestamp of when the entity was updated at
  • context (Context) context about the user. See the context guide for more info.

Used to edit the membership of a document member.

Must resolve to the updated document member

Example

const server = new CollabServer({
resolvers: {
Mutation: {
editDocumentMember: async (id, editMemberInput) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const editedMember = await db
.edit('document_members')
.values({
last_read: editMemberInput.lastRead,
updated_at: editMemberInput.updatedAt,
})
.where('id', '=', id)
return editedMember;
},
},
},
});

deleteDocumentMember#

deleteDocumentMember(id, context): Promise<{ successful: boolean }>

  • id (string) the id of the document member to delete
  • context (Context) context about the user. See the context guide for more info.

Used to delete a document member from the database.

Must resolve to an object with a successful property indicating if the operation was successful or not.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
deleteDocumentMember: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
await db.delete('document_members').where('id', '=', id)
return { successful: true };
},
},
},
});

addAnnotationMember#

addAnnotationMember(member, context): Promise<AnnotationMember>

Used to write an annotation member to the database.

Must resolve with the new annotation member.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addAnnotationMember: async (member) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newMember = await db
.insert('annotation_members')
.values({
user_id: member.userId,
document_id: member.documentId,
annotation_id: member.annotationId,
created_at: member.createdAt,
updated_at: member.updatedAt,
// This property is passed as a unix timestamp,
// See the "timestamps" guide for more info
annotation_created_at: unixToTimestamp(member.annotationCreatedAt)
});
return newMember;
},
},
},
});

batchAddAnnotationMembers#

OPTIONAL - This resolver is optional, but highly recommended as batch inserting data can greatly increase write speeds. If this resolver is not provided, we will instead loop over the addAnnotationMember resolver.

batchAddAnnotationMembers(members, context): Promise<AnnotationMember[]>

Used to write multiple annotation members to the database.

Must resolve with an array of annotation member.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
batchAddAnnotationMembers: async (members) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newMembers = await db
.insert('annotation_members')
.values(members.map(member => ({
user_id: member.userId,
document_id: member.documentId,
annotation_id: member.annotationId,
created_at: member.createdAt,
updated_at: member.updatedAt,
// This property is passed as a unix timestamp,
// See the "timestamps" guide for more info
annotation_created_at: unixToTimestamp(member.annotationCreatedAt)
})));
return newMembers;
},
},
},
});

editAnnotationMember#

editAnnotationMember(id, editMemberInput, context): Promise<AnnotationMember>

  • id (string) the id of the annotation member to edit
  • editMemberInput (object) the properties to update on the annotation member (all are optional)
    • lastRead (number) the timestamp of when the last time the person read the annotation
    • updatedAt (number) the timestamp of when the entity was updated at
  • context (Context) context about the user. See the context guide for more info.

Used to update a user's membership to an annotation.

Must resolve with the updated annotation member.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
editAnnotationMember: async (id, editMemberInput) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const editedMember = await db
.edit('annotation_members')
.values({
last_read: editMemberInput.lastRead,
updated_at: editMemberInput.updatedAt,
})
.where('id', '=', id)
return editedMember;
},
},
},
});

deleteAnnotationMember#

deleteAnnotationMember(id, context): Promise<{ successful: boolean }>

  • id (string) the id of the member to delete
  • context (Context) context about the user. See the context guide for more info.

Used to delete an annotation member.

Must resolve with an object containing a successful property indicating if the operation was successful or not.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
deleteAnnotationMember: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
await db.delete('annotation_members').where('id', '=', id)
return { successful: true };
},
},
},
});

addMention#

addMention(mention, context): Promise<mention>

  • mention (Mention) the mention to write
  • context (Context) context about the user. See the context guide for more info.

Used to write a mention to the database.

Must resolve with the new mention.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addMention: async (mention) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newMention = await db
.insert('mentions')
.values({
user_id: mention.userId,
document_id: mention.documentId,
annotation_id: mention.annotationId,
created_at: mention.createdAt,
updated_at: mention.updatedAt,
});
return newMention;
},
},
},
});

deleteMention#

deleteMention(id, context): Promise<{ successful: boolean }>

  • id (string) the id of the mention to delete
  • context (Context) context about the user. See the context guide for more info.

Used to delete a mention.

Must resolve with an object containing a successful property indicating if the operation was successful or not.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
deleteMention: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
await db.delete('mentions').where('id', '=', id)
return { successful: true };
},
},
},
});

addSnapshot#

This resolver is only required if you are using the snapshots feature

addSnapshot(snapshot, context): Promise<Snapshot>

  • snapshot (Snapshot) the snapshot to write
  • context (Context) context about the user. See the context guide for more info.

Used to write a snapshot to the database.

Must resolve with the new snapshot.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addSnapshot: async (snapshot) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newSnapshot = await db
.insert('snapshots')
.values({
author_id: snapshot.authorId,
document_id: snapshot.documentId,
name: snapshot.name,
created_at: snapshot.createdAt,
updated_at: snapshot.updatedAt,
xfdf: snapshot.xfdf
});
return newSnapshot;
},
},
},
});

editSnapshot#

This resolver is only required if you are using the snapshots feature

editSnapshot(id, params, context): Promise<Snapshot>

  • id (string) The ID of the snapshot to edit
  • params Properties of the snapshot to edit
    • params.name (string) The new name of the snapshot
    • params.updatedAt (number) The timestamp the entity was edited at
  • context (Context) context about the user. See the context guide for more info.

Used to edit a snapshot in the database.

Must resolve with the updated snapshot.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
editSnapshot: async (id, params) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const editedSnapshot = await db
.edit('snapshots')
.values({
name: params.name,
updated_at: params.updatedAt
})
.where('id', '=', id)
return editedSnapshot;
},
},
},
});

deleteSnapshot#

This resolver is only required if you are using the snapshots feature

deleteSnapshot(id, context): Promise<{ success: boolean }>

  • id (string) The ID of the snapshot to edit
  • context (Context) context about the user. See the context guide for more info.

Used to delete a snapshot.

Must resolve with an object containing a successful property indicating if the operation was successful or not.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
deleteSnapshot: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
await db.delete('snapshots').where('id', '=', id)
return { successful: true };
},
},
},
});

addSnapshotAsset#

This resolver is only required if you are using the snapshots feature

addSnapshotAsset(snapshotAsset, context): Promise<SnapshotAsset>

  • snapshotAsset (SnapshotAsset) the snapshot asset to write
  • context (Context) context about the user. See the context guide for more info.

Used to write a snapshot asset to the database.

Must resolve with the new snapshot asset.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
addSnapshotAsset: async (snapshotAsset) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const newSnapshotAsset = await db
.insert('snapshot_assets')
.values({
snapshot_id: snapshotAsset.snapshotId,
data: snapshotAsset.data,
created_at: snapshotAsset.createdAt,
updated_at: snapshotAsset.updatedAt,
});
return newSnapshotAsset;
},
},
},
});

editSnapshotAsset#

This resolver is only required if you are using the snapshots feature

editSnapshotAsset(id, params, context): Promise<SnapshotAsset>

  • id (string) The ID of the snapshot asset to edit
  • params Properties of the snapshot to edit
    • params.snapshotId (string) The new ID of the snapshot
    • params.updatedAt (number) The timestamp the entity was edited at
  • context (Context) context about the user. See the context guide for more info.

Used to edit a snapshot asset in the database.

Must resolve with the updated snapshot asset.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
editSnapshotAsset: async (id, params) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
const editedSnapshotAsset = await db
.edit('snapshot_assets')
.values({
snapshot_id: params.snapshotId,
updated_at: params.updatedAt
})
.where('id', '=', id)
return editedSnapshotAsset;
},
},
},
});

deleteSnapshotAsset#

This resolver is only required if you are using the snapshots feature

deleteSnapshotAsset(id, context): Promise<{ success: boolean }>

  • id (string) The ID of the snapshot asset to edit
  • context (Context) context about the user. See the context guide for more info.

Used to delete a snapshot asset.

Must resolve with an object containing a successful property indicating if the operation was successful or not.

Example

const server = new CollabServer({
resolvers: {
Mutation: {
deleteSnapshotAsset: async (id) => {
// get a reference to our fictional ORM
const db = getDatabaseConnection();
await db.delete('snapshot_assets').where('id', '=', id)
return { successful: true };
},
},
},
});

Resolver entities#

User entity#

A user entity is an object with the following shape. Properties with a ? are optional (but recommended).

{
id: string;
type: 'STANDARD' | 'ANONYMOUS';
email?: string;
userName?: string;
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
}

Document entity#

A document entity is an object with the following shape. Properties with a ? are optional (but recommended).

{
id: string;
authorId: string;
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
isPublic?: boolean;
name?: string;
}

Annotation entity#

An annotation entity is an object with the following shape. Properties with a ? are optional (but recommended).

note

More information on the annotationId field can be found here

{
id: string;
xfdf: string;
authorId: string;
annotationId: string;
documentId: string;
pageNumber: number;
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
inReplyTo?: string;
}

DocumentMember entity#

{
id: string;
userId: string;
documentId: string;
lastRead: number (UTC timestamp in MS);
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
}

AnnotationMember entity#

An annotation member entity is an object with the following shape. Properties with a ? are optional (but recommended).

An annotation member represents a user's 'membership' to an annotation. It is also used to track if a user has read an annotation or not.

{
id: string;
userId: string;
documentId: string;
annotationId: string;
lastRead: number (UTC timestamp in MS);
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
annotationCreatedAt: number (UTC timestamp in MS);
}

Mention entity#

A mention entity is an object with the following shape. Properties with a ? are optional (but recommended).

A mention represents a mentioned user in an annotation.

{
id: string;
userId: string;
documentId: string;
annotationId: string;
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
}

Snapshot entity#

A snapshot entity is an object with the following shape. Properties with a ? are optional (but recommended).

A snapshot represents the state of a document at a certain period of time

{
id?: string;
authorId: string;
documentId: string;
name: string;
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
xfdf: string;
}

Snapshot asset entity#

A snapshot asset entity is an object with the following shape. Properties with a ? are optional (but recommended).

A snapshot asset represents a large annotation belonging to a snapshot.

{
id?: string;
snapshotId: string;
data: string;
createdAt: number (UTC timestamp in MS);
updatedAt: number (UTC timestamp in MS);
}

Filters#

Most Query resolvers come with a filter object, containing 7 properties that should be used when making your query.

  • filters (object) an object containing filters to apply to the query
    • createdBefore (number - timestamp in MS) If provided, only fetch entities created before this date
    • createdAfter (number - timestamp in MS) If provided, only fetch entities created after this date
    • updatedBefore (number - timestamp in MS) If provided, only fetch entities updated before this date
    • updatedAfter (number - timestamp in MS) If provided, only fetch entities updated after this date
    • orderBy ('updatedAt' | 'createdAt') if provided, order/sort your query by updatedAt or createdAt
    • orderDirection ('ASC' | 'DESC') if provided, order your query in the provided direction
    • limit (number) if provided, limit your query to this amount

It is important that these filters are used properly, as they are what allows the application to scale.

note

Since these filters are the same across all queries, it is recommended to create a utility function to apply the filters to your queries!