# Neurelo API Reference (GraphQL)

## Introduction

This API documentation is intended to provide an overview of how Neurelo generates APIs based on your data definition. It will go into detail on how to perform operations using a sample schema definition. You can then apply these concepts and patterns to your own schema. Reference documentation generated specifically for your data definition is included in your specific project on Neurelo's Data Access Platform.

Neurelo provides two types of APIs that are automatically generated and available for use.&#x20;

* The REST API has predictable resource-oriented URLs, accepts JSON-encoded request bodies, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.&#x20;
* The GraphQL API exposes a single endpoint where clients can send GraphQL queries or mutations and a strictly-typed schema that be be shared across consumers.&#x20;

You can toggle between the GraphQL and REST API documentation when reviewing the documentation in the platform.

{% hint style="info" %}
Reference - [Neurelo Supported Databases and Versions](https://docs.neurelo.com/references/supported-databases)
{% endhint %}

## API Headers

### <mark style="color:green;">Authentication</mark>

API calls to Neurelo are authenticated using an API Key for all API requests. This api key defines which environment the API request is interacting with. You can create an api key under the settings of the specific environment that you are trying to access. API Keys cannot be used across environments.

To utilize an api key, include it as a header with your API Request, using `X-API-KEY` as the key.

### <mark style="color:green;">Query Strategy</mark>

Neurelo generated APIs can be tuned to fetch data using either the `selects` or `joins` strategy. The main difference is that `selects` executes multiple queries behind the scenes to gather the desired data, whereas `joins` uses a single query when possible using database-level joins, to fetch the results.

By default, `joins` strategy is enabled. This can be changed using an HTTP header. For example if you are using cURL, then to fetch all the users and their corresponding posts using `selects` strategy:

```
curl -X POST https://$ENV_API_URL/graphql/
    --data '{"query": "query { findManyUser { name } }", "variables": {}}'
    --header 'X-READ-STRATEGY: selects'
```

Similarly, `'X-READ-STRATEGY: joins'` can be used.

`joins` strategy is suitable for scenarios where you want to minimize the number of queries made to the database, especially when the relation expressions are relatively simpler, and there's less risk of data duplication.

`selects` can be faster in scenarios with multiple many-to-many or one-to-many relations. This is because the database's flat record list from joins can sometimes contain a significant amount of repeated information. Despite joins' quick execution in the database, the process of moving and parsing this redundant data into JSON can incur high costs.

### <mark style="color:green;">Query Viz</mark>

To review the database queries that Neurelo will be sending to your data source, you can use the `X-QUERY-VIZ` HTTP header. For example, to enable query viz while fetching all the users and their corresponding posts:

```
curl https://$ENV_API_URL/graphql/
    --data '{"query": "query { findManyUser { name } }", "variables": {}}'
    --header 'X-QUERY-VIZ: true'
```

This header will return both the query string and its corresponding query parameters.

Similarly, `'X-QUERY-VIZ: false'` can be used to disable the viz. By default, this header is disabled.

{% hint style="danger" %}
IMPORTANT: When query viz is enabled, the queries will still run against your selected environment, and they may modify data in that environment depending on your queries.
{% endhint %}

## Operations

### <mark style="color:green;">Creating Objects</mark>

You can create objects by utilizing one of two mutations available for creating objects. These are `createOneObject` and `createManyObject`, where `Object` is the name of your object.

In this example, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a post has one user.

#### <mark style="color:purple;">Create one User</mark> (`CreateInput`)

To create a single user, assuming that we have the sample schema running in an environment, we can issue a `CreateOne` mutation, with the data argument set to the `UserCreateInput` input type. For example:

```
mutation {
  createOneUser(data: { name: "User 1", country: "Canada" }) {
    id
    name
    country
  }
}
```

The response from this insert request will look like this, which is the object that was inserted:

```
{
  "data": {
    "createOneUser": {
      "id": 1,
      "name": "Admin User",
      "country": "United States"
    }
  }
}
```

To prevent the insertion of records with unique fields or identifiers that already exist, we can use `skipDuplicates`. For example:

```
mutation {
  createOneUser(data: [{ id: 1, name: "User 1", country: "Canada" }, { id: 1, name: "User 2", country: "United States" }]) {
    id
    name
    country
  }
}
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "createOneUser": {
      "id": 1,
      "name": "Admin User",
      "country": "United States"
    }
  }
}
```

In the above example, as User IDs were same, the second record was prevented from being inserted.

NOTE: Currently, `skipDuplicates` is not supported for MongoDB.

#### <mark style="color:purple;">Creating multiple objects</mark> (`CreateManyInput`)

To create multiple users, assuming that we have the sample schema running in an environment, we can issue a `CreateMany` mutation, with the data argument set to an array of `UserCreateInput` types.

```
mutation {
  createManyUser(data: [{ name: "User 1", country: "Canada" }, { name: "User 2", country: "United States" }]) {
    id
  }
}
```

The response (`AffectedRowsOutput`) from this insert request will look like this, which is the count of the inserted rows:

```
{
  "data": {
    "createManyUser": {
      "count": 2
    }
  }
}
```

NOTE: Currently, related objects cannot be created or associated with the 'root' object using `CreateManyInput`. To achieve this, it is necessary to use `CreateInput`.

#### <mark style="color:purple;">Creating with relations</mark>

{% hint style="info" %}
Currently, Neurelo for MongoDB does not directly support the creation of objects with relations. However, you can create inner objects (which are equivalent to embedded documents, meaning documents nested within another document) or use references to accommodate your data modeling needs. Refer to the [Modeling Relationships for MongoDB with Inner Objects and References section](#modeling-relationships-for-mongodb-with-inner-objects-and-references) for more information.
{% endhint %}

When creating a single user, you can create or connect related object in the same transaction.

On an object with a has one relationship to another object, you can use the `create` input to create a new related object automatically associated with the 'root' object. Alternatively, you can use the `connect` input to link an existing related object to the 'root' object. When using `connect`, an error will be thrown if the linked object is not found. To address this, use `connectOrCreate`, which links an existing related object to the 'root' object or creates a new related object if it doesn't exist.

On an object with a has many relationship to another object, you can also utilize `create`, `connect`, and `connectOrCreate`. Additionally, you can use `createMany` to create many related objects.

#### `create`

To demonstrate creating a root object and its related object, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a post has one user.

```
mutation {
  createOneUser(data: { name: "User 1", country: "Canada", posts: { create: [{ title: "Post Title" }] } }) {
    id
    name
    posts {
      id
      title
    }
  }
}
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "createOneUser": {
      "id": 1,
      "name": "User 1",
      "posts": [
        {
          "id": 1,
          "title": "Post Title"
        }
      ]
    }
  }
}
```

#### `connect`

To create a root object and link it with an existing related object in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `connect` input.

```
mutation {
  createOneUser(data: { name: "User 1", country: "Canada", posts: { connect: [{ id: 1 }] } }) {
    id
    name
    country
    posts {
      id
      title
    }
  }
}
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "createOneUser": {
      "id": 1,
      "name": "User 1",
      "country": "Canada",
      "posts": [
        {
          "id": 1,
          "title": "Post Title"
        }
      ]
    }
  }
}
```

In the above example, the Post with an ID of 1 previously existed and is now being associated with a relationship with the new root object.

#### `connectOrCreate`

To create a root object and link it with an existing related object or create a new related object if it doesn't exist (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `connectOrCreate` input. For example,

```
mutation {
  createOneUser(data: { name: "User 1", country: "Canada", posts: { connectOrCreate: [{ create: { title: "Post 1" }, where: { id: 3 } }] } }) {
    id
    name
    country
    posts {
      id
      title
    }
  }
}
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "createOneUser": {
      "id": 1,
      "name": "User 1",
      "country": "Canada",
      "posts": [
        {
          "id": 2,
          "title": "Post 1"
        }
      ]
    }
  }
}
```

In the above example, by observing the Post's ID, we can conclude that the ID 3 was not found in the Post object. As a result, a new object was created.

#### `createMany`

To create a root object and link it with many related objects in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `createMany` input.

```
mutation {
  createOneUser(data: { name: "User 1", country: "Canada", posts: { createMany: { data: [{ title: "Post Title 1" }, { title: "Post Title 2" }] } } }) {
    id
    name
    country
    posts {
      id
      title
    }
  }
}
```

The response from this insert request will look like this, which is the inserted object(s):

```
{
  "data": {
    "createOneUser": {
      "id": 1,
      "name": "User 1",
      "country": "Canada",
      "posts": [
        {
          "id": 1,
          "title": "Post Title 1"
        },
        {
          "id": 2,
          "title": "Post Title 2"
        }
      ]
    }
  }
}
```

Similar to `createInput`, you can use `skipDuplicates` in `createMany` to prevent the insertion of records with unique fields or identifiers that already exist.

### <mark style="color:green;">Retrieving Objects</mark>

You can retrieve objects by utilizing a few different queries that are generated for each object. There are three find queries available per object, they are `findFirstObject`, `findUniqueObject` and `findManyObject`.

In this example, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a BlogPost has one user. A user has a relationship property to posts called `posts` and the post has a relationship property to users called `author`.

#### <mark style="color:purple;">Retrieving unique object</mark>

To retrieve a unique user, assuming that we have the sample schema running in an environment, we can issue a query to `findUniqueUser` with a `where` argument that filters on unique properties. For example:

```
query {
  findUniqueUser(where: { id: 1 }) {
    name
  }
}
```

The response from this request will look like this, which is the retrieved object:

```
{
  "data": {
    "findUniqueUser": {
      "name": "User 1"
    }
  }
}
```

#### <mark style="color:purple;">Retrieving all objects</mark>

To retrieve all users, assuming that we have the sample schema running in an environment, we can issue a query with `findManyUser`. For example:

```
query {
  findManyUser {
    name
  }
}
```

The response from this request will look like this, which is an array of the retrieved objects:

```
{
  "data": {
    "findManyUser": [
      {
        "name": "User 1"
      },
      {
        "name": "User 2"
      }
    ]
  }
}
```

#### <mark style="color:purple;">Filtering for specific objects</mark>

To filter specific objects, we can utilize the `where` query argument. For complete documentation of all the options available to you with the query argument, take a look at the `WhereInput` input type documentation below.

To filter for specific User objects, assuming that we have the sample schema running in an environment, we can issue a query with the `where` argument set to the `UserWhereInput` input type.

```
query {
  findManyUser(where: { name: { contains: "Jane" } }) {
    name
  }
}
```

The response from this request will look like this, which is an array of the retrieved objects:

```
{
  "data": {
    "findManyUser": [
      {
        "name": "Jane"
      }
    ]
  }
}
```

#### <mark style="color:purple;">Retrieving related objects</mark>

To retrieve all users with their posts, assuming that we have the sample schema running in an environment, we can issue a query with `findManyUser`. For example:

```
query {
  findManyUser {
    name
    posts {
      name
      words
    }
  }
}
```

The response from this request will look like this, which is an array of the retrieved objects and their related objects:

```
{
  "data": {
    "findManyUser": [
      {
        "name": "User 1",
        "posts": [
          {
            "name": "My Trip to Egypt",
            "words": 1200
          },
          {
            "name": "My Trip to Canada",
            "words": 800
          }
        ]
      },
      {
        "name": "User 2",
        "posts": [
          {
            "name": "Greek mythology: Part 1",
            "words": 1000
          }
        ]
      }
    ]
  }
}
```

#### <mark style="color:purple;">Retrieving n-levels of related objects</mark>

Assume that in our sample schema, there is a new object Activity, such that each post has many activities. A post has a relationship property to activities called `activity`.

To retrieve all the users, their post names, and activities on each post, assuming that we have the sample schema running in an environment, we can issue a query with `findManyUser`. For example:

```
query {
  findManyUser {
    name
    posts {
      name
      activity {
        name
        action
      }
    }
  }
}
```

The response from this request will look like this, which is an array of the retrieved objects and their specific related objects:

```
{
  "data": {
    "findManyUser": [
      {
        "name": "User 1",
        "posts": [
          {
            "name": "My Trip to Egypt",
            "activity": [
              {
                "name": "User 123",
                "action": "Like"
              },
              {
                "name": "User 234",
                "action": "Share"
              }
            ]
          },
          {
            "name": "My Trip to Canada",
            "activity": [
              {
                "name": "User 345",
                "action": "Like"
              }
            ]
          }
        ]
      }
    ]
  }
}
```

### <mark style="color:green;">Updating Objects</mark>

You can update objects by using one of two update mutations, these are similar to the create mutations, but include a `where` argument to specify which objects should be updated. There are `updateOneObject` and a `updateManyObject` mutations available, `Object` being replaced by the object that you are performing the update on.

#### <mark style="color:purple;">Updating unique object</mark> (`UpdateInput`)

Updating a unique object is similar to updating many, except that the `where` argument is limited to filtering on unique properties. For example, the `id` property on User is a unique property and is guaranteed to be unique to a single User object.

```
mutation {
  updateOneUser(data: { name: "John" }, where: { id: 1 }) {
    name
  }
}
```

The response from this request will look like this, which is the updated object:

```
{
  "data": {
    "updateOneUser": {
      "id": 1,
      "name": "John"
    }
  }
}
```

#### <mark style="color:purple;">Updating multiple objects</mark> (`UpdateManyInput`)

To update multiple users, we can utilize the `updateManyObject` mutation with the `data` and `where` arguments to provide both our updated properties and a condition set to limit which objects are updated.

```
mutation {
  updateManyUser(data: { name: "John" }, where: { name: { contains: "Jane " } }) {
    count
  }
}
```

The response (`AffectedRowsOutput`) from this update request will look like this, which is the count of the updated rows:

```
{
  "data": {
    "updateManyUser": {
      "count": 1
    }
  }
}
```

#### <mark style="color:purple;">Updating scalars using special operations</mark>

#### `push`

When using the `array` type for a property, the `UpdateInput` provides a `push` operation. This inserts a value or array of values at the end of the array. If the value is null, it would initialize the array with the value(s) provided. For example,

```
mutation {
  updateOnePost(data: { name: "My updated .bashrc file", tags: { push: ["alias"] } }) {
    id
    name
    tags
  }
}
```

The response from this request will look like this, which is the updated object:

```
{
  "data": {
    "id": 1,
    "name": "My updated .bashrc file",
    "tags": ["termcolor", "alias"]
  }
}
```

#### <mark style="color:purple;">Updating with relations</mark>

{% hint style="info" %}
Currently, Neurelo for MongoDB does not directly support the updation of objects with relations. However, you can update inner objects (which are equivalent to embedded documents, meaning documents nested within another document) or use references to accommodate your data modeling needs. Refer to the [Modeling Relationships for MongoDB with Inner Objects and References section](#modeling-relationships-for-mongodb-with-inner-objects-and-references) for more information.
{% endhint %}

When updating a single object, you can create, update, disconnect or delete related object in the same transaction.

On an object with a has one relationship to another object, you can use the `update` input to update a related object automatically associated with the 'root' object. Alternatively, you can use the `disconnect` input to unlink an existing related object with the 'root' object. When using `update`, an error will be thrown if the linked object is not found. To address this, use `upsert`, which updates an existing related object to the 'root' object or creates a new related object if it doesn't exist. You can also use `delete` input to delete an existing related object.

The `connect`, `connectOrCreate`, and `create` inputs (as explained in the Creating objects with relations section) are also supported here.

On an object with a has many relationship to another object, you can also utilize `create`, `connect`, `connectOrCreate`, `update`, `upsert`, `delete`, and `disconnect` inputs. Additionally, you can use `createMany`, `updateMany`, and `deleteMany` to create, update, and delete many related objects, respectively.

`createMany` is explained in the Creating objects with relations section.

#### `update`

To demonstrate updating a root object and its related object, we will use a sample schema with a User object and a BlogPost object. A user has many posts, and a post has one user.

```
mutation {
  updateManyUser(where: { id: 1 }, data: { name: "Admin User 1", posts: { update: { where: { id: 2 }, data: { name: "My system configuration" } } } }) {
    id
    name
    posts {
      id
      name
    }
  }
}
```

The response from this update request will look like this, which shows the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      },
      {
        "id": 2,
        "name": "My system configuration"
      }
    ]
  }
}
```

In the above example, the Post with an ID of 2 previously existed and is now being updated along with the name of the user.

#### `upsert`

To update a root object and update its corresponding related object or create a new related object if it doesn't exist (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `upsert` input. For example,

```
mutation {
  updateManyUser(where: { id: 1 }, data: { name: "Admin User 1", posts: { upsert: { where: { id: 2 }, update: { name: "Hardware Info" }, create: { name: "Hardware Info" } } } }) {
    id
    name
    posts {
      id
      name
    }
  }
}
```

The response from this upsert request will look like this, which is the upsert object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      },
      {
        "id": 2,
        "name": "Hardware Info"
      }
    ]
  }
}
```

#### `disconnect`

To unlink an existing related object with the 'root' object (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `disconnect` input. For example,

```
mutation {
  updateManyUser(where: { id: 1 }, data: { name: "Admin User 1", posts: { disconnect: { id: 2 } } }) {
    id
    name
    posts {
      id
      name
    }
  }
}
```

The response from this request will look like this, which is the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      }
    ]
  }
}
```

In the above example, the Post with an ID of 2 previously existed and still exists, but it is now being unlinked with respect to the 'root' object. Furthermore, the name of the user also gets updated.

#### `delete`

To delete an existing related object asccociated with a 'root' object (when the root and related object are in a has many relationship), assuming that we have the sample schema running in an environment, we can use the `delete` input. For example,

```
mutation {
  updateManyUser(where: { id: 1 }, data: { name: "Admin User 1", posts: { delete: { id: 2 } } }) {
    id
    name
    posts {
      id
      name
    }
  }
}
```

The response from this request will look like this, which is the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test"
      }
    ]
  }
}
```

In the above example, the Post with an ID of 2 is now deleted. Furthermore, the name of the user also gets updated.

#### `updateMany`

To update a root object and its many related objects in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `updateMany` input.

```
mutation {
  updateManyUser(where: { id: 1 }, data: { name: "Admin User 1", posts: { updateMany: [{ where: { id: 1 }, data: { name: "Test Blog" } }, { where: { id: 2 }, data: { name: "Hardware Info Blog" } }] } }) {
    id
    name
    posts {
      id
      name
    }
  }
}
```

The response from this updateMany request will look like this, which is the updated object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User 1",
    "posts": [
      {
        "id": 1,
        "name": "Test Blog"
      },
      {
        "id": 2,
        "name": "Hardware Info Blog"
      }
    ]
  }
}
```

#### `deleteMany`

To update a root object and delete its many related objects in a has many relationship, assuming that we have the sample schema running in an environment, we can use the `deleteMany` input.

```
mutation {
  updateManyUser(where: { id: 1 }, data: { name: "Admin User Empty", posts: { deleteMany: [{ id: 1 }, { id: 2 }] } }) {
    id
    name
    posts {
      id
      name
    }
  }
}
```

The response from this deleteMany request will look like this, which is the deleted object(s):

```
{
  "data": {
    "id": 1,
    "name": "Admin User Empty",
    "posts": []
  }
}
```

### <mark style="color:green;">Deleting Objects</mark>

You can delete objects by utilizing one of two mutations. These mutations are `deleteOneObject` and a `deleteManyObject` mutations available, where `Object` is the name of the object that you are deleting.

In this example, we will use a sample schema with a User object.

#### <mark style="color:purple;">Delete unique object</mark>

To delete a unique user, assuming that we have the sample schema running in an environment, we can issue a query to `deleteOneUser`. For example:

```
mutation {
  deleteOneUser(where: { id: 1 }) {
    id
    name
    country
  }
}
```

The response from this request will look like this, which is the deleted object:

```
{
  "data": {
    "id": 1,
    "name": "User 1",
    "country": "Canada"
  }
}
```

#### <mark style="color:purple;">Delete all objects</mark>

To delete all users, you can issue a `deleteManyUser` mutation with arguments present. For example:

```
mutation {
  deleteManyUser {
    count
  }
}
```

The response from this request will look like this, which is the count of the deleted object(s):

```
{
  "data": {
    "deleteManyUser": {
      "count": 1
    }
  }
}
```

#### <mark style="color:purple;">Delete specific objects</mark>

To delete specific objects, we can utilize the `where` argument. For complete documentation of all the options available to you with the query argument, take a look at the `WhereInput` input type documentation below.

To retrieve all specific, we can issue `deleteManyUser` with the where argument specified. For example:

```
mutation {
  deleteManyUser(where: { name: { contains: "John" } }) {
    count
  }
}
```

The response from this request will look like this, which is the count of the deleted object(s):

```
{
  "data": {
    "deleteManyUser": {
      "count": 1
    }
  }
}
```

### <mark style="color:green;">Aggregating Objects</mark>

You can aggregate objects by utilizing the `aggregateObject` query that is generated for each object. The following aggregate functions are supported: `_count`, `_sum`, `_avg`, `_min`, and `_max`.

In this example, we will use a sample schema with a Post object.

#### <mark style="color:purple;">Aggregating specific objects</mark>

To aggregate post objects, assuming that we have the sample schema running in an environment, we can issue a query with `aggregatePost`. For example, to average the number of words for post objects:

```
query {
  aggregatePost {
    _avg {
      words
    }
  }
}
```

The response (`AggregateOutput`) from this request will look like this, which contains the aggregated value:

```
{
  "data": {
    "aggregatePost": {
      "_avg": {
        "words": 1000.0
      }
    }
  }
}
```

#### <mark style="color:purple;">Aggregating all objects</mark>

Presently, only `_count` function supports aggregating all objects.

To count all objects, assuming that we have the sample schema running in an environment, we can issue a query with `aggregatePost` with the `_count` function and `_all` value specified for it. For example,

```
query {
  aggregatePost {
    _count {
      _all
    }
  }
}
```

The response (`AggregateOutput`) from this request will look like this, which contains the aggregated value:

```
{
  "data": {
    "aggregatePost": {
      "_count": {
        "_all": 2
      }
    }
  }
}
```

### <mark style="color:green;">Grouping Objects</mark>

In this example, we will use a sample schema with a User object.

You can group by objects by utilizing the `groupByObject` query that is generated for each object. It is mandatory to include a `by` option to group records by one or more fields. This option takes an array of field names (`Array`), to specify the fields to group by with.

For example, to count the number of users by country:

```
query {
  groupByUser(by: [country]) {
    country
    _count {
      id
    }
  }
}
```

The response from this request will look like this, which is the grouped and aggregated object(s):

```
{
  "data": {
    "groupByUser": [
      {
        "country": "United States",
        "_count": {
          "id": 1
        }
      },
      {
        "country": "Canada",
        "_count": {
          "id": 1
        }
      }
    ]
  }
}
```

#### <mark style="color:purple;">Group By with Filtering</mark>

To filter the fields before grouping, we can utilize the `where` option. For example,

```
query {
  groupByUser(where: { country: { startsWith: "C" } }, by: [country]) {
    country
    _count {
      id
    }
  }
}
```

The response from this request will look like this, which is the grouped and aggregated object:

```
{
  "data": {
    "groupByUser": [
      {
        "country": "Canada",
        "_count": {
          "id": 1
        }
      }
    ]
  }
}
```

To filter the groups by aggregate functions, we can utilize the `having` option.

```
query {
  groupByUser(having: { id: { _count: { lte: 2 } } }, by: [country]) {
    country
    _count {
      id
    }
  }
}
```

The response from this request will look like this, which is the grouped and aggregated object(s):

```
{
  "data": {
    "groupByUser": [
      {
        "country": "United States",
        "_count": {
          "id": 1
        }
      },
      {
        "country": "Canada",
        "_count": {
          "id": 1
        }
      }
    ]
  }
}
```

## Input Types

In this section, we’ll take a look at some arguments that apply to many different operations. The avaliability of each argument is dependent on the operation being performed, for example, the `WhereInput` argument is not available on Insert operations.

Once you are familiar with these arguments, we hope that utilizing individual operations on your objects comes naturally and will not require continous referral to the generated reference documentation.

### <mark style="color:green;">`where`</mark> <mark style="color:green;"></mark><mark style="color:green;">(</mark><mark style="color:green;">`WhereInput`</mark><mark style="color:green;">)</mark>

The `WhereInput` argument allows you to add conditions to your query. This argument can be used when retrieving objects, as well as when updating objects where it will act as the condition on which objects are updated.

#### <mark style="color:purple;">Filtering with Scalars</mark>

Each scalar has it’s own set of operations that can be performed to assert a condition, for example, an integer has the following operators available to it: `equals`, `not`, `in`, `notIn`, `lt`, `lte`, `gt`, `gte`. With the exception of `in` and `notIn`, all of these operators will accept the matching scalar, `in` and `notIn` will take an array of the matching scalar.

Here is a list of all available operators: `equals`, `not`, `in`, `notIn`, `lt`, `lte`, `gt`, `gte`, `contains`, `startsWith`, and `endsWith`. The availiblity of these is dependant on the scalar type, for example, you cannot utilize the `gt` operator on a string.

A where condition for a given object includes all of the scalars present on the object with their respective scalar filter type. To construct a where condition utilizing only an object’s scalar properties, you can use the name of the property as the key, and then a where object for that specific scalar type.

Here is a more comprehensive overview of these operators:

| Operator            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | Example                                                                                                                                                                                                                                                                               |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| equals              | Filter for field values equal to a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | To filter when `age` equals 21, `{ age: { equals: 21 } }`                                                                                                                                                                                                                             |
| lt/lte              | Filter for field values "less than / less than or equal to" a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | To filter when `age` is less than 21, `{ age: { lt: 21 } }`                                                                                                                                                                                                                           |
| gt/gte              | Filter for field values "greater than / greater than or equal to" a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | To filter when `age` is greater than or equal to 30, `{ age: { gte: 30 } }`                                                                                                                                                                                                           |
| not                 | Filters when field value does not equal a specific value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | To filter records that have all ids except `100`, `{id: {not: 100}}`                                                                                                                                                                                                                  |
| in/notIn            | Checks if a field value exists / does not exist in a list, and filters accordingly.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | To filter records having ids as either `1`, `3` or `6`, `{ id: { in: [1, 3, 6] } }`                                                                                                                                                                                                   |
| startsWith/endsWith | Filter when a specific value is at the start or end of a field value. Case-sensitive by default. `_` wildcard can be used to match one or more characters. `$` wildcard can be used to match zero or more characters.                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | To filter names that start with "Ron" (case-insensitive), `{name: {startsWith: "Ron", mode: "insensitive"} }`. To filter names that start with "R", followed by one or more characters, and then "d", `{name: {startsWith: "R_d"} }`. This matches names such as "Roland" and "Rudd". |
| contains            | Filters when a specific value is contained in a field value. Case-sensitive by default. `_` wildcard can be used to match one or more characters. `$` wildcard can be used to match zero or more characters.                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | To filter country names that contain the word "United", `{country: {contains: "United" } }`                                                                                                                                                                                           |
| search              | Filters using full-text search capabilities. This operator is specific to strings and currently, it is not supported with MongoDB. Currently, this operator is not supported with MongoDB. For querying, PostgreSQL and MySQL's native full-text search capabilities are leveraged. For example, in Postgres, `&` can be used to perform a boolean AND on two strings. Similarly, `\|` and `!` can be used to perform boolean OR and boolean NOT, respectively. For MySQL, `+` and `-` are used as boolean AND and boolean NOT, respectively. Boolean OR in MySQL is represented using no operators; for example, `Tomato Orange` would check if the text contains `Tomato` or `Orange`. | In PostgreSQL, to filter records with the engine as Firefox or Chrome, use `{engine: {search: "Firefox \| Chrome"}}`. In MySQL, to filter records with Firefox but not Chrome in the report, use `{report: {search: "+Firefox -Chrome"}}`.                                            |

#### <mark style="color:purple;">Combining conditions</mark>

When you add conditions to a `WhereInput` object, you are asserting that all of the listed conditions must evaluate to true for an object to be returned, but you are also able to further customize your query’s condition by utilizing the `AND`, `OR`, and `NOT` properties. All of these properties accept an array of the object’s WhereInput type, allowing you to nest and compose different filtering conditions to construct your query.

Here is a more comprehensive overview of these operators:

| Operator | Description                                                  | Example                                                                                                                                                                                                                                                                                                                                                               |
| -------- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| OR       | Filters for field values when one or more conditions is true | To filter names starting with either "A" or "B", `{OR: [{name: {startsWith: "A"}}, {name: {startsWith: "B"}}]}`. Furthermore, to filter names starting with "A" or ending with "n", `{OR: [{name: {startsWith: "A"}}, {name: endsWith: "n"}]}`.                                                                                                                       |
| AND      | Filters for field values when all conditions are true        | To filter for first names starting with "A" and last names ending with "n", `{AND: [{firstName: {startsWith: "A"}}, {lastName: {endsWith: "n"}}]}`. Furthermore, to filter names starting with "B" and ending with "n", we can use operators such as `startsWith` and `endsWith` in a single object itself, like `{AND: [{name: {startsWith: "B", endsWith: "n"}}]}`. |
| NOT      | Filters for field values when all the conditions are false   | To filter for names that are not "Alan" and not "George", `{NOT: [{name: {equals: "Alan"}}, {name: {equals: "George"}}]}`. Furthermore, to filter names not starting with "C" and not ending with "n", we can use operators such as `startsWith` and `endsWith` in a single object itself, like `{NOT: [{name: {startsWith: "C", endsWith: "n"}}]}`.                  |

Note on Scope of Logical operators:

* Implicit AND: Combining conditions within one object, `{name: {startsWith: "A", endsWith: "n"}}`, uses an implicit `AND` logic, meaning all conditions must be met.
* Explicit Logical Operators: Using `OR`, `AND`, or `NOT` to combine multiple conditions, such as `{OR: [ {...}, {...} ]}`, processes each condition in its own scope before applying the logical operator. This structure allows for complex filtering, evaluating from the inner-most conditions outwards.

Furthermore, you can combine these operators to do more powerful filtering. For example,

* `NOT` and `AND`: To filter for names that do no start with "A" and end with "n", `{NOT: {AND: [{name: {startsWith: "A"}}, {name: {endsWith: "n"}}]}}`.
* `OR` and `AND`: To filter for ids that are between 0 to 10 or 30 to 40, `{OR: [{AND: [{id: {gte: 0}}, {id: {lte: 10}}]}, {AND: [{id: {gte: 30}}, {id: {lte: 40}}]}]}`.

#### <mark style="color:purple;">Filtering with List</mark>

You can use the following operators to perform filtering with lists: `has`, `hasSome`, `hasEvery`, and `isEmpty`.

Additionally, you can employ the `equals` operator introduced earlier.

Here is a more comprehensive overview of these operators:

| Operator | Description                                                             | Example                                                                                                                                          |
| -------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| has      | Filters for lists where a given value exists.                           | To filter when `Canada` is in a `locations` list, `{locations: {has: "Canada"}}`                                                                 |
| hasSome  | Filters for lists where at least one value from the search list exists. | To filter when `Canada` or `United States` are in a `locations` list, `{locations: {hasSome: ["Canada", "United States"]}}`                      |
| hasEvery | Filters for lists where all values from the search list exists.         | To filter when `Canada`, `United States`, and `Spain` are in a `locations` list, `{locations: {hasEvery: ["Canada", "United States", "Spain"]}}` |
| isEmpty  | Filters for empty lists.                                                | To filter for empty `locations` list, `{locations: {isEmpty: true}}`. Similarly, to filter for non-empty lists, use `{isEmpty: false}`.          |

#### <mark style="color:purple;">Filtering with JSON</mark>

You can use the following operators to perform JSON filtering: `array_starts_with`, `array_ends_with`, `array_contains`, `string_starts_with`, `string_ends_with`, and `string_contains`.

Additionally, you can employ operators introduced earlier, such as `equals`, `gt`, `gte`, `lt`, `lte`, and `not`.

To filter a specific part of the JSON object, each of these operators requires a specified `path`. For example, consider a JSON object like `{Produce: {Fruits: {Organic: ["Orange", "Mango", "Banana"], NonOrganic: ["Apple", "Melon"]}}}`. In Postgres, the path `Produce -> Fruits -> Organic` can be represented as `["Produce", "Fruits", "Organic"]`. Similarly, for MySQL, it can be represented as `'$.Produce.Fruits.Organic'`.

Here is a more comprehensive overview of these operators (all the paths are specific to Postgres):

| Operator             | Description                                                                                     | Example                                                                                                                                                                                         |
| -------------------- | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| array\_starts\_with  | Filters for JSON objects where a specified path has a list that begins with a specific value.   | To filter when the list at `["Fruits", "Organic"]` path starts with `Orange`, `{Crops: {path: ["Fruits", "Organic"], array_starts_with: "Orange"}}`                                             |
| array\_ends\_with    | Filters for JSON objects where a specified path has a list that ends with a specific value.     | To filter when the list at `["Fruits", "Organic"]` path ends with `Apple` , `{Crops: {path: ["Fruits", "Organic"], array_starts_with: "Apple"}}`                                                |
| array\_contains      | Filters for JSON objects where a specified path has a list that contains a specific value.      | To filter when the list at `["Schedule", "Heathrow"]` path contains 9 , `{Misc: {path: ["Schedule", "Heathrow"], array_contains: 9}}`                                                           |
| string\_starts\_with | Filters for JSON objects where a specified path has a string that begins with a specific value. | To filter when the string at `["Premium Customers", "NYC", "George"]` path starts with `Q2F0cw==` , `{IDs: {path: ["Premium Customers", "NYC", "George"], string_starts_with: "Q2F0cw=="}}`     |
| string\_ends\_with   | Filters for JSON objects where a specified path has a string that ends with a specific value.   | To filter when the string at `["Premium Customers", "NYC", "George"]` path ends with `TmV1cmVsbw==` , `{IDs: {path: ["Premium Customers", "NYC", "George"], string_ends_with: "TmV1cmVsbw=="}}` |
| string\_contains     | Filters for JSON objects where a specified path has a string that contains a specific value.    | To filter when the string at `["FullReport", "Browser"]` path contains `Firefox` , `{Details: {path: ["FullReport", "Browser"], string_contains: "Firefox"}}`                                   |

#### <mark style="color:purple;">Filtering with Relations</mark>

You are able to utilize related objects in a condition too, though the manner in which you do so will depend on the type of relationship that this object has with it’s related object.

When a relationship has one of another object, you can utilize the `is` and `isNot` filters to construct conditions on a related object. For example, if we have a Post object, which has one User object, we can create a condition utilizing the User’s objects properties like so: `{ user: { is: { name: { equal: "George" } } } }`. This filter would return all Post objects where the related User’s name is equal to George.

When a relationship has many of another object, you can utilize the `some`, `every`, and `none` conditions to construct conditions on a set of related objects. For example, if we have a User object that has many Posts, we can create a condition utilizing the Post’s objects properties like so:`{ posts: some: [ { title: { equal: "Cats" } } ] }`. This will return a list of users where at least one of their posts has the title of “Cats”.

Here is a more comprehensive overview of these operators:

| Operator | Description                                                                                                                                        | Example                                                                                                                                                         |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| is       | Filters a record when related record "is" equal to the filter condition. Can only be used when a relationship has one of another object.           | If every user has one post, to filter when name equals Alan, `{ user: { is: { name: { equal: "Alan" } } } }`                                                    |
| isNot    | Filters a record when related record "is not" equal to the filter condition. Can only be used when a relationship has one of another object.       | If every user has one post, to filter when name does not start with "A", `{ user: { isNot: { name: { startsWith: "A" } } } }`                                   |
| some     | Filters a record when related record has "some" filter conditions as true. Can only be used when a relationship has many of another object.        | If a brand has many sauces, to find out brands that have "Paprika" in some of their sauces, `{ sauces: { some: [ { ingr: { contains: "paprika" } } ] } }`       |
| every    | Filters a record when related record has "every" filter conditions as true. Can only be used when a relationship has many of another object.       | If a brand has many sauces, to find out brands that have "Paprika" in all of their sauces, `{ sauces: { every: [ { ingr: { contains: "paprika" } } ] } }`       |
| none     | Filters a record when related record has "none" of its filter conditions as true. Can only be used when a relationship has many of another object. | If a brand has many sauces, to find out brands that do not have "Paprika" in any of their sauces, `{ sauces: { none: [ { ingr: { contains: "paprika" } } ] } }` |

Note: You can combine the three sections above to construct complex queries.

### <mark style="color:green;">`order_by`</mark> <mark style="color:green;"></mark><mark style="color:green;">(</mark><mark style="color:green;">`OrderByWithRelationInput`</mark><mark style="color:green;">)</mark>

The `orderBy` argument takes an array of objects, with the following definition:

`{ [key: ScalarName]: "asc" | "desc" }`

You can use any scalar on the object being queried as a key, along with the direction of the sort as the value in the object. Our GraphQL APIs support ordering of nested fields.

For example, given a sample schema with a User object:

```
{
  findManyUser(orderBy: { id: desc }) {
    id
    name
    posts(orderBy: { id: asc }) {
      id
      name
    }
  }
}
```

The response from this request will look like this:

```
{
  "data": {
    "findManyUser": [
      {
        "id": 2,
        "name": "User 2",
        "posts": [
          {
            "id": 1,
            "name": "Post 1"
          },
          {
            "id": 2,
            "name": "Post 2"
          }
        ]
      },
      {
        "id": 1,
        "name": "User 1",
        "posts": []
      }
    ]
  }
}
```

### <mark style="color:green;">`skip`</mark> <mark style="color:green;"></mark><mark style="color:green;">&</mark> <mark style="color:green;"></mark><mark style="color:green;">`take`</mark>

Pagination with Neurelo’s APIs works using the `skip` and `take` options as an implementation of offset based pagination. You can pass an integer to each option to limit or skip the resulting objects.

For example, given a sample schema with a User object, to skip one record and take the next record:

```
{
  findManyUser(skip: 1, take: 1) {
    id
    name
  }
}
```

The response from this request will look like this:

```
{
  "data": {
    "findManyUser": [
      {
        "id": 2,
        "name": "User 2"
      }
    ]
  }
}
```

### <mark style="color:green;">`by`</mark>

The `by` option takes an array of field names (`Array`), to specify the fields to group by with. It must be ensured that every selected scalar field in your graphql body that is not part of an aggregation must be included in `by` option's array of field names.

For example, let us use a sample schema with a User object,

```
# If the selected scalar fields in graphql body are name and country, then by needs to equal ["name", "country"].
query {
  groupByUser(by: [name, country]) {
    name
    country
    _count {
      id
    }
  }
}
```

The response from this request will look like this:

```
{
  "data": {
    "groupByUser": [
      {
        "country": "United States",
        "_count": {
          "id": 1
        }
      },
      {
        "country": "Canada",
        "_count": {
          "id": 1
        }
      }
    ]
  }
}
```

### <mark style="color:green;">`having`</mark> <mark style="color:green;"></mark><mark style="color:green;">(</mark><mark style="color:green;">`HavingInput`</mark><mark style="color:green;">)</mark>

`having` option is used in conjuction with `by` option to filter the groups by aggregate functions. It is a special kind of `where` which accepts all the `where` options, together with the ability to filter aggregate functions.

To filter by aggregate function, the following definition can be used:

`{ [field: ScalarName]: { [key: aggregateFunction]: whereCondition } }`

Supported aggregate functions are: `_count`, `_sum`, `_avg`, `_min`, and `_max`. whereCondition is the same as a `where` body and accepts all the `where` options.

For example, let us use a sample schema with a Post object,

```
query {
  groupByPost(by: [location], having: { num_words: { _sum: { gte: 1000000 } } }) {
    location
    _sum {
      num_words
    }
  }
}
```

The response from this request will look like this, which is the grouped and aggregated object(s):

```
{
  "data": {
    "groupByPost": [
      {
        "location": "United States",
        "_sum": {
          "id": 5179839
        }
      },
      {
        "location": "India",
        "_sum": {
          "id": 3044357
        }
      }
    ]
  }
}
```

Furthermore, similar to `filter`, you can use `AND`, `OR`, and `NOT` operators to do more powerful filtering. For example, to filter when the number of words is between 1000 and 10000, you can use `{"OR": [{"id": {"_sum": {"gte": 1000}}}, {"id": {"_sum": {"lte": 10000}}}]}`.

### <mark style="color:green;">`cursor`</mark>

`cursor` option is used to retrieve results before or after a specified cursor. This cursor is a unique identifier or a combination of unique identifiers.

Implicitly an ordering is performed based on the unique identifiers specified in the cursor, to ensure a consistent and predictable sequence of results.

For example, let us consider a sample schema with a User object. To fetch all users with IDs less than or equal to 2, the following can be used:

```
query {
  findManyUser(cursor: { id: 2 }, orderBy: { id: desc }) {
    id
    name
  }
}
```

The response from this request will look like this:

```
{
  "data": {
    "findManyUser": [
      {
        "id": 2,
        "name": "User 2"
      },
      {
        "id": 1,
        "name": "User 1"
      }
    ]
  }
}
```

Furthermore, to fetch the user after skipping two users, starting from ID 3, we can use the following:

```
query {
  findManyUser(cursor: { id: 3 }, skip: 2, take: 1) {
    id
    name
  }
}
```

The response from this request will look like this:

```
{
  "data": {
    "findManyUser": [
      {
        "id": 5,
        "name": "User 5"
      }
    ]
  }
}
```

In the above example, User IDs 3 and 4 were skipped, and the result set of User ID 5 was returned.

NOTE: Currently, the REST APIs do not have an equivalent functionality. As a workaround, use `order_by` to sort the primary keys, followed by `filter` to exclude the primary keys. For instance, `cursor: { id: 3 }` would have an equivalent of `order_by=[{"id": "asc"}]` with `filter={"id": {"gte": 3}}`.

### Modeling Relationships for MongoDB with Inner Objects and References

Currently, Neurelo for MongoDB doesn't support direct manipulation of objects with relations. However, you can work with inner objects (equivalent to embedded documents, i.e., documents nested within another document) or use references to model such relationships.

To decide which data modeling approach suits your needs best, refer to our guide on [how to work with embedded documents and references in MongoDB](https://docs.neurelo.com/guides/how-to-work-with-embedded-documents-and-references-in-mongodb).

In summary, inner objects enhance read performance by reducing database queries and ensuring atomic updates within a single document. They are optimal for closely related data that is frequently accessed together, remains relatively static, and adheres to the 16MB document size limit. However, they might lead to data redundancy and scalability challenges as your database grows.

On the other hand, references are more suitable for large or frequently changing data. They excel in many-to-many relationships by offering the flexibility to link documents across different collections, preventing data duplication and maintaining database normalization. The trade-off, however, is the need for additional queries to get related data, which could affect your performance.

**Inner Objects**

Neurelo supports the management of inner objects at the schema level. To learn how to create inner objects for your schema, please refer to the Neurelo Schema Language reference.

Now, assume that will have a sample schema with a User object and an Address inner object. A user **has one** address. For example,

```json
{
  "objects": {
    "user": {
      "properties": {
        "id": {
          "type": "string",
          "sourceName": "_id",
          "sourceType": "ObjectId",
          "default": {
            "function": "auto"
          },
          "identifier": true
        },
        "name": {
          "type": "string"
        },
        "address": {
          "$ref": "#/innerObjects/Address",
          "nullable": true
        }
      }
    }
  },
  "innerObjects": {
    "Address": {
      "properties": {
        "id": {
          "type": "string",
          "sourceType": "ObjectId"
        },
        "street": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "zipcode": {
          "type": "string"
        }
      }
    }
  }
}
```

**Create**

To create a single user with its address, assuming that we have the sample schema running in an environment, we can issue a `CreateOne` mutation, with the data argument set to the `userCreateInput` input type. For example:

```graphql
mutation {
  createOneuser(data: { name: "Admin User", address: { set: { id: "5ca4bbc7a2dd94ee5816238d", street: "Diagon Alley", zipcode: "27000", city: "Nowhere Land" } } }) {
    id
    name
    address {
      id
      street
      city
      zipcode
    }
  }
}
```

Note that, at present, our APIs **do not allow** the user to auto generate inner object ids, hence we had to manually specify it in the above examples.

The response from this insert request will look like this, which is the object that was inserted:

```json
{
  "data": {
    "createOneuser": {
      "id": "65eb623e591811da1638040a",
      "name": "Admin User",
      "address": {
        "id": "5ca4bbc7a2dd94ee5816238d",
        "street": "Diagon Alley",
        "city": "Nowhere Land",
        "zipcode": "27000"
      }
    }
  }
}
```

To create multiple users with their address, we can issue a `CreateMany` mutation, with the data argument set to an array of `userCreateInput` types.

```graphql
mutation {
  createManyuser(data: [{ name: "Admin User", address: { set: { id: "5ca4bbc7a2dd94ee5816238d", street: "Diagon Alley", zipcode: "27000", city: "Nowhere Land" } } }, { name: "Admin User 2", address: { set: { id: "5ca4bbc7a2dd94ee5816238e", street: "King's Court", zipcode: "27100", city: "Nashville" } } }]) {
    count
  }
}
```

The response (`AffectedRowsOutput`) from this insert request will look like this, which is the count of the inserted rows:

```json
{
  "data": {
    "createManyuser": {
      "count": 2
    }
  }
}
```

**Retrieve**

To retrieve a unique user with its address, assuming that we have the sample schema running in an environment, we can issue a query to `findUniqueUser` with a `where` argument that filters on unique properties. For example:

```graphql
query {
  findUniqueuser(where: { { id: "65eb623e591811da1638040a" } }) {
    id
    name
    address {
      id
      street
      city
      zipcode
    }
  }
}
```

The response from this request will look like this, which is the retrieved object:

```json
{
  "data": {
    "findUniqueuser": {
      "id": "65eb623e591811da1638040a",
      "name": "Admin User",
      "address": {
        "id": "5ca4bbc7a2dd94ee5816238d",
        "street": "Diagon Alley",
        "city": "Nowhere Land",
        "zipcode": "27000"
      }
    }
  }
}
```

Similarly, to retrieve all users with their address, assuming that we have the sample schema running in an environment, we can issue a query with `findManyuser`. For example:

```graphql
query {
  findManyuser {
    id
    name
    address {
      id
      city
      street
      zipcode
    }
  }
}
```

The response from this request will look like this, which is an array of the retrieved objects (with their inner objects):

```json
{
  "data": {
    "findManyuser": [
      {
        "id": "65eb623e591811da1638040a",
        "name": "Admin User",
        "address": {
          "id": "5ca4bbc7a2dd94ee5816238d",
          "street": "Diagon Alley",
          "city": "Nowhere Land",
          "zipcode": "27000"
        }
      }
    ]
  }
}
```

**Update**

To update multiple users with their address, we can utilize the `updateManyObject` mutation with the `data` and `where` arguments to provide both our updated properties and a condition set to limit which objects are updated.

```graphql
mutation {
  updateManyuser(data: { address: { set: { id: "5ca4bbc7a2dd94ee5816238d", street: "High Street", city: "Nowhere Land", zipcode: "27000" } } }, where: { address: { is: { id: { equals: "5ca4bbc7a2dd94ee5816238d" } } } }) {
    count
  }
}
```

The response (`AffectedRowsOutput`) from this update request will look like this, which is the count of the updated rows:

```json
{
  "data": {
    "updateManyuser": {
      "count": 1
    }
  }
}
```

**Delete**

To delete specific users with their address, you can issue a `deleteOneuser` mutation with arguments present. For example:

```graphql
mutation {
  deleteOneuser(where: { id: "65eb623e591811da1638040a" }) {
    id
    name
    address {
      id
      street
      city
      zipcode
    }
  }
}
```

The response from this request will look like this, which is the deleted object:

```json
{
  "data": {
    "deleteOneuser": {
      "id": "65eb623e591811da1638040a",
      "name": "Admin User",
      "address": {
        "id": "5ca4bbc7a2dd94ee5816238d",
        "street": "Diagon Alley",
        "city": "Nowhere Land",
        "zipcode": "27000"
      }
    }
  }
}
```

Note that this will **delete both the object and its inner object.** To remove the contents of just the inner object, you can use the UPDATE request with an `unset`:

```graphql
mutation {
  updateManyuser(data: { address: { unset: true } }, where: { id: { equals: "65eb623e591811da1638040a" } }) {
    count
  }
}
```

This approach requires setting the inner object of your schema, specifically the unique ID, to have `nullable` set to `true`.

## API Errors

Errors for the API calls will be presented in a list format so that multiple issues can be communicated at once. Each error object will contain an `errors` property that contains the error message and the location of the error.

Two types of errors can be returned as a response:

* Validation errors - request is invalid or contains invalid data
* Execution errors - request execution failed

For example, a sample schema and a sample request like:

```
{
  "objects": {
    "actor": {
      "properties": {
        "actor_id": {
          "type": "integer",
          "identifier": true
        },
        "first_name": {
          "type": "string"
        },
        "last_name": {
          "type": "string"
        },
        "movie_count": {
          "type": "integer"
        }
      }
    }
  }
}
```

```
query {
  findManyActor(where: { movie_count: { gt: "3" } }) {
    id
    firstname
  }
}
```

Will generate the following response:

```
{
  "data": null,
  "errors": [
    {
      "message": "Invalid value for argument \"where.movie_count.gt\", expected type \"Int\"",
      "locations": [
        {
          "line": 2,
          "column": 18
        }
      ]
    },
    {
      "message": "Unknown field \"firstname\" on type \"Actor\". Did you mean \"first_name\"?",
      "locations": [
        {
          "line": 4,
          "column": 9
        }
      ]
    }
  ]
}
```

The above example contains two errors, each with an error message and the specific location of the invalid attribute in the details section. The location parameter contains a JSON path to the attribute that failed the validation.

<br>
