PortalJS — Connect CKAN
Overview
Connect an existing portaljs-catalog portal to a live CKAN backend for the "decoupled /
any backend" path. The portal stops reading the static datasets.json manifest (and files
in /public/data/) and instead feeds its two data surfaces — the /search catalog and
the /@namespace/slug showcases — straight from a CKAN instance's REST API
(package_search / package_show) through a generated fetch client. Output is plain,
editable Next.js code with no runtime dependency — never @portaljs/ckan, whose bundle
wires React UI components to React 18 internals and crashes at import under the template's
React 19. Pages fetch CKAN server-side in getStaticProps/getStaticPaths, so the catalog
is pre-rendered at build time and the site can still be statically deployed. Run this right
after portaljs-new-portal to swap a freshly scaffolded portal's sample data over to CKAN.
Prerequisites
- A scaffolded PortalJS portal (see
portaljs-new-portal) with package.json, pages/,
datasets.json, pages/search.tsx, and pages/[owner]/[slug].tsx present.
- A CKAN base URL that is publicly reachable, e.g.
https://demo.dev.datopian.com.
- Node 18+ and npm available in the portal directory (no new packages are installed).
Instructions
The canonical, full step-by-step workflow is
.claude/commands/portaljs-connect-ckan.md —
the single source of truth. Read and follow it when executing. Summary:
- Gather input from
$ARGUMENTS — CKAN base URL (required), org filter (optional), group
filter (optional), portal directory (default .). If the URL is missing, interview the
user; never dead-end with a missing-input error.
- Validate the target directory is a
portaljs-catalog portal; if not, suggest
portaljs-new-portal instead of failing silently.
- Verify the CKAN backend is reachable via
package_search?rows=1, and validate each org
filter via organization_show; on failure, explain and re-prompt rather than dead-ending.
- Generate
lib/ckan.ts — a self-contained server-side fetch client wrapping
package_search and package_show, with DMS, ORG_FILTER, GROUP_FILTER, and
MAX_DATASETS as editable constants.
- Rewire
pages/search.tsx to list datasets from package_search, linking each to
/@namespace/slug via datasetHref; leave pages/index.tsx untouched.
- Overwrite
pages/[owner]/[slug].tsx to pre-render one page per dataset via
getStaticPaths and fetch details with package_show, previewing tabular resources
through the existing Table component.
- Verify the build with
npx next build; fix any error before reporting success.
- Report what changed: client, catalog, showcase, filters, and static page count.
Output
- Created:
lib/ckan.ts (fetch wrapper client — no dependency added to package.json).
- Modified:
pages/search.tsx (catalog reads package_search); pages/[owner]/[slug].tsx
(showcase reads package_show, overwritten to drop the datasets.json source).
- Unchanged:
pages/index.tsx (still the static search-first landing page).
- Verified:
npx next build succeeds and prints the static page count.
- Result:
/search and /@namespace/slug are served from the CKAN backend; the
DMS env var can override the base URL at deploy time without editing code.
Error Handling
| Symptom |
Cause |
Fix |
| Missing CKAN URL |
User invoked the skill with no $ARGUMENTS |
Ask for the base URL (and optional org/group filter); never error out. |
package_search request fails or times out |
URL isn't a reachable CKAN root |
Tell the user, ask them to confirm the URL, and retry. |
| Org filter not found |
organization_show returns success: false |
List valid orgs from organization_list and ask which one was meant. |
| Catalog renders empty after connecting |
Wrong org/group filter name in lib/ckan.ts |
Clear or correct the filter constants and rebuild. |
next build fails |
Typo in substituted CKAN_URL or bad TypeScript edit |
Print the log, fix the first error, and re-run before reporting success. |
<Table> fails to load a resource |
CKAN resource host blocks CORS |
Note that the Download link still works; prefer datastore-backed resources. |
Examples
Example 1 — Public CKAN demo, no filters
/portaljs-connect-ckan url=https://demo.dev.datopian.com
Example 2 — Restrict the catalog to one organization
/portaljs-connect-ckan url=https://demo.dev.datopian.com org=my-org
Example 3 — Filter by group and target a specific portal directory
/portaljs-connect-ckan url=https://data.example.gov group=education dir=./my-portal
Resources
Source: jeremylongshore/claude-code-plugins-plus-skills → plugins/community/portaljs/skills/portaljs-connect-ckan/SKILL.md
1---2name: portaljs-connect-ckan3description: Wire a scaffolded PortalJS portal to a CKAN backend over its API. Generates a tiny server-side fetch client (no runtime dependency) and feeds the /search catalog and /@namespace/slug showcases from CKAN instead of datasets.json. Use when connecting an existing portal to a live CKAN instance instead of a static manifest.4---567# PortalJS — Connect CKAN89## Overview1011Connect an existing `portaljs-catalog` portal to a live CKAN backend for the "decoupled /12any backend" path. The portal stops reading the static `datasets.json` manifest (and files13in `/public/data/`) and instead feeds its two data surfaces — the **`/search` catalog** and14the **`/@namespace/slug` showcases** — straight from a CKAN instance's REST API15(`package_search` / `package_show`) through a generated fetch client. Output is plain,16editable Next.js code with **no runtime dependency** — never `@portaljs/ckan`, whose bundle17wires React UI components to React 18 internals and crashes at import under the template's18React 19. Pages fetch CKAN server-side in `getStaticProps`/`getStaticPaths`, so the catalog19is pre-rendered at build time and the site can still be statically deployed. Run this right20after `portaljs-new-portal` to swap a freshly scaffolded portal's sample data over to CKAN.2122## Prerequisites2324- A scaffolded PortalJS portal (see `portaljs-new-portal`) with `package.json`, `pages/`,25 `datasets.json`, `pages/search.tsx`, and `pages/[owner]/[slug].tsx` present.26- A CKAN base URL that is publicly reachable, e.g. `https://demo.dev.datopian.com`.27- Node 18+ and npm available in the portal directory (no new packages are installed).2829## Instructions3031The canonical, full step-by-step workflow is32[`.claude/commands/portaljs-connect-ckan.md`](https://github.com/datopian/portaljs/blob/main/.claude/commands/portaljs-connect-ckan.md) —33the single source of truth. Read and follow it when executing. Summary:34351. Gather input from `$ARGUMENTS` — CKAN base URL (required), org filter (optional), group36 filter (optional), portal directory (default `.`). If the URL is missing, interview the37 user; never dead-end with a missing-input error.382. Validate the target directory is a `portaljs-catalog` portal; if not, suggest39 `portaljs-new-portal` instead of failing silently.403. Verify the CKAN backend is reachable via `package_search?rows=1`, and validate each org41 filter via `organization_show`; on failure, explain and re-prompt rather than dead-ending.424. Generate `lib/ckan.ts` — a self-contained server-side fetch client wrapping43 `package_search` and `package_show`, with `DMS`, `ORG_FILTER`, `GROUP_FILTER`, and44 `MAX_DATASETS` as editable constants.455. Rewire `pages/search.tsx` to list datasets from `package_search`, linking each to46 `/@namespace/slug` via `datasetHref`; leave `pages/index.tsx` untouched.476. Overwrite `pages/[owner]/[slug].tsx` to pre-render one page per dataset via48 `getStaticPaths` and fetch details with `package_show`, previewing tabular resources49 through the existing `Table` component.507. Verify the build with `npx next build`; fix any error before reporting success.518. Report what changed: client, catalog, showcase, filters, and static page count.5253## Output5455- **Created:** `lib/ckan.ts` (fetch wrapper client — no dependency added to `package.json`).56- **Modified:** `pages/search.tsx` (catalog reads `package_search`); `pages/[owner]/[slug].tsx`57 (showcase reads `package_show`, overwritten to drop the `datasets.json` source).58- **Unchanged:** `pages/index.tsx` (still the static search-first landing page).59- **Verified:** `npx next build` succeeds and prints the static page count.60- **Result:** `/search` and `/@namespace/slug` are served from the CKAN backend; the61 `DMS` env var can override the base URL at deploy time without editing code.6263## Error Handling6465| Symptom | Cause | Fix |66| --- | --- | --- |67| Missing CKAN URL | User invoked the skill with no `$ARGUMENTS` | Ask for the base URL (and optional org/group filter); never error out. |68| `package_search` request fails or times out | URL isn't a reachable CKAN root | Tell the user, ask them to confirm the URL, and retry. |69| Org filter not found | `organization_show` returns `success: false` | List valid orgs from `organization_list` and ask which one was meant. |70| Catalog renders empty after connecting | Wrong org/group filter name in `lib/ckan.ts` | Clear or correct the filter constants and rebuild. |71| `next build` fails | Typo in substituted `CKAN_URL` or bad TypeScript edit | Print the log, fix the first error, and re-run before reporting success. |72| `<Table>` fails to load a resource | CKAN resource host blocks CORS | Note that the Download link still works; prefer datastore-backed resources. |7374## Examples7576### Example 1 — Public CKAN demo, no filters7778```79/portaljs-connect-ckan url=https://demo.dev.datopian.com80```8182### Example 2 — Restrict the catalog to one organization8384```85/portaljs-connect-ckan url=https://demo.dev.datopian.com org=my-org86```8788### Example 3 — Filter by group and target a specific portal directory8990```91/portaljs-connect-ckan url=https://data.example.gov group=education dir=./my-portal92```9394## Resources9596- Full workflow: [`.claude/commands/portaljs-connect-ckan.md`](https://github.com/datopian/portaljs/blob/main/.claude/commands/portaljs-connect-ckan.md)97- Client, filters, and troubleshooting reference: [`references/reference.md`](references/reference.md)98- Related skills: `portaljs-new-portal`, `portaljs-add-dataset`, `portaljs-deploy`99- CKAN Action API documentation: <https://docs.ckan.org/en/latest/api/>100101---102103**Source:** [`jeremylongshore/claude-code-plugins-plus-skills`](https://github.com/jeremylongshore/claude-code-plugins-plus-skills) → `plugins/community/portaljs/skills/portaljs-connect-ckan/SKILL.md`