Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changeset/server-subpath-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
"ra-data-simple-prisma": minor
---

Add `ra-data-simple-prisma/server` subpath export for server-side use.

The server entry contains only the Prisma request handlers and helpers
and has no dependency on `react-admin` (and therefore no transitive
dependency on `ra-core`, `ra-ui-materialui`, or `react-router-dom`).
Importing from this subpath inside Next.js / Vercel serverless functions
avoids tracing UI-only packages into the function bundle and fixes ESM
resolution failures caused by `react-router-dom@7.x`'s `exports` field.

The top-level `ra-data-simple-prisma` import is unchanged and still
re-exports both the client-side `dataProvider` and the server-side
handlers, so this is a non-breaking change. `react-admin` is now marked
as an optional peer dependency for server-only consumers.
16 changes: 13 additions & 3 deletions packages/ra-data-simple-prisma/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,18 @@ export default ReactAdmin;

### Backend: import the request handlers

Simplest implementation ever:
Server-side code should import from the `/server` subpath. This entry
contains only the request handlers and helpers, with no dependency on
`react-admin` (and therefore no transitive dependency on `ra-core`,
`ra-ui-materialui`, or `react-router-dom`). This keeps Next.js / Vercel
serverless bundles small and avoids ESM resolution issues caused by
tracing UI-only packages into the function.

```js
// -- Example for Next Pages router --
// /api/[resource].ts <= catch all resource requests

import { defaultHandler } from "ra-data-simple-prisma";
import { defaultHandler } from "ra-data-simple-prisma/server";
import { prismaClient } from "../prisma/client"; // <= Your prisma client instance

export default async function handler(req, res) {
Expand All @@ -48,7 +53,7 @@ export default async function handler(req, res) {
// -- Example for Next App router --
// /app/api/[resource]/route.ts <= catch all resource requests

import { defaultHandler } from "ra-data-simple-prisma";
import { defaultHandler } from "ra-data-simple-prisma/server";
import { prismaClient } from "../prisma/client"; // <= Your prisma client instance
import { NextResponse } from "next/server";

Expand All @@ -61,6 +66,11 @@ const handler = async (req: Request) => {
export { handler as GET, handler as POST };
```

> The top-level `ra-data-simple-prisma` import still re-exports the
> handlers for backwards compatibility, but new server-side code should
> prefer `ra-data-simple-prisma/server` so that `react-admin` stays out
> of the server bundle.

### (List) Filters: Available Operators

To be used with an underscore after the `source` name
Expand Down
23 changes: 22 additions & 1 deletion packages/ra-data-simple-prisma/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,24 @@
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js",
"default": "./dist/index.js"
},
"./server": {
"types": "./dist/server.d.ts",
"import": "./dist/server.mjs",
"require": "./dist/server.js",
"default": "./dist/server.js"
},
"./package.json": "./package.json"
},
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts --treeshake --external react-admin --external ra-core --external ra-ui-materialui",
"build": "tsup src/index.ts src/server.ts --format esm,cjs --dts --treeshake --external react-admin --external ra-core --external ra-ui-materialui",
"build:check": "node -e \"const fs=require('fs');const files=['dist/server.js','dist/server.mjs'];for (const f of files){const c=fs.readFileSync(f,'utf8');if(/['\\\"]react-admin['\\\"]/.test(c)){console.error('FAIL: '+f+' references react-admin');process.exit(1)}}console.log('OK: server bundle has no react-admin reference')\"",
"dev": "pnpm build --watch",
"lint": "eslint src --fix",
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
Expand Down Expand Up @@ -39,6 +55,11 @@
"@prisma/client": ">=5",
"react-admin": ">=5.7.2"
},
"peerDependenciesMeta": {
"react-admin": {
"optional": true
}
},
"dependencies": {
"axios": "^1.13.5",
"deverything": "^4.10.1"
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-data-simple-prisma/src/Http.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {
import type {
CreateParams,
DeleteManyParams,
DeleteParams,
Expand Down
4 changes: 2 additions & 2 deletions packages/ra-data-simple-prisma/src/audit/auditHandler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AuditOptions, AuditLogPayload, defaultAuditOptions } from "./types";
import { RaPayload } from "../Http";
import { Identifier } from "react-admin";
import type { RaPayload } from "../Http";
import type { Identifier } from "react-admin";

export const auditHandler = async (
request: RaPayload,
Expand Down
2 changes: 1 addition & 1 deletion packages/ra-data-simple-prisma/src/audit/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PlainObject } from "deverything";
import { AuthProvider, Identifier } from "react-admin";
import type { AuthProvider, Identifier } from "react-admin";

export type AuditOptions = {
model: { create: Function };
Expand Down
28 changes: 28 additions & 0 deletions packages/ra-data-simple-prisma/src/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Server-only entry point.
// This module intentionally excludes any code that depends on `react-admin`
// (notably the `dataProvider` and `HttpError`) so that Next.js / Vercel
// serverless functions importing handlers do not transitively pull in
// `react-admin`, `ra-core`, `ra-ui-materialui`, or `react-router-dom`.
//
// Use `ra-data-simple-prisma/server` from server-side code, and the
// top-level `ra-data-simple-prisma` import from client-side code.

export * from "./audit";
export * from "./createHandler";
export * from "./defaultHandler";
export * from "./deleteHandler";
export * from "./deleteManyHandler";
export * from "./extractOrderBy";
export * from "./extractSkipTake";
export * from "./extractWhere";
export * from "./getInfiniteListHandler";
export * from "./getListHandler";
export * from "./getManyHandler";
export * from "./getManyReferenceHandler";
export * from "./getOneHandler";
export * from "./hasFieldChanged";
export * from "./Http";
export * from "./isExport";
export * from "./permissions";
export * from "./updateHandler";
export * from "./updateManyHandler";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi I very much understand the full picture and thank you for chipping in - but most these exports (audit, http) have dependency on react-admin... so not sure where the trick is? 🤔

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as a matter of fact this is an exact copy of the top-level index....

24 changes: 24 additions & 0 deletions packages/ra-data-simple-prisma/tests/serverBundle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { describe, expect, test } from "@jest/globals";
import { existsSync, readFileSync } from "fs";
import { join } from "path";

const distDir = join(__dirname, "..", "dist");

// These tests only run if `pnpm build` has been executed beforehand.
// They guard against a regression in which the server-only entry
// transitively imports `react-admin`, which would pull react-admin (and
// its transitive deps) into Next.js / Vercel serverless bundles that
// only need the Prisma-side handlers.
const maybeDescribe = existsSync(join(distDir, "server.js")) ? describe : describe.skip;

maybeDescribe("dist/server bundle", () => {
test("CJS server bundle does not reference react-admin", () => {
const contents = readFileSync(join(distDir, "server.js"), "utf8");
expect(contents).not.toMatch(/['"]react-admin['"]/);
});

test("ESM server bundle does not reference react-admin", () => {
const contents = readFileSync(join(distDir, "server.mjs"), "utf8");
expect(contents).not.toMatch(/['"]react-admin['"]/);
});
});