DEV Community

Cover image for Open-Source No-Code/Low-Code Platform NocoBase v0.20: Supporting Multiple Data Sources
NocoBase
NocoBase

Posted on

Open-Source No-Code/Low-Code Platform NocoBase v0.20: Supporting Multiple Data Sources

About NocoBase

NocoBase is a private, open-source, no-code platform offering total control and infinite scalability. It empowers teams to adapt quickly to changes while significantly reducing costs. Avoid years of development and substantial investment by deploying NocoBase in minutes.

👇 Get NocoBase
Homepage

Demo

Documentation

GitHub

New Features

Support for Multiple Data Sources

Added the "Data Source Manager" plugin, used to manage all collections and fields for data sources. The Data Source Manager plugin provides a centralized interface for managing data sources and does not provide the capability to access data sources. It needs to be used in conjunction with various data source plugins. Currently supported data sources include:

In addition, more data sources can be extended, which can be common types of databases or platforms that provide APIs (SDKs).

Data Source Manager

Adjustment to Collections Management

Moved the original "Collection Manager" to "Data Source > Main Database > Configuration".

Main Database Configuration

Support for Non-ID Fields as Primary and Relationship Constraints

When creating a collection, you can choose not to create an ID field.

Predefined Fields

Integer fields can be used as primary keys.

Integer fields can be used as primary keys

Single-line text fields can also be used as primary keys.

Single-line text fields can also be used as primary keys

Relationship constraints support selecting other fields with Unique indexes set as non-primary key fields.

non-primary key fields

Adjustment to Drag-and-Drop Sorting

Added a "Sort" type field. Sorting fields are no longer automatically generated when creating collections and need to be manually created.

Added a "Sort" type field

When selecting a field as a group, grouping will be done before sorting.

grouping will be done before sorting

When enabling drag-and-drop sorting in table block, you need to select the sorting field.

select the sorting field

When creating a Kanban block, you need to select the sorting field.

need to select the sorting field

Adjustment to User and Permission Interfaces

Added user management interface and unified user and role management under one menu.

Added user management interface

Adjusted the role management interface to facilitate the management of user-associated roles, permissions, departments, etc.

Adjusted the role management interface

Moved the original "Action Permissions" to the "Data Source" tab.

Moved the original "Action Permissions"

Department Plugin

Department Plugin

Organize users by departments, set hierarchical relationships, link roles to control permissions, and use departments as variables in workflows and expressions.

Workflow: Approval

The approval plugin provides dedicated workflow types (triggers) "Initiate Approval" and "Approval" nodes for this process. Combined with NocoBase's unique custom data tables and custom blocks, various approval scenarios can be quickly and flexibly created and managed.

Approval configuration

Approval Configuration

Approval process

Approval Process

Further details can be found in the documentation: Workflow Approval

Workflow: End Process Node

This node immediately ends the current execution of the workflow when executed and ends with the status configured in the node. It is typically used for specific logic flow control, to exit the current workflow after meeting certain logical conditions, without continuing with subsequent processing. It can be likened to the return command in programming languages, used to exit the currently executing function.

used to exit the currently executing function

Further details can be found in the documentation: End Process Node

Workflow: Custom Variable Node

Variables can be declared in the workflow or assigned values to previously declared variables, typically used to store temporary data in the workflow. It is suitable for scenarios where calculation results need to be stored for later use outside the branch (such as loops, parallelism, etc.).

workflow

Further details can be found in the documentation: Custom Variable Node

Workflow: Request Interceptor

The request interceptor plugin provides a mechanism to intercept operations on forms, where the interception event is triggered after the corresponding form operation is submitted and before it is processed. If a "End Process" node is executed in the subsequent process flow after triggering, or if other nodes fail to execute (errors or other incomplete executions), the form operation will be intercepted, otherwise the scheduled operation will be executed normally. It can be used for business validation or logic checks to approve or intercept client-submitted create, update, and delete operations.

request interceptor plugin

Further details can be found in the documentation: Request Interceptor

Workflow: Response Message Node

The response message node is used to provide feedback to the client with custom messages in specific types of workflows (such as request interception and form events).

Node configuration

Node configuration

Prompt message

Prompt message

Further details can be found in the documentation: Response Message Node

Incompatible Changes

Conflictingly Named APIs

In this kernel change, some new version APIs conflict with the old version names. These conflicting old version APIs will be retained in this version but will be uniformly suffixed with _deprecated.

Original API Deprecated API New API
CollectionProvider CollectionProvider_deprecated CollectionProvider
useCollection useCollection_deprecated useCollection
useCollectionField useCollectionField_deprecated useCollectionField
useCollectionManager useCollectionManager_deprecated useCollectionManager
useContext(CollectionManagerContext) useCollectionManager_deprecated useCollectionManager

If you are using the above related APIs, you have two ways to change:

  • Simple replacement: Replace the original API with the one suffixed with _deprecated, for example, replace useCollection() with useRecord_deprecated().
  • Use the new API according to the new documentation: Although the names of the new APIs are the same as the old APIs, there are differences in parameters and return values. You need to refer to the new documentation

to adjust the corresponding code.

Other APIs Requiring Adjustment

  • registerTemplate() changed to app.dataSourceManager.addCollectionTemplates()
  • registerField() changed to app.dataSourceManager.addFieldInterfaces()
  • registerGroup() changed to app.dataSourceManager.addFieldInterfaceGroups()
  • useContext(CollectionManagerContext) changed to useCollectionManager_deprecated()
  • Extend collections using ExtendCollectionsProvider
  • RecordProvider requires explicit passing of the parent parameter when needed

Change Examples

Collection Template Extension

Definition

Previously defined as an object, it now needs to be changed to a class. For example:

Before:

import { ICollectionTemplate } from '@nocobase/client';

const calendar: ICollectionTemplate = {
  name: 'calendar',
  title: 'Calendar collection',
  order: 2,
  color: 'orange',
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Now:

import { CollectionTemplate } from '@nocobase/client';

class CalendarCollectionTemplate extends CollectionTemplate {
  name = 'calendar';
  title = 'Calendar collection';
  order = 2;
  color = 'orange';
}
Enter fullscreen mode Exit fullscreen mode

The original object properties become class members.

Registration

Previously registered through registerTemplate, now needs to be registered through the plugin's dataSourceManager.addCollectionTemplates. For example:

Before:

import { registerTemplate } from '@nocobase/client';
import { calendar } from './calendar'

registerTemplate('calendar', calendar);
Enter fullscreen mode Exit fullscreen mode

Now:

import { Plugin } from '@nocobase/client';
import { CalendarCollectionTemplate } from './calendar'

export class CalendarPluginClient extends Plugin {
  async load() {
    this.app.dataSourceManager.addCollectionTemplates([CalendarCollectionTemplate]);
  }
}
Enter fullscreen mode Exit fullscreen mode

Field Interface Extension

Definition

Previously defined as an object, it now needs to be changed to a class. For example:

Before:

import { IField } from '@nocobase/client';

const attachment: IField = {
  name: 'attachment',
  type: 'object',
  group: 'media',
  title: 'Attachment',
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Now:

import { CollectionFieldInterface } from '@nocobase/client';

class AttachmentFieldInterface extends CollectionFieldInterface {
  name = 'attachment';
  type = 'object';
  group = 'media';
  title = 'Attachment';
  // ...
}
Enter fullscreen mode Exit fullscreen mode

The original object properties become class members.

Registration

Previously registered through registerField, now needs to be registered through the plugin's dataSourceManager.addFieldInterfaces and does not require passing CollectionManagerProvider again. For example:

Before:

import { registerField } from '@nocobase/client';
import { attachment } from './attachment'

- registerField(attachment.group, 'attachment', attachment);

export const FileManagerProvider: FC = (props) => {
  return (
-   <CollectionManagerProvider interfaces={{ attachment }}>
      <SchemaComponentOptions scope={hooks} components={{ UploadActionInitializer }}>
        {props.children}
      </SchemaComponentOptions>
-   </CollectionManagerProvider>
  );
};
Enter fullscreen mode Exit fullscreen mode

Now:

import { Plugin } from '@nocobase/client';
import { AttachmentFieldInterface } from './attachment'

export class FilPlugin extends Plugin {
  async load() {
    this.app.dataSourceManager.addFieldInterfaces([AttachmentFieldInterface]);
  }
}
Enter fullscreen mode Exit fullscreen mode

Field Interface Group Extension

Previously registered through registerGroup, now needs to be registered through the plugin's dataSourceManager.addFieldInterfaceGroups. For example:

- import { registerGroup, Plugin } from '@nocobase/client';
+ import { Plugin } from '@nocobase/client';

- registerGroup('map', {
-        label: 'Map-based geometry',
-        order: 10
- })

export class MapPlugin extends Plugin {
  async load() {
+    this.app.dataSourceManager.addFieldInterfaceGroups({
+      map: {
+        label: generateNTemplate('Map-based geometry'),
+        order: 51,
+      },
+    });
  }
}
Enter fullscreen mode Exit fullscreen mode

useContext(CollectionManagerContext) Changed to useCollectionManager_deprecated()

- const ctx = useContext(CollectionManagerContext);
+ const ctx = useCollectionManager_deprecated();
Enter fullscreen mode Exit fullscreen mode

Extend Collections, Use ExtendCollectionsProvider Instead of CollectionManagerProvider

const Demo = () => {
-  <CollectionManagerProvider collections={[apiKeysCollection]}>
+  <ExtendCollectionsProvider collections={[apiKeysCollection]}>
...
-  </CollectionManagerProvider>
+  </ExtendCollectionsProvider>
}
Enter fullscreen mode Exit fullscreen mode

Changes to RecordProvider

Previously, when the parent property was not passed, the value of the last RecordProvider was automatically retrieved as the parent. Now, the parent needs to be explicitly passed, and when the parent is not passed, the value of the parent will be undefined.

- <RecordProvider record={recordData}>
+ <RecordProvider record={recordData} parent={parentRecordData}>
...
</RecordProvider>
Enter fullscreen mode Exit fullscreen mode

If there is no historical baggage, you can also directly use CollectionRecordProvider to replace.

- <RecordProvider record={recordData}>
+ <CollectionRecordProvider record={recordData} parent={parentRecordData}>
...
- </RecordProvider>
+ </CollectionRecordProvider>
Enter fullscreen mode Exit fullscreen mode

⚠️Difference Between RecordProvider and CollectionRecordProvider

  • RecordProvider is deprecated and will be removed in the future.
  • RecordProvider carries the old RecordContext, while CollectionRecordProvider does not.

Top comments (0)