Skip to content

/portaljs-define-schema

Define a dataset's metadata profile — infer a Frictionless Table Schema from its data, add Data Package metadata (license, sources, keywords), and write it into datasets.json so the showcase renders a typed field table. Extend or customize via the L0-L3 profile ladder. Use when

From plugin
portaljs
2.4k14 skills25 commands

Claude Code

$ npx -y skills add datopian/portaljs --skill portaljs-define-schema --agent claude-code

SKILL.md

portaljs-define-schema.SKILL.md
name: portaljs-define-schema
description: Define a dataset's metadata profile — infer a Frictionless Table Schema from its data, add Data Package metadata (license, sources, keywords), and write it into datasets.json so the showcase renders a typed field table. Extend or customize via the L0-L3 profile ladder. Use when a registered dataset needs field types, constraints, or catalog metadata before publishing.
allowed-tools: Read, Write, Edit, Bash(npx:*), Bash(node:*), Bash(head:*)
version: 1.0.0
author: Datopian <hello@datopian.com>
license: MIT
compatibility: Claude Code with PortalJS portals (Next.js 14, React 18, Node 18+). Runs from any project via the plugin, a personal ~/.claude/commands install, or a portaljs clone.
tags:
  - portaljs
  - data-portal
  - schema
  - frictionless
  - metadata
  - datapackage

PortalJS — Define Schema

Overview

Define a dataset's metadata profile — the **authoring** skill for the metadata-profile contract (`lib/metadata`). Where `portaljs-add-dataset` registers *that* a dataset exists, this skill describes *what its data means*: infer a Frictionless **Table Schema** (fields, types, constraints) from sampled data, add the **Data Package** fields a catalog surfaces (title, licenses, sources, keywords), and write them onto the dataset's entry in `datasets.json`. The showcase at `/@<namespace>/<slug>` then renders a typed field table instead of a bare preview. The model is Frictionless-native; DCAT is a serialization layer built on top later, not authored here.

The skill runs on a profile ladder — reach for higher levels only when needed:

| Level | What it is | When | | --- | --- | --- | | L0 | Default `frictionless-tabular` profile; declare schema + metadata. | Default. Standard tabular CSV/TSV. | | L1 | L0 plus extra descriptive package fields. | Extra metadata, standard validation is fine. | | L2 | Fully custom profile (own schema template + `validate()`). | A dataset type needing custom validation rules. | | L3 | Multiple registered profiles, resolved per dataset. | A portal mixing dataset types. |

The skill is interactive and never dead-ends: if input is thin it interviews in short rounds, infers defaults from the data, echoes the schema for confirmation, and accepts "use defaults" to proceed with the inferred schema as-is.

Prerequisites

  • A scaffolded PortalJS portal with the metadata contract (`lib/metadata/types.ts`,

`pages/[owner]/[slug].tsx`); see `portaljs-new-portal`.

  • The target dataset already registered in `datasets.json` (see `portaljs-add-dataset`).
  • For tabular schema inference, the dataset's CSV/TSV file present under

`PORTAL_DIR/public/data/`. JSON/GeoJSON datasets get package metadata only — no `fields`.

  • Node 18+; `tsx` optional, used for the schema-validation check.

Instructions

The canonical, full step-by-step workflow is [`.claude/commands/portaljs-define-schema.md`](https://github.com/datopian/portaljs/blob/main/.claude/commands/portaljs-define-schema.md) — the single source of truth. Read and follow it when executing. Summary:

1. Gather `PORTAL_DIR`, `DATASET` (slug or `namespace/slug`), and `LEVEL` (default `L0`) from input; if `DATASET` is missing, list the portal's slugs and ask. 2. Validate the portal has the metadata contract (`datasets.json`, `lib/metadata/types.ts`, the showcase route); proceed anyway if `lib/metadata/` predates the contract. 3. For tabular datasets, sample the header and ~50 rows from `public/data/<file>` and infer each field's type, constraints (`required`, `unique`, `pattern`), and a primary key. 4. Echo the inferred schema as a table for confirmation; offer to go beyond L0 only if warranted. 5. Ask for optional Data Package metadata: license, source(s), keywords, version. 6. Write the schema and metadata onto the dataset's entry in `datasets.json` in place, preserving all other fields; for L2/L3, scaffold and register a custom profile module. 7. Optionally validate the schema against the data's rows via the profile's `validate()`. 8. Verify with `npx next build`; fix malformed JSON or an invalid `FieldType` before reporting success. 9. Report the profile, fields, metadata set, and the showcase URL.

Output

  • **Modified:** `datasets.json` (target entry gains `profile`, `schema`, `licenses`,

`sources`, `keywords`, `version` — unset fields omitted).

  • **Created (L2/L3 only):** `lib/metadata/<profile-id>.ts`; `lib/metadata/registry.ts`

updated with a `registerProfile(...)` call.

  • **Verified:** `npx next build` succeeds.
  • **Result:** `/@<namespace>/<slug>` renders a typed field table in place of a bare preview.

Error Handling

| Symptom | Cause | Fix | | --- | --- | --- | | Dataset not found in `datasets.json` | Wrong slug or missing `namespace/` prefix | List available slugs and re-prompt. | | `lib/metadata/` missing | Portal predates the metadata-profile contract | Proceed anyway — schema fields are optional and ignored by older showcases. | | No `fields` schema produced | Dataset is JSON/GeoJSON, not tabular | Expected — capture Data Package metadata only. | | Validation reports type errors | Sampled values don't coerce to the inferred type | Relax the `type` or drop the offending `required`/`pattern` constraint. | | `next build` fails on `datasets.json` | Stray comma or a type outside `FieldType` | Fix the JSON/type and rebuild before reporting success. |

Examples

Example 1 — Default L0 schema for a CSV dataset

/portaljs-define-schema population-2022

Infers fields (e.g. `country: string`, `population: integer`), drafts titles, asks for a license and source, and writes the schema under the default `frictionless-tabular` profile.

Example 2 — Metadata only for a GeoJSON dataset

/portaljs-define-schema neighborhoods-geo

GeoJSON has no tabular `fields`; the skill captures license, sources, and keywords onto the entry and skips schema inference.

Example 3 — Custom L2 profile with its own validation

Read more
@datopian
Ships withportaljs

🌀 AI-native framework for building data portals. Scaffold a full portal from a brief and load datasets in minutes with agentic skills — any backend (CKAN, GitHub, Frictionless).

Stars on GitHub

Momentum

stars in total
2.4k
Star history of datopian/portaljs on GitHub, a chart by star-history.com

Other skills on portaljs.

arcgis-to-portaljs
Auto-invoked

arcgis-to-portaljs

Migrate a whole ArcGIS Hub site into a PortalJS Arc portal end-to-end. Harvests the Hub /data.json (DCAT-US) inventory, exports…

@datopian@datopianInstall Skill
portaljs-add-chart
Auto-invoked

portaljs-add-chart

Add a chart (line, bar, area, pie, or scatter) to a dataset's showcase in a PortalJS portal. Installs recharts, writes a reusable…

@datopian@datopianInstall Skill
portaljs-add-dataset
Auto-invoked

portaljs-add-dataset

Add a dataset (CSV, TSV, JSON, or GeoJSON) to an existing PortalJS portal. Appends an entry to datasets.json so the catalog and…

@datopian@datopianInstall Skill
portaljs-add-dcat
Auto-invoked

portaljs-add-dcat

Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3,…

@datopian@datopianInstall Skill
portaljs-add-geo
Auto-invoked

portaljs-add-geo

Auto-ingest a geospatial file (GeoJSON, Shapefile, GeoPackage, KML/KMZ, FlatGeobuf, CSV-with-geometry) into a PortalJS portal on…

@datopian@datopianInstall Skill
portaljs-add-map
Auto-invoked

portaljs-add-map

Render a GeoJSON dataset on an interactive Leaflet map in the Views section of a dataset's showcase. Installs react-leaflet and a…

@datopian@datopianInstall Skill

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills10d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc12d293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react1d252k
@facebook@facebookInstall Skill

Questions about portaljs-define-schema.

Which coding agents can use portaljs-define-schema?
portaljs-define-schema installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.