Functions:DataAPI:Mutations - bettyblocks/cli GitHub Wiki

DataAPI Mutations

​ Any request which can modify data is considered a mutation. You may have already encountered the login mutation, which is present in every app. The Data API has mutations for each model in your application. These mutations can be used to create, update or delete data for your models. ​ If you're not yet familiar with sending queries to the Data API, we suggest you start with this article. Whereas queries can be sent from the Page Builder and the Playground, mutations are currently restricted to custom action functions. ​ For the examples in this article, we'll be using a Task model. Apply queries for your own implementation using models of your application. The examples also show how to send the query using the gql helper. ​

Create a Task

​ This mutation will create a new Task record and insert it into the database. Any fields added to $input will be applied to the record. As with all mutations, $input will be subject to validations specified in the model.

const mutation = `
mutation {
  createTask(input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { input: { task: "New Task" } });

Create many Tasks

​ This mutation can be used to create many Tasks records at the same time. This works exactly like the createTask mutation, but the $input variable expects a list of inputs. Ids will be automatically generated by the database. ​ Example use cases: ​

  • Add posts to blog
  • Add items to inventory ​
const mutation = `
mutation {
  createManyTask(input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { input: [
    { task: "Mow lawn" },
    { task: "Wash dishes" },
    { task: "Build todo application" },
]})

Delete a Task

​ This mutation will delete a Task record from the database. The only required input is the id of the record to be deleted. Deleting a record will not delete any of its relationships. ​

const mutation = `
mutation {
  deleteTask(id: $id) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { id: 1 });

Delete many tasks

​ This mutation can be used to delete multiple Task records at the same time. The $input variable expects a list of ids, as formatted in the example below. Filtering with $where is currently not supported, so this mutation is best combined with a separate query. ​ Example use cases: ​

  • Delete all expired items
  • Delete users based on the domain of their email address ​
const mutation = `
mutation {
  deleteManyTask(input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { input: { ids: [1, 2, 3] } } });

Update a Task

​ This mutation will update a single Task record. The record to be updated is selected by the $id. Any properties present in $input will replace the existing values on the record. ​

const mutation = `
mutation {
  updateTask(id: $id, input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { id: 1, input: { task: "Updated Task" } });

Update many Tasks

​ This mutation can be used to update multiple Task records. A $where filter is used to select the records which will be updated. The $input variable contains the properties which will be applied to the selected records. All of the records will receive the same update. ​ Example use case: ​

  • Mark all posts older than a year as "archived"
  • Set locale of users which are located at the office to English ​
const mutation = `
mutation {
  updateManyTask(where: $where, input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { where: { task: { eq: "Existing Task" } }, input: { task: "Updated Task" } });

Upsert a Task

​ This mutation can be used to insert or update a Task. The $input expects Task data, without id property, and the $uniqueBy field expects a list of property names. The properties on $uniqueBy will be used to identify if the record already exists, if it does, the record will be updated, otherwise a new record will be created. It is important to know that all fields used on $uniqueBy must also be included in the $input, so that we can check which value to use.

Example use case:

  • After importing data, you want to update a field of a specific record, but you don't know if the record is already imported, you can then select this record based on a different unique field. ​
const mutation = `
​
mutation {
  upsertTask(input: $input, uniqueBy: $uniqueBy) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { input: { taskId: "unique:123", task: "Updated Task" }, uniqueBy: ["taskId"]});

Upsert many Tasks

​ This mutation can be used to update or insert multiple Tasks at the same time. The $input expects a list of Task data, each with its own id and properties. For each item in the list, the mutation will first check if there is already a record with the same id. If a record exists, it will be updated with the data from the item. If there is no record with this id, it will be created with the given input. ​ Example use case: ​

  1. Fetch records from your inventory which the status "old" or "unprocessed"
  2. Process inventory items and set the new status
  3. Update the entire inventory in one mutation, using upsertMany ​
const mutation = `
​
mutation {
  upsertManyTask(input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { input: [
    { id: 1, task: "Mow lawn" },
    { id: 2, task: "Wash dishes" },
    { id: 3, task: "Build todo application" },
]});

Changing relationships

​ In create and update mutations, it is possible to add or remove relationships between records. The syntax for changing relationships is different for each type. Below you will find examples of how to change each relationship type. In this section, we will use the WebUser, Company and Role models as an example. ​

Belongs to

​ Belongs to relationships are set directly on the model which belongs to another model. Adding the id for the related model in the input will associate the new WebUser with Company 1. Updating relationships will use the same syntax. ​

const mutation = `
mutation {
  createWebUser(input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { input: {
  firstName: "Betty",
  company: {id: 1},
}});

Has many

​ The inverse of a belongs to relationship. In this example we are taking a Company and associating the WebUser with id 5. The WebUser will be detached from its current Company, and attached to Company 1. ​

const mutation = `
mutation {
  updateCompany(id: 1, input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, {
  input: {
    webUsers: { _add: [{ id: 5 }] },
  }
})

​ Here you'll notice the _add syntax, which allows you to add new relations to the existing record. Both the Has many and Has and belongs to many will have three different functions to manipulate the list of relations. The other two are _remove, which allows for removal of existing relations and _replace, which will completely replace the existing relations with the ones provided in the input. ​

Has and belongs to many

​ Here are some examples: ​

Creating a WebUser with roles

​ When creating a record you should use the "_add" operation: ​

const mutation = `
mutation {
  createWebUser(id: $id, input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { id: 1, input: {
  firstName: "Betty",
  ...
  roles: {_add: [{id: 1}, {id: 2}, {id: 3}]},
}})

Updating roles on a WebUser

​ Assume that the WebUser with id 1 has no roles to start with. Each mutation will be done in sequence, so we keep going with the result from each mutation: ​

const mutation = `
mutation {
  updateWebUser(id: $id, input: $input) {
    id
  }
}
`
​
const { data, errors } = await gql(mutation, { id: 1, input: {
  roles: {_add: [{id: 1}, {id: 2}, {id: 3}]},
}})

​ The user is now associated with the roles with ids 1, 2 and 3. ​

const { data, errors } = await gql(mutation, { id: 1, input: {
  roles: {_remove: [{id: 2}, {id: 3}]},
}})

​ The association with roles 2 and 3 are removed. That means the user is only associated with role 1. ​

const { data, errors } = await gql(mutation, { id: 1, input: {
  roles: {_replace: [{id: 3}, {id: 4}]},
}})

​ All existing roles on the user were removed, and the user is now associated with only roles 3 and 4. ​

Deprecated!

​ Below is an example of the old syntax for updating has and belongs to many relationships. It works the same way as the "_replace" method of the current API. This method will be removed in the future, so we strongly recommend you not to use it. ​

const { data, errors } = await gql(mutation, { id: 1, input: {
  roles: {id: [1,2,3]},
}})

Authentication

​ In order to send the mutations mentioned above, you don't need to specify authentication. These are only available from the Actions context, so authentication is handled by the Actions runtime. In most cases, authentication for the API can be done using the login mutation. This only concerns the HTTP API used in the PageBuilder/CustomFrontends. ​ There is also an additional login mutation available in Actions: generateJWT. This mutation allows you to generate a JWT for a given user, and you can optionally add custom fields to the resulting token. You can use the mutation to create custom authentication flows. Here is an example of how you can send the mutation: ​

const mutation = `
mutation {
  generateJwt(userId: $userId, authProfileUuid: $authProfileUuid, customFields: $customFields) {
    jwtToken
  }
}
`
​
const { data: {generateJwt: { jwtToken }} } = await gql(mutation, {
  userId: 1,
  authProfileId: "your-auth-profile-id",
  customFields: { some: ["extra", "fields"] }
})
​
// The JWT payload contains the extra fields
const payload = JSON.parse(atob(jwtToken.split('.')[1]))
​
console.log(payload.custom_fields) // { some: ["extra", "fields"] }