Skip to content

/portaljs-add-dcat

Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.

From plugin
portaljs
2.4k25 skills25 commands
> /plugin marketplace add datopian/portaljs
> /plugin install portaljs@datopian-portaljs

Command definition

portaljs-add-dcat.md
description: Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.
allowed-tools: Read, Write, Edit, Bash, WebFetch

/portaljs-add-dcat

Turn an existing PortalJS (`portaljs-catalog`) portal into a **harvestable** data catalog: emit standards-compliant **DCAT** metadata feeds so external catalogs and government data portals (data.europa.eu, data.gov, national portals) can harvest its datasets automatically.

PortalJS is Frictionless-native (a dataset is a Data Package — see `/portaljs-define-schema`); **DCAT is the serialization + harvest layer on top** (`lib/metadata/dcat.ts` + `lib/metadata/dcat-profiles.ts`). This skill selects one or more DCAT **application profiles**, maps every dataset's metadata to them, and writes static feed files at build so they harvest on **any** host (static Cloudflare Pages, a CDN, a Worker) — no runtime.

What it produces

Wired into `predev`/`prebuild` via `scripts/generate-dcat.ts`, so the feeds are always fresh:

| File | What | |------|------| | `public/catalog.jsonld` · `catalog.ttl` · `catalog.rdf` | **Canonical** feed (first profile) in JSON-LD, Turtle, RDF/XML — the stable autodiscovery targets | | `public/catalog.<profile>.{jsonld,ttl,rdf}` | One feed per configured profile × serialization (e.g. `catalog.dcat-ap.ttl`) | | `public/catalog-feeds.json` | Machine-readable index of every emitted feed | | `<link rel="alternate" type="application/ld+json" href="/catalog.jsonld">` in `_document.tsx` | Autodiscovery — how harvesters find the feed |

Config lives in **`dcat.config.json`** at the portal root (created/updated by this skill). All feeds regenerate from `datasets.json` + this config.

Supported profiles

Profiles are a **pluggable registry** (`lib/metadata/dcat-profiles.ts`) — national profiles are config/data, not hardcoded, and multiple can be emitted at once.

| id | Profile | Notes | |----|---------|-------| | `dcat-3` | DCAT 3 (W3C) | Default. `conformsTo` DCAT-3. | | `dcat-2` | DCAT 2 (W3C) | Same core subset stamped as DCAT-2. | | `dcat-ap` | DCAT-AP (data.europa.eu) | EU profile: adds foaf/vcard, publisher, contactPoint, `dcat:theme` (EU data-theme vocab). | | `dcat-us` | DCAT-US 3.0 (data.gov) | US federal / Project Open Data: adds publisher, contactPoint, `dct:accessLevel`. | | `geodcat-ap` | GeoDCAT-AP (spatial / INSPIRE) | DCAT-AP + spatial coverage: `dct:spatial` (bbox/geometry as `gsp:wktLiteral`) + `dcat:spatialResolutionInMeters` from each dataset's `dcat.spatial` (or the catalog-wide `spatial` config). | | `croissant` | Croissant (MLCommons / schema.org) | ML-dataset metadata: a schema.org `DataCatalog` of Croissant `Dataset`s with `cr:FileObject` distributions + `cr:RecordSet`/`cr:Field` mapped from the Frictionless Table Schema. **JSON-LD only.** | | `dcat-ap-se` · `dcat-ap-ch` · `dcat-ap-de` | National (Sweden / Switzerland / Germany) | DCAT-AP + national `conformsTo`; examples of the plug mechanism. | | `dcat-ap-it` | DCAT-AP_IT (Italy — AgID / dati.gov.it) | DCAT-AP + `dcatapit:*` types, agents with `dct:identifier` (IPA code), `dct:rightsHolder` + `dct:accrualPeriodicity` on every dataset, licence on every distribution. Needs `publisher.identifier`. |

**Add another national profile** without code: `registerDcatProfile(makeNationalProfile({ id, label, conformsTo, context }))` in a small module the app loads, then list its id in `dcat.config.json`. See `lib/metadata/README.md`.

**GeoDCAT-AP spatial input.** Per dataset, add `"dcat": { "spatial": { "bbox": "POLYGON((…))" } }` (or `"geometry"`, `"uri"`, `"spatialResolutionInMeters"`) to `datasets.json`; or set a catalog-wide `"spatial"` in `dcat.config.json`. WKT strings are serialized as `gsp:wktLiteral`. A dataset with no spatial coverage is still GeoDCAT-AP-conformant.

**DCAT-AP_IT input.** Set `publisher.identifier` in `dcat.config.json` to the IPA code (Indice della Pubblica Amministrazione) of the publishing administration — the VAT number for a private body — plus EU `themes` and a licence (dataset `licenses`, or `license` in config). Dataset identifiers become `<IPA code>:<namespace>/<slug>`. Per dataset, `datasets.json` can add `"dcat": { "rightsHolder": { "name": "Comune di Palermo", "identifier": "c_g273" }, "accrualPeriodicity": "ANNUAL" }` (an EU frequency token or IRI) and an explicit `"identifier"` (e.g. `"c_g273:D.12"`). Defaults: the rights holder is the dataset's publisher, else the catalog publisher; the frequency is `UNKNOWN` (AgID allows it); `dct:modified` falls back to the catalog's. Other profiles ignore these fields.

**Croissant** describes datasets for ML tooling (schema.org JSON-LD). It reads the Frictionless Table Schema to emit `cr:RecordSet`/`cr:Field`, and stamps a `sha256` on each `cr:FileObject` from the local `public/data/<file>` bytes (mlcroissant requires a checksum on a hosted file; remote-only files are emitted without one). Because a Croissant document describes a single dataset, the feed is a `DataCatalog` whose `dataset[]` entries are each a standalone Croissant `Dataset`.

Required input — ask, don't error

  • **Portal directory** — path to the portal project (defaults to current directory).
  • **Profiles** — which to emit (default: `dcat-3`). For national-portal harvesting the

user wants `dcat-ap` (EU) or `dcat-us` (US) + optionally a national profile.

  • **Publisher + contact** — DCAT-AP and DCAT-US **require** `dct:publisher` and

`dcat:contactPoint`. If the user picks one of those profiles, ask for the publishing organization (name + homepage) and a contact (name + email). Without them the feed is emitted but **fails conformance** — the generator will say what's missing.

  • **Site URL** — the portal's public origin (e.g. `https://data.example.org`), so feed

links are absolute. Without it links are

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 commands on portaljs.

add-chart
Auto-invoked

add-chart

Command

Renamed → /portaljs-add-chart. This alias will be removed next minor release.

@datopian@datopianInstall Command
add-dataset
Auto-invoked

add-dataset

Command

Renamed → /portaljs-add-dataset. This alias will be removed next minor release.

@datopian@datopianInstall Command
add-map
Auto-invoked

add-map

Command

Renamed → /portaljs-add-map. This alias will be removed next minor release.

@datopian@datopianInstall Command
add-resource
Auto-invoked

add-resource

Command

Renamed → /portaljs-add-resource. This alias will be removed next minor release.

@datopian@datopianInstall Command
arcgis-to-portaljs
Auto-invoked

arcgis-to-portaljs

Command

Migrate a whole ArcGIS Hub site (opendata.arcgis.com or a Hub Premium custom domain) into a PortalJS Arc portal end-to-end.…

@datopian@datopianInstall Command
architect
Auto-invoked

architect

Command

Renamed → /portaljs-architect. This alias will be removed next minor release.

@datopian@datopianInstall Command

More commands from other plugins.

build
Auto-invoked

build

Command

Implement tasks incrementally — build, test, verify, commit. Add "auto" to run the whole plan in one approved pass.

@addyosmani@addyosmaniInstall Command