Download an ARC RO-Crate¶
DataPLANT DataHubs publish a generated ARC RO-Crate metadata file in the
project's generic Package Registry. Download it with
client.Packages.DownloadROCrateAsync(projectId) (available since version
0.5.0). The method takes the numeric project ID and uses these built-in
package coordinates:
| Coordinate | Value |
|---|---|
| Package name | isa_arc_json |
| Package version | 0.0.1 |
| File name | arc-ro-crate-metadata.json |
This retrieves the currently available generated main-branch export.
It does not generate a new export, and the available file is not guaranteed to
represent the latest commit on main.
For a file stored under different package coordinates, use
DownloadGenericFileAsync.
Download the metadata¶
Replace 42 with the ARC's numeric project ID. The example uses anonymous
access for a public export. For server-side downloads that require credentials, replace
Authentication.Anonymous() with
Authentication.PersonalAccessToken("your-datahub-pat").
For browser downloads that require redirects, use OAuth bearer authentication;
see redirects and authentication for supported
authentication modes and CORS requirements.
This shared JavaScript / TypeScript example retrieves the JSON-LD text in a browser or Node.js. It does not write a file and needs no filesystem import. Frontend npm imports are resolved by your application's bundler, such as Vite.
import { DataHubClient } from "@nfdi4plants/datahub-client";
import { Authentication } from "@nfdi4plants/datahub-client/Http/Authentication.js";
const client = new DataHubClient("https://git.nfdi4plants.org", Authentication.Anonymous());
const projectId = 42;
const metadata = await client.Packages.DownloadROCrateAsync(projectId);
Download and save¶
The method returns JSON-LD text. The F#, Node.js, and Python examples save that
text as UTF-8 to arc-ro-crate-metadata.json in the current working directory,
replacing an existing file with that name. The browser example starts a file
download through the browser. In an existing Python async application, use
await download_ro_crate() inside the running event loop instead of
asyncio.run.
#r "nuget: DataHubClient"
open System.IO
open System.Text
open DataHubClient
let client = DataHubClient("https://git.nfdi4plants.org", Authentication.Anonymous())
let projectId = 42
let metadata =
client.Packages.DownloadROCrateAsync(projectId)
|> Async.RunSynchronously
File.WriteAllText("arc-ro-crate-metadata.json", metadata, UTF8Encoding(false))
printfn "Saved arc-ro-crate-metadata.json"
import { DataHubClient } from "@nfdi4plants/datahub-client";
import { Authentication } from "@nfdi4plants/datahub-client/Http/Authentication.js";
const client = new DataHubClient("https://git.nfdi4plants.org", Authentication.Anonymous());
const projectId = 42;
const metadata = await client.Packages.DownloadROCrateAsync(projectId);
import { writeFile } from "node:fs/promises";
await writeFile("arc-ro-crate-metadata.json", metadata, "utf8");
console.log("Saved arc-ro-crate-metadata.json");
Save this JavaScript helper as browser.mjs in your application. It uses a
Blob and a download link to save the returned text:
/** Download an ARC's JSON-LD metadata using the browser's save-file flow. */
export async function downloadROCrate(client, projectId) {
const metadata = await client.Packages.DownloadROCrateAsync(projectId);
const blob = new Blob([metadata], { type: "application/ld+json;charset=utf-8" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "arc-ro-crate-metadata.json";
link.hidden = true;
document.body.append(link);
try {
link.click();
} finally {
link.remove();
// Give the browser time to start the download before releasing its URL.
setTimeout(() => URL.revokeObjectURL(url), 1000);
}
}
For a .ts helper with explicit parameter types, see
TypeScript browser downloads.
Call it from a button in your frontend module. These npm imports are resolved by your application's bundler, such as Vite:
import { DataHubClient } from "@nfdi4plants/datahub-client";
import { Authentication } from "@nfdi4plants/datahub-client/Http/Authentication.js";
import { downloadROCrate } from "./browser.mjs";
const client = new DataHubClient("https://git.nfdi4plants.org", Authentication.Anonymous());
const button = document.createElement("button");
button.textContent = "Download RO-Crate";
button.addEventListener("click", () => {
downloadROCrate(client, 42).catch(console.error);
});
document.body.append(button);
import asyncio
from pathlib import Path
from datahub_client import DataHubClient, Authentication
client = DataHubClient("https://git.nfdi4plants.org", Authentication.Anonymous())
project_id = 42
async def download_ro_crate():
metadata = await client.Packages.DownloadROCrateAsync(project_id)
Path("arc-ro-crate-metadata.json").write_text(metadata, encoding="utf-8", newline="")
print("Saved arc-ro-crate-metadata.json")
asyncio.run(download_ro_crate())
Browser imports and CORS¶
node:fs/promises is Node.js's filesystem module; it cannot be imported into a
browser application. It appears in the Node.js example to write the downloaded
text to disk. DataHubClient itself uses fetch and returns a string; use the
browser example above to save that string as a file.
When adapting the Node.js save example for a browser, remove its filesystem
import as well as the writeFile call. A retained static import can prevent the
module from loading even when the imported function is never called. The
metadata-only snippet above contains just the client imports and download.
When the DataHub and your application have different origins, the DataHub must
allow your application's origin through
CORS. Requests
include X-DataHubClient-Version, so even anonymous downloads need a successful
preflight that allows this header. Authenticated downloads also need the
authentication header allowed, such as PRIVATE-TOKEN. A rejected CORS request
is a separate problem from an unsupported node:fs/promises import.
Missing or inaccessible exports¶
A public project does not guarantee an available, anonymously readable export.
If the requested export is missing, the server returns HTTP 404, exposed as
NotFoundError. HTTP 401 or 403 is exposed as UnauthorizedError and means
the request needs valid credentials or additional access. Check the project
and package visibility and use an appropriate token when needed.
The download raises an error in these cases; it does not return an empty
RO-Crate. Errors expose StatusCode and the server's response Body for
diagnostics.
Redirects and authentication¶
Package downloads can redirect from the DataHub to a signed object-storage URL.
The default .NET and Python transports, and the JavaScript transport in Node,
follow those redirects while removing Authorization, PRIVATE-TOKEN, and
JOB-TOKEN when the origin changes (scheme, hostname, or port). Removed
credentials are not restored if a later redirect returns to the DataHub.
Browsers hide intermediate redirect destinations from JavaScript. A browser
request using Authentication.PersonalAccessToken(...) or
Authentication.JobToken(...) therefore stops with an explanatory error at
any redirect, including a same-origin redirect, before sending the token to
the destination. Anonymous downloads and OAuth bearer authentication through
Authentication.OAuthToken(...) use native redirect handling; browsers remove
Authorization when the origin changes. CORS must permit the download too.
For private browser downloads that require GitLab's custom token headers, use
a server-side client to perform the download.
If you inject your own .NET HttpClient into DotNetHttpClient, configure its
handler with AllowAutoRedirect = false so the transport can inspect redirects.
Supply authentication through Authentication or individual request headers;
credential-bearing HttpClient.DefaultRequestHeaders cause cross-origin
redirects to fail, because HttpClient would otherwise add those defaults
back. Custom handlers must not add credentials to redirected requests.
See live integration tests for verifying the DataHub's storage configuration and actual download path.
Offline verification
The F#, JavaScript, TypeScript, and Python
sample programs
replace HTTP access with a fixture, check the exact package endpoint and
anonymous headers, and verify the saved UTF-8 content in a temporary
working directory. The Python sample uses the real HTTPX client with a
mock transport to verify HTTP 302 redirects to an object-storage host.
The browser checks exercise the helper above with a
local HTTP server and verify the downloaded file. These checks need no
DataHub credentials or live export.
See Packages for the remaining Package Registry calls.