@narrative.io/jsonforms-provider-protocols
v1.2.0
Published
Dynamic data provider capabilities for JSONForms with Vue 3 integration
Readme
JSONForms Provider Protocols
A Vue 3 library that adds dynamic data provider capabilities to JSONForms, enabling form fields to fetch and display data from various sources like REST APIs, databases, and custom protocols.
✨ Features
- 🔌 Protocol-based Architecture: Extensible system for different data sources
- 🚀 Vue 3 Integration: Seamless integration with JSONForms Vue renderers
- 💾 Built-in Caching: TTL-based caching with configurable expiration
- 🔄 Dynamic Data Loading: Support for mount, focus, and query-based loading
- 🎯 Template Support: Dynamic URL and parameter templating
- 📦 TypeScript Support: Full TypeScript definitions included
- 🔐 Authentication: Flexible authentication mechanism support
📦 Installation
# Using bun (recommended)
bun add @narrative.io/jsonforms-provider-protocols
# Using npm
npm install @narrative.io/jsonforms-provider-protocols
# Peer dependencies
bun add @jsonforms/vue @jsonforms/core vue🚀 Quick Start
1. Register the Plugin
import { createApp } from 'vue'
import ProviderProtocols, { RestApiProtocol } from '@narrative.io/jsonforms-provider-protocols'
const app = createApp(App)
app.use(ProviderProtocols, {
protocols: [RestApiProtocol()]
})2. Configure Your Form
{
"type": "Control",
"scope": "#/properties/country",
"options": {
"provider": {
"ref": "countries",
"protocol": "rest_api",
"config": {
"url": "https://api.example.com/countries",
"items": "$.data[*]",
"map": {
"label": "$.name",
"value": "$.code"
}
}
}
}
}3. Use in Vue
<script setup lang="ts">
import { JsonForms } from '@jsonforms/vue'
import { providerRenderers } from '@narrative.io/jsonforms-provider-protocols/vue'
import { markRaw, ref } from 'vue'
const data = ref({ country: null })
const handleChange = (event) => data.value = event.data
</script>
<template>
<JsonForms
:data="data"
:schema="schema"
:uischema="uischema"
:renderers="markRaw(providerRenderers)"
@change="handleChange"
/>
</template>📖 Documentation
Getting Started
Core Concepts
- Protocols - How to fetch and transform data
- Authentication - Configure and use authentication
- Vue Components - Available components and composables
- API Reference - Complete API documentation
Examples
Help & Troubleshooting
🔧 Key Concepts
Protocols
Define how data is fetched and transformed:
const CustomProtocol: Protocol = {
protocol: 'my-api',
async resolve(config, context) {
const response = await fetch(config.url)
const data = await response.json()
return {
items: data.map(item => ({
label: item.name,
value: item.id
})),
ttl: 300
}
}
}Data Transforms
Transform API response data before mapping to form items using a pipeline of transforms:
{
"provider": {
"protocol": "rest_api",
"config": {
"url": "https://api.example.com/data",
"items": "$.data[*]",
"transforms": [
{
"name": "flatten",
"key": "children",
"labelFormat": "{parent.name} → {name}"
},
{
"name": "filter",
"key": "active",
"values": [true]
}
],
"map": {
"label": "$.name",
"value": "$.id"
}
}
}
}Built-in Transforms
Flatten Transform Recursively flattens nested tree structures into a single-level array:
{
"name": "flatten",
"key": "children",
"labelFormat": "{parent.name} → {name}"
}key: The property containing nested children arrayslabelFormat(optional): Template for formatting labels using parent and child properties- Adds
_depth,_parent, and_formattedLabelmetadata to items
Filter Transform Filters items based on property values:
{
"name": "filter",
"key": "category",
"values": ["A", "B"]
}key: The property to checkvalues(optional): Array of values to match. If omitted, filters by key existence
Combining Transforms Transforms are applied sequentially in pipeline order:
{
"transforms": [
{ "name": "flatten", "key": "children" },
{ "name": "filter", "key": "type", "values": ["product"] }
]
}Custom Transforms Register custom transforms for your specific needs:
import { registerTransform } from '@narrative.io/jsonforms-provider-protocols'
registerTransform('uppercase', (items, config) => {
return items.map(item => ({
...item,
name: item.name.toUpperCase()
}))
})Template Variables
Create dynamic URLs using form data:
{
"url": "https://api.example.com/countries/{{data.country}}/states",
"query": { "search": "{{ui.query}}" }
}Load Strategies
Control when data is fetched:
mount- Load when component mounts (default)onFocus- Load when field receives focusquery- Load when user types (autocomplete)
Derive Functionality
Auto-populate fields from form data or external sources:
{
"type": "Control",
"scope": "#/properties/derived_field",
"options": {
"derive": "country",
"mode": "follow",
"readonly": true
}
}External Data Support
Access external data sources separate from form data using the externalData() syntax:
<script setup>
// Provide external data to the form
const externalData = ref({
user: { preferences: { theme: "dark" } },
tree: { name: "Oak", type: "Deciduous" }
})
provide('externalData', externalData)
</script>{
"type": "Control",
"scope": "#/properties/tree_preference",
"options": {
"derive": "externalData(tree.name)",
"mode": "follow",
"readonly": true
}
}Error Handling
Control error display behavior with the showError property:
{
"provider": {
"protocol": "rest_api",
"config": {
"url": "https://api.example.com/data",
"showError": false,
"items": "$.data[*]",
"map": { "label": "$.name", "value": "$.id" }
}
}
}When showError is false, failed requests return empty results instead of throwing errors. Defaults to true.
Composables
Use providers directly in your components:
<script setup>
import { useProvider } from '@narrative.io/jsonforms-provider-protocols/vue'
const { items, loading, error, reload } = useProvider(binding, context)
</script>🛠 Development
# Install dependencies
bun install
# Run tests
bun test
# Build library
bun run build
# Type checking
bunx vue-tsc --noEmit🤝 Contributing
Contributions are welcome! Please read our Contributing Guide and check out the issue tracker.
📄 License
MIT - see LICENSE file for details.
