npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@getdashfy/ext-json

v0.2.2

Published

JSON extension for Dashfy - Display data from any JSON/REST API with flexible rendering options

Readme

@getdashfy/ext-json

Full README Row

Deploy on Railway

JSON extension for Dashfy - Display data from any JSON/REST API with flexible rendering options.

This extension provides widgets to fetch, transform, and visualize JSON data from REST APIs with support for custom templates, status monitoring, and key-value displays.

Features

  • 🌐 Universal API support: Connect to any REST API that returns JSON
  • 🎨 Flexible rendering: Templates, React components, or raw JSON display
  • 🔑 Key-value display: Extract and show specific fields from JSON responses
  • 📊 Status monitoring: Evaluate assertions and display status indicators
  • 🔄 Data transformation: Transform API responses before rendering
  • 🛣️ JSONPath support: Extract nested data using simplified JSONPath expressions
  • 🔐 Authentication: Support for custom headers (Bearer tokens, API keys, etc.)
  • ⚡ Real-time updates: Automatic data refresh via WebSocket subscriptions
  • 🎨 Theme support: Works with all Dashfy themes (light/dark mode)

Installation

Install with your favorite package manager:

npm

npm install @getdashfy/ext-json

pnpm

pnpm add @getdashfy/ext-json

yarn

yarn add @getdashfy/ext-json

bun

bun add @getdashfy/ext-json

Quick start

1. Server setup

Register the JSON API client in your Dashfy server (dashfy.server.ts):

import { Dashfy } from '@getdashfy/server'
import { createJsonClient } from '@getdashfy/ext-json/client'

// Create a new Dashfy server instance
const dashfy = new Dashfy()

// Load dashboard configuration
await dashfy.configureFromFile('./dashfy.config.yml')

// Register JSON API (all options are optional)
dashfy.registerApi(
  'json',
  createJsonClient({
    baseUrl: 'https://api.example.com',
    headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
    timeout: 5_000,
  }),
)

// Start server
await dashfy.start()

2. Client setup

Register JSON widgets in your React application (App.tsx):

import { WidgetRegistry } from '@getdashfy/ui'
import { CustomJson, JsonKeys, JsonStatus } from '@getdashfy/ext-json'

// Register JSON extension
WidgetRegistry.addExtension('json', {
  CustomJson,
  JsonKeys,
  JsonStatus,
})

3. Dashboard configuration

Add JSON widgets to your dashboard configuration (dashfy.config.yml):

dashboards:
  - title: API Dashboard
    columns: 3
    rows: 2
    widgets:
      - extension: json
        widget: JsonKeys
        title: User Profile
        url: https://api.example.com/user
        keys:
          - name
          - email
          - location
        x: 0
        y: 0
        columns: 1
        rows: 1

      - extension: json
        widget: JsonStatus
        title: API Health
        url: https://api.example.com/health
        statuses:
          - assert: equals(status, ok)
            status: success
            label: API Online
          - assert: equals(status, degraded)
            status: warning
            label: API Degraded
        x: 1
        y: 0
        columns: 1
        rows: 1

JSON client configuration

Configuration options

createJsonClient({
  // Base URL prepended to all requests (optional)
  baseUrl: 'https://api.example.com',

  // Default headers included in all requests (optional)
  headers: {
    Authorization: 'Bearer your-token',
    'Content-Type': 'application/json',
  },

  // Request timeout in milliseconds (default: 10_000)
  timeout: 5_000,
})

Environment variables

Use environment variables for sensitive configuration:

API_BASE_URL=https://api.example.com
API_TOKEN=your-secret-token
createJsonClient({
  baseUrl: process.env.API_BASE_URL,
  headers: {
    Authorization: `Bearer ${process.env.API_TOKEN}`,
  },
})

API endpoints

createJsonClient registers a single endpoint. Widgets subscribe to it through the endpoint parameter (default get), and you can call it from your own custom widgets.

| Endpoint | Parameters | Returns | | -------- | ------------------------------------------ | -------------------------- | | get | url, headers, method, body, path | { data, url, timestamp } |

The method and body parameters are accepted by the client but not surfaced by any built-in widget, so they are available for custom widgets (e.g. POST requests). When path is provided, the response is narrowed with a simplified JSONPath expression (see JSONPath support).

Available widgets

JsonKeys

Display specific key-value pairs extracted from a JSON response. Supports nested property access using dot notation.

Parameters:

| Parameter | Type | Required | Default | Description | | ---------- | --------------------- | -------- | ----------- | ------------------------------------------------ | | url | string | yes | - | URL to fetch the JSON data | | keys | string[] | yes | - | Array of keys to extract (supports dot notation) | | title | string | no | "JSON Keys" | Custom widget title | | subject | string | no | - | Custom widget subject | | headers | Record<string,string> | no | - | HTTP headers to send with the request | | path | string | no | - | JSONPath expression to extract specific data | | api | string | no | "json" | API subscription ID | | endpoint | string | no | "get" | API endpoint to call |

Example:

- extension: json
  widget: JsonKeys
  title: User Profile
  url: https://api.example.com/user/123
  headers:
    Authorization: Bearer token
  keys:
    - name
    - email
    - profile.age
    - stats.posts
  columns: 1
  rows: 1

Use dot notation to access nested properties: user.name reads data.user.name, stats.followers reads data.stats.followers.

CustomJson

Display JSON data with flexible rendering options: Eta templates, React components, or raw JSON.

Parameters:

| Parameter | Type | Required | Default | Description | | ----------- | --------------------- | -------- | ----------- | -------------------------------------------- | | url | string | yes | - | URL to fetch the JSON data | | title | string | no | "JSON Data" | Custom widget title | | subject | string | no | - | Custom widget subject | | headers | Record<string,string> | no | - | HTTP headers to send with the request | | path | string | no | - | JSONPath expression to extract specific data | | template | string | no | - | Eta template string for HTML rendering | | render | function | string | no | - | Custom React render function | | transform | function | string | no | - | Transform function to process data | | showRaw | boolean | no | true | Show raw JSON if no render/template provided | | api | string | no | "json" | API subscription ID | | endpoint | string | no | "get" | API endpoint to call |

Example (template):

- extension: json
  widget: CustomJson
  title: Weather
  url: https://api.example.com/weather
  template: |
    <div>
      <h2><%= data.city %></h2>
      <p>Temperature: <%= data.temp %>°C</p>
      <p>Condition: <%= data.condition %></p>
    </div>
  columns: 2
  rows: 1

Example (React render function):

<CustomJson
  title="Repository Stats"
  url="https://api.github.com/repos/facebook/react"
  transform={(data) => ({
    name: data.name,
    stars: data.stargazers_count,
    forks: data.forks_count,
  })}
  render={(data) => (
    <div>
      <h2>{data.name}</h2>
      <p>⭐ {data.stars} stars</p>
      <p>🍴 {data.forks} forks</p>
    </div>
  )}
/>

Example (raw JSON):

- extension: json
  widget: CustomJson
  title: API Response
  url: https://api.example.com/data
  showRaw: true
  columns: 2
  rows: 1

Note: string-based render functions (from YAML/JSON config) do not support JSX. Use template for HTML rendering, or pass an actual function in code for JSX.

Template syntax

Templates use the Eta template engine:

<!-- Output value -->
<%= data.field %>

<!-- Conditional -->
<% if (data.count > 5) { %> Many items <% } else { %> Few items <% } %>

<!-- Loop -->
<ul>
  <% data.items.forEach(item => { %>
  <li><%= item.name %></li>
  <% }) %>
</ul>

<!-- Expressions -->
<p>Total: <%= data.price * data.quantity %></p>

JsonStatus

Display status indicators based on assertions evaluated against JSON data.

Parameters:

| Parameter | Type | Required | Default | Description | | ---------- | --------------------- | -------- | ------------- | -------------------------------------------- | | url | string | yes | - | URL to fetch the JSON data | | statuses | StatusAssertion[] | yes | - | Array of status assertions to evaluate | | title | string | no | "JSON Status" | Custom widget title | | subject | string | no | - | Custom widget subject | | headers | Record<string,string> | no | - | HTTP headers to send with the request | | path | string | no | - | JSONPath expression to extract specific data | | api | string | no | "json" | API subscription ID | | endpoint | string | no | "get" | API endpoint to call |

StatusAssertion:

| Field | Type | Required | Description | | -------- | ---------------------------------------------- | -------- | ---------------------------------------- | | assert | string | yes | Assertion expression (see formats below) | | status | "success" | "warning" | "error" | "unknown" | yes | Status to display if assertion passes | | label | string | no | Optional label to display with status |

Example:

- extension: json
  widget: JsonStatus
  title: API Health
  url: https://api.example.com/health
  statuses:
    - assert: equals(status, ok)
      status: success
      label: API Online
    - assert: equals(status, degraded)
      status: warning
      label: API Degraded
    - assert: equals(status, down)
      status: error
      label: API Down
  columns: 1
  rows: 1

Assertion formats

| Format | Description | | -------------------------- | ----------------------------------------- | | equals(key, value) | The value strictly equals the expectation | | contains(key, substring) | The value contains the substring | | matches(key, pattern) | The value matches a regular expression | | truthy(key) | The value is truthy | | falsy(key) | The value is falsy |

Assertions are evaluated in order, and the last matching assertion wins. Put a broad default first and more specific conditions after it:

statuses:
  # Default status
  - assert: truthy(status)
    status: unknown
    label: Unknown Status

  # Specific conditions (evaluated last, take precedence)
  - assert: equals(status, operational)
    status: success
    label: All Systems Operational
  - assert: equals(status, outage)
    status: error
    label: Service Outage

JSONPath support

The path parameter narrows the response before it reaches a widget. This extension implements a simplified subset of JSONPath, not the full specification:

| Syntax | Description | | ------------- | ---------------------------------- | | $ | The entire response (root) | | $.user.name | Dot notation for nested properties | | user.name | Leading $. is optional | | $.items[0] | A specific array element by index | | $.items[*] | The entire array |

- extension: json
  widget: JsonKeys
  url: https://api.example.com/data
  path: $.users[0] # Extract the first user, then read its keys
  keys:
    - name
    - email

Authentication

Pass credentials through request headers, either per widget or as client defaults:

# Bearer token
headers:
  Authorization: Bearer your-token

# API key
headers:
  X-API-Key: your-api-key

# Basic auth (base64 encoded)
headers:
  Authorization: Basic dXNlcjpwYXNz

Troubleshooting

CORS errors

If you encounter CORS errors, ensure the API server includes appropriate CORS headers, or configure a proxy in your Dashfy server:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization

Authentication errors

Solution: Verify that your API token/key is valid and has the necessary permissions.

JSONPath not working

Solution: Confirm your expression uses the supported simplified syntax above. Full JSONPath features (filters, recursive descent, unions) are not implemented.

Template rendering errors

Solution: Check your Eta template syntax and ensure all referenced variables exist on the data object.

Contributing

Contributions are welcome. For issues and pull requests related to the extension, use the dashfy/dashfy-ext-json repository. Framework contributions belong in dashfy/dashfy.

Community

Join the community on Dashfy's Discord server to discuss the project, ask questions, or get help.

Join the conversation on X (Twitter) and follow @dashfydev for updates and announcements.

License

This project is licensed under the AGPL-3.0 License - see the LICENSE file for details.