@jsondrop/core

中文文档
0.1.0发布于 2026/11/01MITTypeScriptES Module零依赖

A typed, framework-agnostic client for Drupal's JSON:API.

原始使用文档
文档目录4 节
  1. Usage
  2. Typing your resources
  3. API
  4. Users logging in (OAuth)

@jsondrop/core

A typed, framework-agnostic client for Drupal's JSON:API. No dependencies.

npm install @jsondrop/core

Usage

import { JsonApiClient, clientCredentials } from '@jsondrop/core';

const drupal = new JsonApiClient({
  baseUrl: 'https://cms.example.com',
  auth: clientCredentials({ clientId: '…', clientSecret: '…', scope: 'content:read' }),
});

const articles = await drupal.getCollection('node--article', {
  query: {
    include: ['field_image', 'uid'],
    filter: { status: 1 },
    sort: ['-created'],
    page: { limit: 10 },
  },
});

articles[0].field_image; // the included file--file, with relationshipMeta.alt

Responses are deserialized: attributes and relationships sit on one object, and relationships are replaced by their included resources (recursively). A relationship whose target was not included is a stub ({ id, type, stub: true }, see isStub).

Typing your resources

Register resource types once, by declaration merging:

import type { ResourceBase, ToOne, ToMany } from '@jsondrop/core';

interface NodeArticle extends ResourceBase<'node--article'> {
  title: string;
  body: { value: string; processed: string } | null;
  field_tags: ToMany<TaxonomyTerm>;
  field_image: ToOne<File>;
}

declare module '@jsondrop/core' {
  interface ResourceTypes {
    'node--article': NodeArticle;
  }
}

const article = await drupal.getResource('node--article', id); // NodeArticle

API

Resources: getResource, getCollection, getPage (with next()), iterate (async, follows next links), createResource, updateResource, deleteResource, uploadFile.

Requests relative to the API prefix, for custom resources: get, post, patch, delete, request. They return the raw document alongside data, meta, links, status and headers.

Options (client, some per request):

Option
baseUrlSite origin
apiPrefixDefault /jsonapi
locale, defaultLocaleLanguage path prefix (/de/jsonapi/…)
authbearer, basic, sessionCookie, clientCredentials, or your own AuthStrategy; false per request
headersObject or (async) function
resourcePathsType → path, for types renamed with JSON:API Extras
fetchOptionsPassed to fetch, e.g. { next: { revalidate: 60, tags: ['node:1'] } }
onResponseCalled after each request, for logging

Users logging in (OAuth)

OAuthClient runs simple_oauth 6.x's authorization code flow with PKCE and refresh tokens (6.x dropped the password and implicit grants):

import { OAuthClient } from '@jsondrop/core';

const oauth = new OAuthClient({ baseUrl, clientId, clientSecret, redirectUri, scope: 'openid profile' });

// 1. Redirect to Drupal; keep state + codeVerifier in an httpOnly cookie.
const { url, state, codeVerifier } = await oauth.startLogin();
// 2. On the redirect back: checks the state, exchanges the code.
const tokens = await oauth.finishLogin({ code, state: returnedState }, { state, codeVerifier });
// 3. Store the tokens server-side; requests as the user:
const asUser = drupal.extend({ auth: oauth.strategy(tokenStore) });

The strategy refreshes the token before it expires and after a 401. Refresh tokens are single-use, so concurrent refreshes share one request: keep one OAuthClient per app. When Drupal rejects the refresh token, the store is emptied and requests throw LoginRequiredError: send the user to log in again. oauth.userInfo(accessToken) reads OpenID Connect claims. See example 7.

Renamed resources (JSON:API Extras): the API's entry point lists every type's path, so let the client read them once at startup instead of mapping them by hand:

const drupal = await new JsonApiClient({ baseUrl }).withDiscoveredPaths();
drupal.resourcePath('products'); // '/products', as the site configures it

client.extend({ headers }) derives a client that adds headers (a cart token, a market) and shares the parent's auth and token cache.

Errors: non-2xx responses throw JsonApiError with status, errors and the document; its message is Drupal's detail. On a 401, strategies with cached tokens are renewed and the request retried once.

Links: absolute URLs from documents (links.next) are sent to baseUrl's origin, so a Drupal behind a proxy that builds links with an internal host still works, and credentials never go elsewhere.

Queries also accept a prebuilt string, e.g. from drupal-jsonapi-params.

版本与发布记录

@jsondrop/core 共发布 2 个版本,首次发布于 2026/11/01;过去 12 个月发布了 2 个版本。

版本发布日期距今
0.1.0latest2026/11/011 天前
0.0.0-stage2026/11/011 天前

关于 @jsondrop/core 的常见问题

@jsondrop/core 是什么?

Drupal JSON:API的一个类型化的、与框架无关的客户端。

如何安装 @jsondrop/core?

在项目目录运行 npm install @jsondrop/core,或使用 pnpm add / yarn add / bun add。

@jsondrop/core 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@jsondrop/core 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@jsondrop/core 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.19。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@jsondrop/core 可以商用吗?

@jsondrop/core 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@jsondrop/core 还在维护吗?

最新版本 0.1.0 发布于 2026/11/01(1 天前);至今共发布 2 个版本,其中过去 12 个月发布了 2 个;首次发布于 2026/11/01。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。