CRUD Client
<bk-crud-client></bk-crud-client>
The CRUD Client is a purely logical component (does not render), designed to handle CRUD
operations towards a single collection of Mia-Platform CRUD Service.
How to configure
For its basic usage, the CRUD Client needs dataSchema
and basePath
properties to be provided.
{
"tag": "bk-crud-client",
"properties": {
"basePath": "/orders",
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"__STATE__": {"type": "string"},
"name": {"type": "string"}
}
}
}
}
basePath
is the base path of the URL to which HTTP requests are sent, while dataSchema
provides information on the structure of the data of the associated CRUD Service collection.
Data Fetching
The CRUD Client holds as state a representation of the query that is used to fetch data. This is an object shaped like the payload of a change-query event, in which each field influences the parameters of the HTTP call that the CRUD Client executes to fetch data from the CRUD Service.
{
"characteristic": { // volatile filter, influences data querying
"tab": "Public",
"filters": [
{
"operator": "equal",
"property": "__STATE__",
"value": "PUBLIC"
}
]
},
"pageNumber": 1, // data pagination, determines skipped pages
"pageSize": 25, // data pagination, determines page size
"search": "neckla", // currently searched text, influences data querying
"sortDirection": "descend", // data sorting direction
"sortProperty": "createdAt", // property to use for data sorting
"filters": [ // permanent filters, influences data querying
{
"operator": "greater",
"property": "price",
"value": 100
}
]
}
When a component signals the need to modify the way data is fetched (that is, when a change-query
event is emitted), the CRUD Client handles the process as follows:
- The CRUD Client updates its internal query to reflect the requested changes
- The internal query is then translated into appropriate parameters for fetching data from the
CRUD Service
- A GET request is made to the
/count
route to retrieve the count of data matching the specified parameters - The result of the count call is communicated to other components through the emission of a count-data event
- Next, another GET request is executed, this time targeting the
/
route. This request retrieves the data that corresponds to the specified parameters - The fetched data is propagated to other components by emitting a display-data event
In essence, the process involves updating the query, obtaining a count of matching data, sharing the count result, fetching the data, and then communicating the fetched data to connected components.
On Connection
Upon connecting to the DOM, the CRUD Client awaits for an initial change-query event to be received in order to initialize the its internal representation of the query to perform when fetching data. The amount of milliseconds that are awaited can be configured with bootstrapTimeout
property.
If no change-query
event is received, the CRUD Client emits one itself. The payload of this event can be controlled with the initialEvent
property, and defaults to:
{
"pageSize": 25,
"pageNumber": 1
}
which initializes parameteres for data pagination.
__STATE__ life-cycle
In case __STATE__
must be finely controlled upon creation/duplication/patching, some care must be taken while configuring.
As per CRUD-Service
interface, __STATE__
cannot be patched and requires a reserved endpoint call. Hence, no BO configuration
leads to patch with __STATE__
included as part of the $set
body.
__STATE__
though can be tuned upon creation and duplication. The default behavior is
- on creation if not specified,
__STATE__
defaults to its CRUD collection default, i.e. no param is passed along with the HTTP request - on duplication, to ensure backward compatibility, HTTP post is performed without
__STATE__
param defaulting again according with CRUD collections default
This behavior can be overridden using the property keepStateWhileDuplicating
.
If set to true
it will carry along the __STATE__
of the original item duplicating it as well.
A good configuration could be (in case __STATE__
must be tunable by the user and carry duplication __STATE__
along),
- in the
dataSchema
:
{
"__STATE__": {
"type": "string",
"label": "CRUD State",
"default": "PUBLIC",
"enum": ["PUBLIC", "DRAFT", "TRASH"]
}
}
- in the CRUD Client:
{
"tag": "bk-crud-client",
"properties": {
"basePatch": "/base-path",
"keepStateWhileDuplicating": true,
"dataSchema": {
"$ref": "dataSchema"
}
}
}
Data Create/Update/Delete
The CRUD Client acts as a proxy between rendering components and the CRUD Service, facilitating the communication and interaction between the two. It is designed to handle various types of events that may be received from other components, leading to HTTP requests being made to the CRUD Service. These events include:
Data Creation
When a create-data event is received, the CRUD Client sends a POST request to its base path, aiming to insert a new item that corresponds to the payload of the event.
Data Duplication
The duplicate-data event is handled similarly to the
create-data
event. However, it is specifically designed to work with payload items that are already present in the collection. As a result, the CRUD Client sends a modified version of the payload in the request body, with all default-fields removed.Data Update
In response to an update-data event, the CRUD Client initiates a PATCH request to a path defined by the
_id
field of the item to be updated (/<_id-of-the-item>
). The request body specifies the data modifications using$set
and$unset
keys.Bulk Update
Upon receipt of a bulk-update event, the CRUD Client sends a PATCH request to the
/bulk
route of the CRUD Service. This results in a bulk update operation targeting multiple items.__STATE__ Update
As defined in the CRUD Service interface, the special field
__STATE__
within CRUD Service collections requires specific handling. It cannot be updated directly through a standard PATCH request resulting from anupdate-data
event. Instead, when the CRUD Client receives an update-state-bulk event, it triggers a POST request to the/state
route to update the field using its dedicated interface.Data Deletion
By default, data deletion follows the
__STATE__
pattern of theCRUD Service
.When the CRUD Client receives the delete-data event, it processes the payload to determine the appropriate action for the
__STATE__
field of the elements being deleted. If the current value of__STATE__
is "PUBLIC" or "DRAFT", the__STATE__
field is updated to "TRASH"; if the current__STATE__
is "TRASH" it is updated to "DELETED".This behavior can be partially overridden using property
enableDefinitiveDelete
: if this is set to true, the elements in__STATE__
"TRASH" will be irreversibly deleted from the collection.Additionally, the CRUD Client listens to the http-delete event. This event deletes the elements specified in the payload, regardless of their starting value of
__STATE__
.The following table summarizes the outcomes based on different scenarios:
Event Starting __STATE__
enableDefinitiveDelete
Outcome delete-data
PUBLIC / DRAFT false __STATE__
is updated to TRASHdelete-data
TRASH false __STATE__
is updated to DELETEDdelete-data
PUBLIC / DRAFT true __STATE__
is updated to TRASHdelete-data
TRASH true element is permanently deleted http-delete
PUBLIC / DRAFT / TRASH - element is permanently deleted Both
delete-data
andhttp-delete
also support bulk actions, in case payload is an array.Data Import
Beginning with version 6.9.0 of the CRUD Service, an
/import
route is introduced. This route enables the execution of a PATCH request with a multipart body, incorporating a file that is transformed into items added to the collections.
In each of these scenarios, the outcome of the operation is broadcast to all components through a corresponding result event, either success or error. Additionally, the process triggers a new data fetching sequence, ensuring that all components have access to the most up-to-date data.
For further details on how each route works, refer to the CRUD Service documentation.
Rerouting HTTP requests
Property reroutingRules
allows to reroute HTTP calls based on the their pathname and HTTP method.
reroutingRules
is an shaped like an array of objects with keys from
and to
.
- key
from
allows to specify two strings that are matched against the url pathname and HTTP method of the request - key
to
allows to specify a string, being the URL pathname to which the request is redirected
Each emitted HTTP request is matched against all rules using its from
key. The request is rerouted to the value specified in the to
key of first macthing rule.
Requests that match no rule are not redirected.
If key from
of an entry of reroutingRules
is a string, all HTTP methods will be matched.
The regular expression specified in the from
key of an entry of reroutingRules
may include groups, which can be referenced in the to
key using the character "$" and the index of the group or its name.
{
"reroutingRules": [
{
"from": {
"url": "^/orders/$",
"method": "GET"
},
"to": "/orders-list/"
},
{
"from": {
"url": "^/orders/count$",
"method": "GET"
},
"to": "/orders-count/"
},
{
"from": "^/orders/import$",
"to": "^/orders-import$"
}
]
}
Estimate count
The property useEstimateCount
enables to call every GET /count
request with the query parameter _useEstimate=true
. It returns the total number of elements in the collection but it doesn't take into account any type of filter.
To use this property you must use CRUD Service version 6.10.0
or above.
Examples
Example: Data Fetching
A CRUD Client configured like the following
{
"tag": "bk-crud-client",
"properties": {
"basePath": "/orders",
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"__STATE__": {"type": "string"},
"name": {"type": "string"}
}
}
}
}
having an internal query like
{
"pageNumber": 1, // data pagination, determines skipped pages
"pageSize": 25, // data pagination, determines page size
"filters": [ // permanent filters, influences data querying
{
"operator": "equal",
"property": "name",
"value": "Alex"
}
]
}
fetches data with a GET request with query parameters:
{
"_p": "_id,__STATE__,name",
"_l": 25, // paginates data in batches of 25 as per `pageSize`
"_sk": 0, // skips no page due to `pageNumber` being set to 1
"_q": {"name": {"$eq": "Alex"}} // `filters` is converted to a mongo-like query
}
_p
parameter can be omitted by setting shouldIncludeProjections
property to false.
In order to properly build the "_q" parameter from the "filters" key of its internal query, the CRUD Client needs the dataSchema
property to provide information on how to interpret each field.
If a change-query with payload
{
"pageNumber": 2
}
the internal query is updated and consequently the data is newly fetched with parameters:
{
"_p": "_id,__STATE__,name",
"_l": 25,
"_sk": 1, // <- one page is now skipped
"_q": {"price": {"$eq": "Alex"}}
}
Example: Data Creation / Update
A CRUD Client configured like the following
{
"tag": "bk-crud-client",
"properties": {
"basePath": "/orders",
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"__STATE__": {"type": "string"},
"name": {"type": "string"},
"price": {"type": "number"}
}
}
}
}
upon receiving a create-data event with payload:
{
"__STATE__": "PUBLIC",
"price": 123,
"name": "Necklace"
}emits a POST request to endpoint
/orders/
in which the body replicates the event payload{
"__STATE__": "PUBLIC",
"name": "Necklace",
"price": 123
}upon receiving an update-data event with payload:
{
"_id": "order-id-1",
"__STATE__": "PUBLIC",
"price": null,
"name": "Necklace"
}emits a PATCH request to endpoint
/orders/order-id-1?_st=PUBLIC
with body{
"$set": {
"name": "Necklace"
},
"$unset": {
"price": true
}
}::info Since fields
_id
and__STATE__
are needed to initiate the PATCH request, these should always be included in the payload of theupdate-data
event. :::
Example: Data Duplication
A CRUD Client configured like the following
{
"tag": "bk-crud-client",
"properties": {
"basePath": "/orders",
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"__STATE__": {"type": "string"},
"name": {"type": "string"},
"price": {"type": "number"}
}
}
}
}
upon receiving a duplicate-data event with payload:
{
"_id": "order-id-1",
"__STATE__": "PUBLIC",
"price": 123,
"name": "Necklace"
}
emits a POST request to endpoint /orders/
. In this request, the body includes the payload of the event, but omits the CRUD Service default-fields _id
and __STATE__
:
{
"name": "Necklace",
"price": 123
}
This action creates a new item with the __STATE__
set to "DRAFT".
However, if the CRUD Client has the property keepStateWhileDuplicating
set to true, the __STATE__
field is retained in the request body when performing the POST call.
{
"tag": "bk-crud-client",
"properties": {
"keepStateWhileDuplicating": true,
"basePath": "/orders",
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"__STATE__": {"type": "string"},
"name": {"type": "string"},
"price": {"type": "number"}
}
}
}
}
now, when the same duplicate-data
event:
{
"_id": "order-id-1",
"__STATE__": "PUBLIC",
"price": 123,
"name": "Necklace"
}
the CRUD Client emits a POST request to the endpoint /orders/
with body:
{
"__STATE__": "PUBLIC", // <- __STATE__ field is also duplicated
"name": "Necklace",
"price": 123
}
This results in the creation of an item with the __STATE__
set to "PUBLIC".
Example: Data Deletion
The CRUD Client supports various way of deleting data, depending on the value of property enableDefinitiveDelete
and on the type of event that triggered the data deletion flow.
delete-data
Assuming a delete-data event to be received with payload:
{
"_id": "id-1",
"__STATE__": "PUBLIC"
}then the default behavior of the CRUD Client is to request the
__STATE__
field to be updated to "TRASH" for element withid
"id-1".This consists in a POST call to route
/state
of CRUD Service with payload:{
"filter": { "_id": "id-1" },
"stateTo": "TRASH"
}delete-data (bulk)
Assuming a delete-data event to be received with payload:
[
{
"_id": "id-1",
"__STATE__": "PUBLIC"
},
{
"_id": "id-2",
"__STATE__": "TRASH"
}
]then the default behavior of the CRUD Client is to request the
__STATE__
field to be updated to "TRASH" for element withid
"id-1" and to "DELETED" for element with_id
"id-2".This consists in a POST call to route
/state
of CRUD Service with payload:[
{
"filter": { "_id": "id-1" },
"stateTo": "TRASH"
},
{
"filter": { "_id": "id-2" },
"stateTo": "DELETED"
}
]enableDefinitiveDelete
Assuming a delete-data event to be received with payload:
[
{
"_id": "id-1",
"__STATE__": "PUBLIC"
},
{
"_id": "id-2",
"__STATE__": "TRASH"
}
]and the CRUD Client to have property
enableDefinitiveDelete
set to true:{
"tag": "bk-crud-client",
"properties": {
"basePath": "/base-path",
"enableDefinitiveDelete": true,
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"__STATE__": {"type": "string"}
}
}
}
}then the CRUD Client requests the
__STATE__
field to be updated to "TRASH" for element withid
"id-1" and to request permanent deletion of element with_id
"id-2".This results in two http calls:
- one POST call to
/state
route with payload[
{
"filter": { "_id": "id-1" },
"stateTo": "TRASH"
}
] - one DELETE call to
/id-2
- one POST call to
http-delete
Assuming an http-delete event to be received with payload:
[
{
"_id": "id-1",
"__STATE__": "PUBLIC"
},
{
"_id": "id-2",
"__STATE__": "TRASH"
}
]then the CRUD Client requests permanent deletion of elements with
_id
"id-1" and "id-2".This consists in a DELETE call to
/
with request:{
"_q": {
"_id": {
"$in": ["id-1", "id-2"]
}
},
"_st": "PUBLIC,TRASH"
}
Example: Reroute HTTP Requests
A CRUD Client configured like the following
{
"tag": "bk-crud-client",
"properties": {
"basePath": "/orders",
"dataSchema": {
"type": "object",
"properties": {
"_id": {"type": "string"},
"__STATE__": {"type": "string"},
"name": {"type": "string"}
}
},
"reroutingRules": [
{
"from": {
"url": "^/orders/$",
"method": "GET"
},
"to": "/list/"
},
{
"from": {
"url": "^/orders/([^/]+)$",
"method": "PATCH"
},
"to": "/update/$1"
}
]
}
}
fetches data from the path "/list/" instead of the default "/orders/", and requests the update of an item by calling the path "/update/" instead of the default "/orders/".
The HTTP request triggered by receiving a change-query event would normally be a GET call against the "/" path, which is appended to the basePath
"/orders", resulting in a GET request against "/orders/".
This call matches the first entry of reroutingRules
; therefore, it is rerouted to "/list/".
The HTTP request to perform a PATCH against the CRUD Service would normally be directed to "/orders/id-of-the-order". Such a request is intercepted by the second rule specified in reroutingRules
and redirected to "/update/id-of-the-order".
Notice how the "$" character can be used in the target path to reference groups captured in the regular expression specified inside from.url
.
API
Properties & Attributes
property | attribute | type | default | description |
---|---|---|---|---|
basePath | - | string | - | the URL base path to which to send HTTP requests |
headers | - | {[key: string]: string} | - | headers to add when an HTTP request is sent |
credentials | - | 'include'|'omit'|'same-origin' | - | credentials policy to apply to HTTP requests |
reroutingRules | - | ReroutingRule[] | - | rules to redirect HTTP request to new routes |
appendTrailingSlash | append-trailing-slash | boolean | true | should append a trailingSlash to URLs |
bootstrapTimeout | bootstrap-timeout | number | 1000 | value in ms before default bootstrap starts and no change-query was received |
dataSchema | - | ExtendedJSONSchema7Definition | - | data-schema describing which field to retrieve from CRUD collection |
enableDefinitiveDelete | enable-definitive-delete | boolean | false | when true , http DELETE cannot be rolled back |
initialEvent | - | ChangeQueryPayload | {pageSize: 25, pageNumber: 1} | in case of no change-query received within botstrapTimout milliseconds from connection to DOM, an initial change-query event with this payload will be thrown |
keepStateWhileDuplicating | keep-state-while-duplicating | boolean | false | if true duplicate will keep the original record __STATE__ |
shouldIncludeProjections | should-include-projections | boolean | true | should append projection when exporting from CRUD service |
keepPageCount | keep-page-count | boolean | false | should attempt to stay on current page after successful CRUD operation |
reflectToUrl | reflect-to-url | boolean | true | on internal state update, should reflect internal state on URL |
useEstimateCount | - | boolean | false | Calls GET /count with the query parameter _useEstimate set at true |
baseSortProperty | base-sort-property | string | - | field to use as additional sorting criteria when fetching data. It is appended to _s search parameter |
ReroutingRule
type ReroutingRule = {
from: string | { url: string, method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE' }
to: string
}
Listens to
event | action | emits | on error |
---|---|---|---|
change-query | sends GETs with endpoints / and /count to the CRUD service base path | success,count-data,display-data,selected-data-bulk | error |
create-data | sends a POST to the CRUD Service base path | success | error |
update-data | sends a PATCH to the CRUD Service base path | success | error |
delete-data | sends a PATCH to the CRUD Service base path on state endpoint, if enableDefinitiveDelete is true it sends a DELETE | success | error |
http-delete | sends a DELETE to the CRUD Service base path | success | error |
bulk-update | sends a PATCH to the CRUD Service /bulk path | success | error |
duplicate-data | sends a POST to the CRUD Service base path, CRUD Service default fields are removed from payload | success | error |
update-state-bulk | sends a POST state to the CRUD Service /state path | success | error |
import-user-config | sends a PATCH with multipart body to the CRUD Service /import path | success | error |
Emits
event | description |
---|---|
loading-data | raise awareness of incoming data |
display-data | contains data organized according with dataSchema property |
count-data | sends a PATCH to the CRUD service base path on state endpoint, if enableDefinitiveDelete is true it sends a DELETE , response contains the total amount of document retrieved and the collection pagination offset |
error | contains HTTP error messages when something goes wrong |
success | notifies a successful HTTP request |