- Overview
- Architecture and Functionality
- Architecture of BaseResolver
- Functionality of BaseResolver
- BaseResolver API
- Query Operations
- Get One
- List
- Filters
- Pagination and Order
- Mutation Operations
- Create
- CreateMany
- Update
- UpdateMany
- Delete
- DeleteMany
- CreateOrUpdateMany
- Customizing BaseResolver
- Conclusion
Overview
BaseResolver is a class within GraphWeaver that facilitates the automatic generation of GraphQL schemas and types at runtime.
With BaseResolver, developers can easily and quickly create GraphQL APIs for their data models without the need to manually define schemas and types for each model.
The generated schema includes queries for fetching single entities or lists of entities, along with support for filtering, ordering, and pagination.
Additionally, BaseResolver provides mutations for create, update, and delete operations, as well as bulk operations such as CreateMany and UpdateMany.
BaseResolver is designed to be compatible with various data providers. Such as, PostgreSQL, MySQL, REST, Microsoft Dynamics 365 and Xero APIs.
Let’s start by looking at the architecture of the system.
Architecture and Functionality
Before diving into the details, let's provide an overview of the architecture and functionality of BaseResolver.
Architecture of BaseResolver
BaseResolver follows a modular architecture, comprising several components:
- a GraphQL server
- a schema generator (BaseResolver itself)
- data providers such as PostgreSQL and Microsoft Dynamics 365
The GraphQL server handles requests and responses, while the schema generator generates the GraphQL schema based on the data model.
The data providers interact with the respective data sources and supply data to the schema generator.
Functionality of BaseResolver
The primary functionality of BaseResolver is to automatically generate schemas for GraphQL APIs.
The generated schema includes queries for fetching single entities or lists of entities, along with filtering, ordering, and pagination capabilities.
BaseResolver also provides mutations for create, update, and delete operations, as well as bulk operations like CreateMany and UpdateMany.
While BaseResolver is designed to work with the built in Data Providers, it can be extended to interface with other data providers if needed.
When a request is made to the GraphQL server, it passes the request to the appropriate resolver function within BaseResolver.
The resolver function interacts with the data provider to retrieve the required data, which is then formatted according to the schema and returned to the client.
BaseResolver supports custom queries and mutations, allowing developers to add additional functionality to their GraphQL APIs beyond the standard operations like create, update, and delete.
Overall, the architecture and functionality of BaseResolver make it a powerful tool for easily creating GraphQL APIs that connect to various data sources.
BaseResolver API
BaseResolver automatically generates a set of Queries and Mutations for each entity. Let's explore them in more detail.
Query Operations
Query operations are used to retrieve data from the server and include Get One and List queries.
Get One
The Get One queries allow fetching a single entity using its ID. For example:
query {
user(id: "1") {
id
firstName
email
}
}
In this example, the user
query retrieves a single driver entity with the ID "1". The user
query is automatically generated by BaseResolver, eliminating the need for manual definition.
List
The List queries are used to fetch a list of entities based on specific criteria. For instance, to list all users with the state "NSW", the following request can be made:
query {
users(filter: { state: "NSW" }) {
id
firstName
state
}
}
Filters
List queries support automatic generation of filters based on entity properties.
Each entity has a corresponding filter input type with the entity name followed by the suffix "ListFilter". For example, if the entity is named "user", the corresponding filter input type would be "UserListFilter".
The filter input type includes fields for each filterable property of the entity.
Operators such as "gt" (greater than), "gte" (greater than or equal to), "lt" (less than), "lte" (less than or equal to), "ne" (not equal to), "in" (in a specified list), "nin" (not in a specified list), "notnull" (not null), "null" (null), "like" (pattern matching), and "ilike" (case-insensitive pattern matching) are automatically added as fields to the ListFilter.
The name of each field is formed by combining the operator name with the property name, separated by an underscore. For example, the field for the "in" operator applied to the "id" property would be named "id_in".
Here's an example of a GraphQL query using the "UserListFilter" input type to list all users with IDs in the array ["1", "99", "1023"]:
query {
users(filter: { id_in: ["1", "99", "1023"] }) {
id
firstName
}
}
In this query, "id_in" is the field name on the "UserListFilter" input type corresponding to the "in" operator applied to the "id" property of the "User" entity type.
Next, let's explore pagination and ordering.
Pagination and Order
BaseResolver provides pagination capabilities by automatically generating a PaginationInput
type that can be used as an input in GraphQL queries.
The PaginationInput
includes fields for specifying the limit, offset, and orderBy.
- The limit field defines the maximum number of items to return.
- The offset field determines the number of items to skip before returning results.
- The orderBy field specifies the sorting order for the results. BaseResolver also generates an
OrderByInput
type that allows specifying the field to order by and the sort order.
Pagination is applied to the list query method in the base resolver. The client can provide the filter and pagination arguments to limit and sort the returned results.
For example, let's retrieve the first 3 users, sorted by their ID in descending order, using pagination:
query {
users(pagination: { limit: 3, offset: 0 }, orderBy: { id: DESC }) {
id
firstName
age
}
}
In this query, we set the limit to 3, the offset to 0 (no skipping), and specify the orderBy field as "id" in descending order.
The response will contain the first 3 users, sorted by ID, along with their respective ID, firstName, and age fields.
The response might look like this:
{
"data": {
"users": [
{
"id": "10",
"firstName": "John",
"age": 25
},
{
"id": "9",
"firstName": "Jane",
"age": 30
},
{
"id": "8",
"firstName": "Bob",
"age": 28
}
]
}
}
Mutation Operations
Mutation operations are used to modify data in the data provider.
BaseResolver generates several mutations, including Create, Update, and Delete operations, along with bulk operations like CreateMany, UpdateMany, DeleteMany, and CreateOrUpdateMany.
Let's explore some examples and learn how to use them.
Create
The create mutation is used to create a single entity.
Here's an example of using the BaseResolver create mutation to create a user:
mutation {
createUser(data: { firstName: "John", lastName: "Doe" }) {
id
}
}
In this mutation, we use the createUser
operation to create a new user.
We pass the user’s firstName and lastName as an object in the data argument. The response will contain the ID of the created user.
CreateMany
The CreateMany mutation allows you to create multiple entities at once by passing an array of objects. Here's an example:
mutation {
createUsers(input: {
data: [
{ firstName: "Alice", lastName: "Smith" },
{ firstName: "Bob", lastName: "Johnson" },
{ firstName: "Charlie", lastName: "Garcia" }
]
}) {
id
}
}
In this example, we're using the CreateMany mutation to create multiple users. We provide an array of objects, where each object represents the data for a single user.
Update
The update mutation is used to modify an existing entity.
To update a user's last name from "Smith" to "Jones," we can use the updateUser mutation:
mutation {
updateUser(data: { id: 1, lastName: "Jones" }) {
id
firstName
lastName
}
}
In this mutation, we specify the user’s ID and the updated lastName value. The query returns the updated user's ID, firstName, and lastName.
UpdateMany
Similar to CreateMany, the UpdateMany mutation allows you to update multiple entities at once.
Here's an example:
mutation {
updateUsers(input: {
data: [
{ id: "1", firstName: "Alice", lastName: "Smith" },
{ id: "2", firstName: "Bob", lastName: "Johnson" },
{ id: "3", firstName: "Charlie", lastName: "Garcia" }
]
}) {
id
}
}
In this example, we use the UpdateMany mutation to update multiple users. We provide an array of objects, where each object represents the updated data for a user.
Delete
The delete mutation is used to remove an entity from the data provider.
For example, to delete the user with ID 1, we can use the following mutation:
mutation {
deleteUser(id: "1")
}
The response for this request will be a boolean value indicating the success or failure of the deletion.
DeleteMany
The DeleteMany mutation allows you to delete multiple entities at once.
Here's an example:
mutation {
deleteManyUsers(ids: ["1", "2"])
}
In this example, we use the DeleteMany mutation to delete multiple users. We pass an array of user IDs to be deleted.
CreateOrUpdateMany
The CreateOrUpdateMany mutation enables creating or updating multiple entities in a single GraphQL mutation.
It accepts an array of objects, where each object represents the data for a single entity.
Here's an example of using the CreateOrUpdateMany mutation for the User entity:
mutation {
createOrUpdateManyUsers(input: {
data: [
{ id: "1", firstName: "Alice", lastName: "Smith" },
{ firstName: "Charlie", lastName: "Garcia" }
]
}) {
id
}
}
In this example, the mutation will update the user with ID 1 and set their firstName and lastName, and it will also create a new user record for Charlie Garcia with the provided data.
The response will include the ID of each created or updated users.
Customizing BaseResolver
You can also add your own custom Queries and Mutations to BaseResolver Classes. To find out more see Customizing BaseResolver.
Conclusion
BaseResolver is a powerful tool for generating GraphQL schemas and types at runtime.
It simplifies the process of creating GraphQL APIs by automatically generating queries, mutations, and other essential components based on your data models.
With support for many data providers, BaseResolver provides flexibility and ease of integration with various data sources.
By extending the functionality with custom queries and mutations, you can tailor your GraphQL API to suit your specific application needs.
Whether you need to fetch data, create entities, update records, or perform bulk operations, BaseResolver offers a comprehensive set of features to streamline your GraphQL development process.