Skip to main content
Version: 12.x (Current)

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:

  1. The CRUD Client updates its internal query to reflect the requested changes
  2. The internal query is then translated into appropriate parameters for fetching data from the CRUD Service
  3. A GET request is made to the /count route to retrieve the count of data matching the specified parameters
  4. The result of the count call is communicated to other components through the emission of a count-data event
  5. Next, another GET request is executed, this time targeting the / route. This request retrieves the data that corresponds to the specified parameters
  6. 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 an update-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 the CRUD 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:

    EventStarting __STATE__enableDefinitiveDeleteOutcome
    delete-dataPUBLIC / DRAFTfalse__STATE__ is updated to TRASH
    delete-dataTRASHfalse__STATE__ is updated to DELETED
    delete-dataPUBLIC / DRAFTtrue__STATE__ is updated to TRASH
    delete-dataTRASHtrueelement is permanently deleted
    http-deletePUBLIC / DRAFT / TRASH-element is permanently deleted

    Both delete-data and http-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.

info

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.

caution

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
}

info

_p parameter can be omitted by setting shouldIncludeProjections property to false.

info

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 the update-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 with id "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 with id "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 with id "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
  • 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

propertyattributetypedefaultdescription
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
appendTrailingSlashappend-trailing-slashbooleantrueshould append a trailingSlash to URLs
bootstrapTimeoutbootstrap-timeoutnumber1000value in ms before default bootstrap starts and no change-query was received
dataSchema-ExtendedJSONSchema7Definition-data-schema describing which field to retrieve from CRUD collection
enableDefinitiveDeleteenable-definitive-deletebooleanfalsewhen 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
keepStateWhileDuplicatingkeep-state-while-duplicatingbooleanfalseif true duplicate will keep the original record __STATE__
shouldIncludeProjectionsshould-include-projectionsbooleantrueshould append projection when exporting from CRUD service
keepPageCountkeep-page-countbooleanfalseshould attempt to stay on current page after successful CRUD operation
reflectToUrlreflect-to-urlbooleantrueon internal state update, should reflect internal state on URL
useEstimateCount-booleanfalseCalls GET /count with the query parameter _useEstimate set at true
baseSortPropertybase-sort-propertystring-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

eventactionemitson error
change-querysends GETs with endpoints / and /count to the CRUD service base pathsuccess,count-data,display-data,selected-data-bulkerror
create-datasends a POST to the CRUD Service base pathsuccesserror
update-datasends a PATCH to the CRUD Service base pathsuccesserror
delete-datasends a PATCH to the CRUD Service base path on state endpoint, if enableDefinitiveDelete is true it sends a DELETEsuccesserror
http-deletesends a DELETE to the CRUD Service base pathsuccesserror
bulk-updatesends a PATCH to the CRUD Service /bulk pathsuccesserror
duplicate-datasends a POST to the CRUD Service base path, CRUD Service default fields are removed from payloadsuccesserror
update-state-bulksends a POST state to the CRUD Service /state pathsuccesserror
import-user-configsends a PATCH with multipart body to the CRUD Service /import pathsuccesserror

Emits

eventdescription
loading-dataraise awareness of incoming data
display-datacontains data organized according with dataSchema property
count-datasends 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
errorcontains HTTP error messages when something goes wrong
successnotifies a successful HTTP request