@carlonicora/nextjs-jsonapi 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/README.md +889 -0
  2. package/dist/AbstractService-BKlpJA61.d.mts +109 -0
  3. package/dist/AbstractService-D9eSVKNa.d.ts +109 -0
  4. package/dist/ApiData-DPKNfY-9.d.mts +10 -0
  5. package/dist/ApiData-DPKNfY-9.d.ts +10 -0
  6. package/dist/ApiDataInterface-DPP8s46n.d.mts +21 -0
  7. package/dist/ApiDataInterface-DPP8s46n.d.ts +21 -0
  8. package/dist/ApiRequestDataTypeInterface-CUKFDBx2.d.mts +20 -0
  9. package/dist/ApiRequestDataTypeInterface-CUKFDBx2.d.ts +20 -0
  10. package/dist/ApiResponseInterface-BHN5D9r5.d.mts +16 -0
  11. package/dist/ApiResponseInterface-DDI7QQPR.d.ts +16 -0
  12. package/dist/BlockNoteEditor-UVO3VZZE.mjs +396 -0
  13. package/dist/BlockNoteEditor-UVO3VZZE.mjs.map +1 -0
  14. package/dist/BlockNoteEditor-VFWG6LXI.js +396 -0
  15. package/dist/BlockNoteEditor-VFWG6LXI.js.map +1 -0
  16. package/dist/JsonApiRequest-S3ICLM7B.mjs +20 -0
  17. package/dist/JsonApiRequest-S3ICLM7B.mjs.map +1 -0
  18. package/dist/JsonApiRequest-ZZLSP26T.js +20 -0
  19. package/dist/JsonApiRequest-ZZLSP26T.js.map +1 -0
  20. package/dist/atoms/index.d.mts +12 -0
  21. package/dist/atoms/index.d.ts +12 -0
  22. package/dist/atoms/index.js +9 -0
  23. package/dist/atoms/index.js.map +1 -0
  24. package/dist/atoms/index.mjs +9 -0
  25. package/dist/atoms/index.mjs.map +1 -0
  26. package/dist/breadcrumb.item.data.interface-CgB4_1EE.d.mts +6 -0
  27. package/dist/breadcrumb.item.data.interface-CgB4_1EE.d.ts +6 -0
  28. package/dist/chunk-2K3Q24UF.js +89 -0
  29. package/dist/chunk-2K3Q24UF.js.map +1 -0
  30. package/dist/chunk-2LM6LCJW.mjs +1091 -0
  31. package/dist/chunk-2LM6LCJW.mjs.map +1 -0
  32. package/dist/chunk-366S2JCC.mjs +31 -0
  33. package/dist/chunk-366S2JCC.mjs.map +1 -0
  34. package/dist/chunk-3FBCC4G3.js +8 -0
  35. package/dist/chunk-3FBCC4G3.js.map +1 -0
  36. package/dist/chunk-4HCRAOS5.js +28 -0
  37. package/dist/chunk-4HCRAOS5.js.map +1 -0
  38. package/dist/chunk-5W6AKZE6.mjs +131 -0
  39. package/dist/chunk-5W6AKZE6.mjs.map +1 -0
  40. package/dist/chunk-6GKHCVF6.js +98 -0
  41. package/dist/chunk-6GKHCVF6.js.map +1 -0
  42. package/dist/chunk-7QVYU63E.js +7 -0
  43. package/dist/chunk-7QVYU63E.js.map +1 -0
  44. package/dist/chunk-A3J3AAYM.mjs +97 -0
  45. package/dist/chunk-A3J3AAYM.mjs.map +1 -0
  46. package/dist/chunk-A5DDIABK.js +4209 -0
  47. package/dist/chunk-A5DDIABK.js.map +1 -0
  48. package/dist/chunk-AUXK7QSA.mjs +15 -0
  49. package/dist/chunk-AUXK7QSA.mjs.map +1 -0
  50. package/dist/chunk-AWONBQQP.js +97 -0
  51. package/dist/chunk-AWONBQQP.js.map +1 -0
  52. package/dist/chunk-BLWVZK6J.mjs +28 -0
  53. package/dist/chunk-BLWVZK6J.mjs.map +1 -0
  54. package/dist/chunk-C7C7VY4F.mjs +77 -0
  55. package/dist/chunk-C7C7VY4F.mjs.map +1 -0
  56. package/dist/chunk-CXQOWQSY.js +55 -0
  57. package/dist/chunk-CXQOWQSY.js.map +1 -0
  58. package/dist/chunk-DD3KISNB.mjs +98 -0
  59. package/dist/chunk-DD3KISNB.mjs.map +1 -0
  60. package/dist/chunk-DKKMWBP4.mjs +1 -0
  61. package/dist/chunk-DKKMWBP4.mjs.map +1 -0
  62. package/dist/chunk-DO2HLAZO.js +48 -0
  63. package/dist/chunk-DO2HLAZO.js.map +1 -0
  64. package/dist/chunk-DZXDB3K2.mjs +17 -0
  65. package/dist/chunk-DZXDB3K2.mjs.map +1 -0
  66. package/dist/chunk-ECDTZBYO.mjs +230 -0
  67. package/dist/chunk-ECDTZBYO.mjs.map +1 -0
  68. package/dist/chunk-EFJEWLRL.js +16 -0
  69. package/dist/chunk-EFJEWLRL.js.map +1 -0
  70. package/dist/chunk-FY4SXJGU.js +806 -0
  71. package/dist/chunk-FY4SXJGU.js.map +1 -0
  72. package/dist/chunk-GYWPEPOH.mjs +1354 -0
  73. package/dist/chunk-GYWPEPOH.mjs.map +1 -0
  74. package/dist/chunk-H6FMOA6B.js +1 -0
  75. package/dist/chunk-H6FMOA6B.js.map +1 -0
  76. package/dist/chunk-HR4H2FP7.mjs +89 -0
  77. package/dist/chunk-HR4H2FP7.mjs.map +1 -0
  78. package/dist/chunk-I2REI7OA.js +462 -0
  79. package/dist/chunk-I2REI7OA.js.map +1 -0
  80. package/dist/chunk-IBS6NI7D.js +77 -0
  81. package/dist/chunk-IBS6NI7D.js.map +1 -0
  82. package/dist/chunk-IWFGEPAA.mjs +4209 -0
  83. package/dist/chunk-IWFGEPAA.mjs.map +1 -0
  84. package/dist/chunk-J4Q36PMP.js +31 -0
  85. package/dist/chunk-J4Q36PMP.js.map +1 -0
  86. package/dist/chunk-JC3WJK65.js +1091 -0
  87. package/dist/chunk-JC3WJK65.js.map +1 -0
  88. package/dist/chunk-L6EQEAXU.mjs +462 -0
  89. package/dist/chunk-L6EQEAXU.mjs.map +1 -0
  90. package/dist/chunk-LXKSUWAV.js +15 -0
  91. package/dist/chunk-LXKSUWAV.js.map +1 -0
  92. package/dist/chunk-MFO27OHB.mjs +48 -0
  93. package/dist/chunk-MFO27OHB.mjs.map +1 -0
  94. package/dist/chunk-PAWJFY3S.mjs +7 -0
  95. package/dist/chunk-PAWJFY3S.mjs.map +1 -0
  96. package/dist/chunk-Q2N6SQYW.mjs +8 -0
  97. package/dist/chunk-Q2N6SQYW.mjs.map +1 -0
  98. package/dist/chunk-RAF7PNLG.js +131 -0
  99. package/dist/chunk-RAF7PNLG.js.map +1 -0
  100. package/dist/chunk-RUR22SVM.js +17 -0
  101. package/dist/chunk-RUR22SVM.js.map +1 -0
  102. package/dist/chunk-TEGF6ZWG.js +109 -0
  103. package/dist/chunk-TEGF6ZWG.js.map +1 -0
  104. package/dist/chunk-TMVHSY3Y.js +230 -0
  105. package/dist/chunk-TMVHSY3Y.js.map +1 -0
  106. package/dist/chunk-V2JJPI7N.js +1354 -0
  107. package/dist/chunk-V2JJPI7N.js.map +1 -0
  108. package/dist/chunk-WWWMJZEF.mjs +806 -0
  109. package/dist/chunk-WWWMJZEF.mjs.map +1 -0
  110. package/dist/chunk-X4BIHJ2B.mjs +55 -0
  111. package/dist/chunk-X4BIHJ2B.mjs.map +1 -0
  112. package/dist/chunk-YDVTFM7X.mjs +109 -0
  113. package/dist/chunk-YDVTFM7X.mjs.map +1 -0
  114. package/dist/chunk-YF5XQZDR.mjs +16 -0
  115. package/dist/chunk-YF5XQZDR.mjs.map +1 -0
  116. package/dist/client/index.d.mts +252 -0
  117. package/dist/client/index.d.ts +252 -0
  118. package/dist/client/index.js +275 -0
  119. package/dist/client/index.js.map +1 -0
  120. package/dist/client/index.mjs +274 -0
  121. package/dist/client/index.mjs.map +1 -0
  122. package/dist/components/index.d.mts +441 -0
  123. package/dist/components/index.d.ts +441 -0
  124. package/dist/components/index.js +2474 -0
  125. package/dist/components/index.js.map +1 -0
  126. package/dist/components/index.mjs +2474 -0
  127. package/dist/components/index.mjs.map +1 -0
  128. package/dist/config-hXufftVS.d.mts +34 -0
  129. package/dist/config-hXufftVS.d.ts +34 -0
  130. package/dist/content.interface-BhyAiOFq.d.ts +35 -0
  131. package/dist/content.interface-Dg2lt_An.d.mts +35 -0
  132. package/dist/contexts/index.d.mts +56 -0
  133. package/dist/contexts/index.d.ts +56 -0
  134. package/dist/contexts/index.js +21 -0
  135. package/dist/contexts/index.js.map +1 -0
  136. package/dist/contexts/index.mjs +21 -0
  137. package/dist/contexts/index.mjs.map +1 -0
  138. package/dist/core/index.d.mts +152 -0
  139. package/dist/core/index.d.ts +152 -0
  140. package/dist/core/index.js +47 -0
  141. package/dist/core/index.js.map +1 -0
  142. package/dist/core/index.mjs +47 -0
  143. package/dist/core/index.mjs.map +1 -0
  144. package/dist/d3.link.interface-QMdB22bC.d.mts +20 -0
  145. package/dist/d3.link.interface-QMdB22bC.d.ts +20 -0
  146. package/dist/features/index.d.mts +553 -0
  147. package/dist/features/index.d.ts +553 -0
  148. package/dist/features/index.js +94 -0
  149. package/dist/features/index.js.map +1 -0
  150. package/dist/features/index.mjs +94 -0
  151. package/dist/features/index.mjs.map +1 -0
  152. package/dist/hooks/index.d.mts +94 -0
  153. package/dist/hooks/index.d.ts +94 -0
  154. package/dist/hooks/index.js +43 -0
  155. package/dist/hooks/index.js.map +1 -0
  156. package/dist/hooks/index.mjs +43 -0
  157. package/dist/hooks/index.mjs.map +1 -0
  158. package/dist/index.d.mts +72 -0
  159. package/dist/index.d.ts +72 -0
  160. package/dist/index.js +84 -0
  161. package/dist/index.js.map +1 -0
  162. package/dist/index.mjs +84 -0
  163. package/dist/index.mjs.map +1 -0
  164. package/dist/interfaces/index.d.mts +3 -0
  165. package/dist/interfaces/index.d.ts +3 -0
  166. package/dist/interfaces/index.js +2 -0
  167. package/dist/interfaces/index.js.map +1 -0
  168. package/dist/interfaces/index.mjs +2 -0
  169. package/dist/interfaces/index.mjs.map +1 -0
  170. package/dist/permissions/index.d.mts +41 -0
  171. package/dist/permissions/index.d.ts +41 -0
  172. package/dist/permissions/index.js +14 -0
  173. package/dist/permissions/index.js.map +1 -0
  174. package/dist/permissions/index.mjs +14 -0
  175. package/dist/permissions/index.mjs.map +1 -0
  176. package/dist/request-7FE3LJLV.mjs +9 -0
  177. package/dist/request-7FE3LJLV.mjs.map +1 -0
  178. package/dist/request-FYMQK5CX.mjs +9 -0
  179. package/dist/request-FYMQK5CX.mjs.map +1 -0
  180. package/dist/request-QFS7NEIE.js +9 -0
  181. package/dist/request-QFS7NEIE.js.map +1 -0
  182. package/dist/request-ZYY6RI5X.js +9 -0
  183. package/dist/request-ZYY6RI5X.js.map +1 -0
  184. package/dist/roles/index.d.mts +33 -0
  185. package/dist/roles/index.d.ts +33 -0
  186. package/dist/roles/index.js +12 -0
  187. package/dist/roles/index.js.map +1 -0
  188. package/dist/roles/index.mjs +12 -0
  189. package/dist/roles/index.mjs.map +1 -0
  190. package/dist/server/index.d.mts +44 -0
  191. package/dist/server/index.d.ts +44 -0
  192. package/dist/server/index.js +29 -0
  193. package/dist/server/index.js.map +1 -0
  194. package/dist/server/index.mjs +29 -0
  195. package/dist/server/index.mjs.map +1 -0
  196. package/dist/shadcnui/index.d.mts +698 -0
  197. package/dist/shadcnui/index.d.ts +698 -0
  198. package/dist/shadcnui/index.js +466 -0
  199. package/dist/shadcnui/index.js.map +1 -0
  200. package/dist/shadcnui/index.mjs +465 -0
  201. package/dist/shadcnui/index.mjs.map +1 -0
  202. package/dist/token-IJSPOMW6.mjs +9 -0
  203. package/dist/token-IJSPOMW6.mjs.map +1 -0
  204. package/dist/token-MJMC26ON.js +9 -0
  205. package/dist/token-MJMC26ON.js.map +1 -0
  206. package/dist/token-UADJQ7VC.mjs +9 -0
  207. package/dist/token-UADJQ7VC.mjs.map +1 -0
  208. package/dist/token-UYE7CV6X.js +9 -0
  209. package/dist/token-UYE7CV6X.js.map +1 -0
  210. package/dist/types-DluCaP1I.d.ts +95 -0
  211. package/dist/types-lQVA8d_P.d.mts +95 -0
  212. package/dist/useDataListRetriever-futhx3OP.d.mts +32 -0
  213. package/dist/useDataListRetriever-futhx3OP.d.ts +32 -0
  214. package/dist/user.interface-CAsTIbuQ.d.mts +85 -0
  215. package/dist/user.interface-CbWqMaaU.d.ts +85 -0
  216. package/dist/utils/index.d.mts +201 -0
  217. package/dist/utils/index.d.ts +201 -0
  218. package/dist/utils/index.js +32 -0
  219. package/dist/utils/index.js.map +1 -0
  220. package/dist/utils/index.mjs +32 -0
  221. package/dist/utils/index.mjs.map +1 -0
  222. package/package.json +205 -0
package/README.md ADDED
@@ -0,0 +1,889 @@
1
+ # @carlonicora/nextjs-jsonapi
2
+
3
+ A comprehensive Next.js package providing JSON:API compliant client with unified server/client support, automatic caching, and a complete shadcn/ui component library.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Features](#features)
8
+ - [Architecture](#architecture)
9
+ - [Installation](#installation)
10
+ - [Quick Start](#quick-start)
11
+ - [Environment Variables](#environment-variables)
12
+ - [Entry Points](#entry-points)
13
+ - [Unified API](#unified-api)
14
+ - [Client Hooks](#client-hooks)
15
+ - [Server Requests](#server-requests)
16
+ - [Permissions](#permissions)
17
+ - [shadcn/ui Components](#shadcnui-components)
18
+ - [Tailwind CSS Configuration](#tailwind-css-configuration)
19
+ - [CSS Variables](#css-variables)
20
+ - [License](#license)
21
+
22
+ ## Features
23
+
24
+ - **Unified API**: Auto-detects environment (server/client) and uses the appropriate request method
25
+ - **JSON:API Compliance**: Full JSON:API specification support with deserialization and pagination
26
+ - **Next.js 16+ Caching**: Built-in support for `cacheLife()` and `cacheTag()` via cache profiles
27
+ - **React Hooks**: `useJsonApiGet` and `useJsonApiMutation` for client-side data fetching
28
+ - **Server Components**: Direct server-side data fetching with automatic token handling
29
+ - **Multi-Tenant Support**: Built-in company ID handling for B2B applications
30
+ - **File Uploads**: Seamless file upload support with multipart requests
31
+ - **shadcn/ui Components**: 44 pre-built UI components (41 standard + 3 custom)
32
+ - **Utility Functions**: `cn` class merger, mobile detection, and ref composition
33
+
34
+ ## Architecture
35
+
36
+ The library is organized into eight entry points:
37
+
38
+ ```
39
+ @carlonicora/nextjs-jsonapi
40
+ ├── (main) # Unified API (auto-detects environment)
41
+ ├── /core # Interfaces, factories, registries, and utilities
42
+ ├── /client # React hooks and client-side utilities
43
+ ├── /server # Server-side requests and caching
44
+ ├── /permissions # Permission checking utilities
45
+ ├── /features # Built-in feature modules (S3, etc.)
46
+ ├── /utils # Utility functions (cn, useIsMobile, etc.)
47
+ └── /shadcnui # 44 shadcn/ui components
48
+ ```
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ pnpm add @carlonicora/nextjs-jsonapi
54
+ ```
55
+
56
+ ### Git Submodule Setup (Alternative)
57
+
58
+ If you want to use the package as a git submodule (for development or before npm release):
59
+
60
+ **1. Add the submodule**
61
+ ```bash
62
+ cd /path/to/your-project
63
+ git submodule add https://github.com/carlonicora/nextjs-jsonapi packages/nextjs-jsonapi
64
+ ```
65
+
66
+ **2. Verify it worked**
67
+ ```bash
68
+ git submodule status
69
+ # Should show: <commit-sha> packages/nextjs-jsonapi (heads/master)
70
+ ```
71
+
72
+ **3. Commit the submodule**
73
+ ```bash
74
+ git add .gitmodules packages/nextjs-jsonapi
75
+ git commit -m "Add nextjs-jsonapi as submodule"
76
+ ```
77
+
78
+ **4. Update your `package.json`** (e.g., `apps/web/package.json`)
79
+ ```json
80
+ {
81
+ "dependencies": {
82
+ "@carlonicora/nextjs-jsonapi": "workspace:*"
83
+ }
84
+ }
85
+ ```
86
+
87
+ **5. Ensure `pnpm-workspace.yaml` includes packages**
88
+ ```yaml
89
+ packages:
90
+ - "apps/*"
91
+ - "packages/*"
92
+ ```
93
+
94
+ **6. Install and build**
95
+ ```bash
96
+ pnpm install
97
+ cd packages/nextjs-jsonapi && pnpm build && cd ../..
98
+ ```
99
+
100
+ **For CI/CD (GitHub Actions)**, add `submodules: recursive` to your checkout step:
101
+ ```yaml
102
+ - uses: actions/checkout@v4
103
+ with:
104
+ submodules: recursive
105
+ ```
106
+
107
+ **Cloning a project with submodules:**
108
+ ```bash
109
+ # When cloning fresh
110
+ git clone --recurse-submodules https://github.com/your/repo.git
111
+
112
+ # If already cloned
113
+ git submodule update --init --recursive
114
+ ```
115
+
116
+ ### Peer Dependencies
117
+
118
+ | Package | Version | Required | Purpose |
119
+ | ----------------- | --------- | -------- | -------------------------------- |
120
+ | `next` | >=14.0.0 | Yes | Next.js framework |
121
+ | `react` | >=18.0.0 | Yes | React library |
122
+ | `react-dom` | >=18.0.0 | Yes | React DOM |
123
+ | `react-hook-form` | >=7.0.0 | Optional | Form handling (for form components) |
124
+
125
+ ## Quick Start
126
+
127
+ ### 1. Configure the API Client
128
+
129
+ Configure the JSON:API client in your environment setup file:
130
+
131
+ ```typescript
132
+ // src/config/env.ts
133
+ import { configureJsonApi } from "@carlonicora/nextjs-jsonapi";
134
+ import { bootstrap } from "@/config/Bootstrapper";
135
+
136
+ configureJsonApi({
137
+ apiUrl: process.env.NEXT_PUBLIC_API_URL!,
138
+ bootstrapper: bootstrap,
139
+ });
140
+ ```
141
+
142
+ ### 2. Define Your Modules and Bootstrapper
143
+
144
+ Create a bootstrapper that registers all modules for both the ModuleRegistry (for `Modules.X` access) and DataClassRegistry (for JSON:API response translation):
145
+
146
+ ```typescript
147
+ // src/config/Bootstrapper.ts
148
+ import { DataClassRegistry, FieldSelector, ModuleRegistry } from "@carlonicora/nextjs-jsonapi/core";
149
+ import { ModuleWithPermissions } from "@carlonicora/nextjs-jsonapi/permissions";
150
+ import { S3Module } from "@carlonicora/nextjs-jsonapi/features";
151
+
152
+ // Import your module definitions
153
+ import { ArticleModule } from "@/features/article/ArticleModule";
154
+ import { UserModule } from "@/features/user/UserModule";
155
+ import { Article } from "@/features/article/data/Article";
156
+ import { User } from "@/features/user/data/User";
157
+
158
+ // Module factory helper
159
+ const moduleFactory = (params: {
160
+ pageUrl?: string;
161
+ name: string;
162
+ cache?: string;
163
+ model: any;
164
+ feature?: string;
165
+ moduleId?: string;
166
+ inclusions?: Record<string, { types?: string[]; fields?: FieldSelector<any>[] }>;
167
+ }): ModuleWithPermissions => ({
168
+ pageUrl: params.pageUrl,
169
+ name: params.name,
170
+ model: params.model,
171
+ feature: params.feature,
172
+ moduleId: params.moduleId,
173
+ cache: params.cache,
174
+ inclusions: params.inclusions ?? {},
175
+ });
176
+
177
+ // Example module definition file (e.g., ArticleModule.ts)
178
+ // export const ArticleModule = (factory: ModuleFactory) =>
179
+ // factory({ name: "articles", model: Article, pageUrl: "/articles" });
180
+
181
+ // Single source of truth for all modules
182
+ const allModules = {
183
+ Article: ArticleModule(moduleFactory),
184
+ User: UserModule(moduleFactory),
185
+ S3: S3Module(moduleFactory), // Built-in S3 module from library
186
+ } satisfies Record<string, ModuleWithPermissions>;
187
+
188
+ // Export type for TypeScript autocompletion
189
+ export type AllModuleDefinitions = typeof allModules;
190
+
191
+ let bootstrapped = false;
192
+
193
+ export function bootstrap(): void {
194
+ if (bootstrapped) return;
195
+
196
+ // Register modules for Modules.X access
197
+ Object.entries(allModules).forEach(([name, module]) => {
198
+ ModuleRegistry.register(name, module);
199
+ });
200
+
201
+ // Register model classes for JSON:API response translation
202
+ DataClassRegistry.bootstrap(allModules);
203
+
204
+ bootstrapped = true;
205
+ }
206
+ ```
207
+
208
+ ### 3. Fetch Data in Server Components
209
+
210
+ ```typescript
211
+ // src/app/articles/page.tsx
212
+ import { JsonApiGet } from "@carlonicora/nextjs-jsonapi";
213
+ import { Modules } from "@carlonicora/nextjs-jsonapi/core";
214
+
215
+ export default async function ArticlesPage() {
216
+ const response = await JsonApiGet({
217
+ classKey: Modules.Article,
218
+ endpoint: "/articles",
219
+ language: "en",
220
+ });
221
+
222
+ if (!response.ok) {
223
+ return <div>Error: {response.error}</div>;
224
+ }
225
+
226
+ return (
227
+ <ul>
228
+ {response.data.map((article) => (
229
+ <li key={article.id}>{article.title}</li>
230
+ ))}
231
+ </ul>
232
+ );
233
+ }
234
+ ```
235
+
236
+ ### 4. Use Hooks in Client Components
237
+
238
+ ```typescript
239
+ "use client";
240
+
241
+ import { useJsonApiGet, useJsonApiMutation } from "@carlonicora/nextjs-jsonapi/client";
242
+ import { Modules } from "@carlonicora/nextjs-jsonapi/core";
243
+
244
+ export function ArticleList() {
245
+ const { data, loading, error, refetch } = useJsonApiGet({
246
+ classKey: Modules.Article,
247
+ endpoint: "/articles",
248
+ });
249
+
250
+ const { mutate, loading: creating } = useJsonApiMutation({
251
+ method: "POST",
252
+ classKey: Modules.Article,
253
+ onSuccess: () => refetch(),
254
+ });
255
+
256
+ if (loading) return <div>Loading...</div>;
257
+ if (error) return <div>Error: {error}</div>;
258
+
259
+ return (
260
+ <div>
261
+ <button
262
+ onClick={() => mutate({
263
+ endpoint: "/articles",
264
+ body: { title: "New Article" }
265
+ })}
266
+ disabled={creating}
267
+ >
268
+ Create Article
269
+ </button>
270
+ <ul>
271
+ {data.map((article) => (
272
+ <li key={article.id}>{article.title}</li>
273
+ ))}
274
+ </ul>
275
+ </div>
276
+ );
277
+ }
278
+ ```
279
+
280
+ ## Environment Variables
281
+
282
+ ```env
283
+ # Required
284
+ NEXT_PUBLIC_API_URL=http://localhost:3000
285
+
286
+ # Optional - Token cookie name (default: "token")
287
+ # Set this if your API uses a different cookie name for JWT tokens
288
+ ```
289
+
290
+ ## Entry Points
291
+
292
+ ### Main Export (`.`)
293
+
294
+ The default export provides the unified API that auto-detects the environment:
295
+
296
+ ```typescript
297
+ import {
298
+ JsonApiGet,
299
+ JsonApiPost,
300
+ JsonApiPut,
301
+ JsonApiPatch,
302
+ JsonApiDelete,
303
+ configureJsonApi,
304
+ } from "@carlonicora/nextjs-jsonapi";
305
+ ```
306
+
307
+ ### Core (`/core`)
308
+
309
+ Core interfaces, factories, registries, and utilities:
310
+
311
+ ```typescript
312
+ import {
313
+ // Interfaces
314
+ ApiDataInterface,
315
+ ApiRequestDataTypeInterface,
316
+ ApiResponseInterface,
317
+
318
+ // Factories
319
+ JsonApiDataFactory,
320
+
321
+ // Registries
322
+ ModuleRegistry, // Register modules during bootstrap
323
+ DataClassRegistry, // Register model classes for JSON:API translation
324
+ Modules, // Access registered modules (e.g., Modules.Article)
325
+
326
+ // Endpoint builder
327
+ EndpointBuilder,
328
+
329
+ // Field selectors
330
+ FieldSelector,
331
+
332
+ // Utilities
333
+ translateResponse,
334
+ } from "@carlonicora/nextjs-jsonapi/core";
335
+ ```
336
+
337
+ ### Client (`/client`)
338
+
339
+ React hooks and client-side utilities (requires `"use client"`):
340
+
341
+ ```typescript
342
+ import {
343
+ // Hooks
344
+ useJsonApiGet,
345
+ useJsonApiMutation,
346
+ useRehydration,
347
+
348
+ // Context
349
+ JsonApiProvider,
350
+ useJsonApiContext,
351
+
352
+ // Request utilities
353
+ directFetch,
354
+ getClientToken,
355
+ } from "@carlonicora/nextjs-jsonapi/client";
356
+ ```
357
+
358
+ ### Server (`/server`)
359
+
360
+ Server-side request utilities:
361
+
362
+ ```typescript
363
+ import {
364
+ serverRequest,
365
+ getServerToken,
366
+ getCacheProfile,
367
+ } from "@carlonicora/nextjs-jsonapi/server";
368
+ ```
369
+
370
+ ### Permissions (`/permissions`)
371
+
372
+ Permission checking utilities:
373
+
374
+ ```typescript
375
+ import {
376
+ checkPermission,
377
+ type PermissionCheck,
378
+ type ModuleWithPermissions,
379
+ type ModuleFactory,
380
+ } from "@carlonicora/nextjs-jsonapi/permissions";
381
+ ```
382
+
383
+ ### Features (`/features`)
384
+
385
+ Built-in feature modules that can be used directly in your application:
386
+
387
+ ```typescript
388
+ import {
389
+ // S3 Module (for file uploads via pre-signed URLs)
390
+ S3Module, // Module definition factory
391
+ S3Service, // Service with getPreSignedUrl, getSignedUrl, deleteFile
392
+ S3, // Data class
393
+ type S3Interface, // Response interface
394
+ type S3Input, // Input parameters
395
+ } from "@carlonicora/nextjs-jsonapi/features";
396
+
397
+ // Usage example:
398
+ const s3Response = await S3Service.getPreSignedUrl({
399
+ key: "companies/123/documents/file.pdf",
400
+ contentType: "application/pdf",
401
+ isPublic: true,
402
+ });
403
+
404
+ await fetch(s3Response.url, {
405
+ method: "PUT",
406
+ headers: s3Response.headers,
407
+ body: file,
408
+ });
409
+ ```
410
+
411
+ ### Utils (`/utils`)
412
+
413
+ Utility functions:
414
+
415
+ ```typescript
416
+ import {
417
+ cn, // Class name merger (clsx + tailwind-merge)
418
+ composeRefs, // Compose multiple refs
419
+ useComposedRefs, // Hook for composing refs
420
+ useIsMobile, // Mobile detection hook
421
+ type ClassValue, // Type for cn function
422
+ } from "@carlonicora/nextjs-jsonapi/utils";
423
+ ```
424
+
425
+ ### shadcn/ui (`/shadcnui`)
426
+
427
+ All shadcn/ui components (requires `"use client"`):
428
+
429
+ ```typescript
430
+ import {
431
+ // UI Components (41)
432
+ Accordion, AccordionItem, AccordionTrigger, AccordionContent,
433
+ Alert, AlertTitle, AlertDescription,
434
+ AlertDialog, AlertDialogTrigger, AlertDialogContent, /* ... */
435
+ Avatar, AvatarImage, AvatarFallback,
436
+ Badge, badgeVariants,
437
+ Breadcrumb, BreadcrumbList, BreadcrumbItem, /* ... */
438
+ Button, buttonVariants,
439
+ Calendar,
440
+ Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter,
441
+ Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext,
442
+ ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, /* ... */
443
+ Checkbox,
444
+ Collapsible, CollapsibleTrigger, CollapsibleContent,
445
+ Command, CommandInput, CommandList, CommandItem, /* ... */
446
+ ContextMenu, ContextMenuTrigger, ContextMenuContent, /* ... */
447
+ Dialog, DialogTrigger, DialogContent, DialogHeader, /* ... */
448
+ Drawer, DrawerTrigger, DrawerContent, /* ... */
449
+ DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, /* ... */
450
+ Form, FormField, FormItem, FormLabel, FormControl, /* ... */
451
+ HoverCard, HoverCardTrigger, HoverCardContent,
452
+ Input,
453
+ Label,
454
+ NavigationMenu, NavigationMenuList, NavigationMenuItem, /* ... */
455
+ Popover, PopoverTrigger, PopoverContent,
456
+ Progress,
457
+ RadioGroup, RadioGroupItem,
458
+ ScrollArea, ScrollBar,
459
+ Select, SelectTrigger, SelectValue, SelectContent, SelectItem, /* ... */
460
+ Separator,
461
+ Sheet, SheetTrigger, SheetContent, SheetHeader, /* ... */
462
+ Sidebar, SidebarProvider, SidebarContent, SidebarMenu, /* ... */
463
+ Skeleton,
464
+ Slider,
465
+ Sonner, Toaster, toast,
466
+ Switch,
467
+ Table, TableHeader, TableBody, TableRow, TableHead, TableCell, /* ... */
468
+ Tabs, TabsList, TabsTrigger, TabsContent,
469
+ Textarea,
470
+ Toggle, toggleVariants,
471
+ Tooltip, TooltipTrigger, TooltipContent, TooltipProvider,
472
+
473
+ // Custom Components (3)
474
+ Kanban, KanbanColumn, KanbanItem, // Drag-and-drop Kanban board
475
+ Link, // next-intl compatible Link
476
+ MultiSelect, // Multi-select dropdown
477
+ } from "@carlonicora/nextjs-jsonapi/shadcnui";
478
+ ```
479
+
480
+ ## Unified API
481
+
482
+ The unified API automatically detects whether code is running on the server or client and uses the appropriate request method.
483
+
484
+ ### JsonApiGet
485
+
486
+ Fetch data from a JSON:API endpoint:
487
+
488
+ ```typescript
489
+ const response = await JsonApiGet({
490
+ classKey: Modules.Article, // Module definition
491
+ endpoint: "/articles/123", // API endpoint
492
+ companyId: "company-uuid", // Optional: for multi-tenant apps
493
+ language: "en", // Required: for i18n
494
+ });
495
+
496
+ if (response.ok) {
497
+ console.log(response.data); // Deserialized data
498
+ console.log(response.pagination); // Pagination info
499
+
500
+ // Navigate pages
501
+ if (response.next) {
502
+ const nextPage = await response.nextPage();
503
+ }
504
+ }
505
+ ```
506
+
507
+ ### JsonApiPost
508
+
509
+ Create a new resource:
510
+
511
+ ```typescript
512
+ const response = await JsonApiPost({
513
+ classKey: Modules.Article,
514
+ endpoint: "/articles",
515
+ body: { title: "New Article", content: "..." },
516
+ language: "en",
517
+
518
+ // Optional
519
+ files: { attachment: file }, // File uploads
520
+ overridesJsonApiCreation: false, // Use raw body instead of JSON:API format
521
+ responseType: Modules.OtherType, // If response type differs
522
+ });
523
+ ```
524
+
525
+ ### JsonApiPut / JsonApiPatch
526
+
527
+ Update a resource:
528
+
529
+ ```typescript
530
+ const response = await JsonApiPut({
531
+ classKey: Modules.Article,
532
+ endpoint: "/articles/123",
533
+ body: { title: "Updated Title" },
534
+ language: "en",
535
+ });
536
+ ```
537
+
538
+ ### JsonApiDelete
539
+
540
+ Delete a resource:
541
+
542
+ ```typescript
543
+ const response = await JsonApiDelete({
544
+ classKey: Modules.Article,
545
+ endpoint: "/articles/123",
546
+ language: "en",
547
+ });
548
+ ```
549
+
550
+ ## Client Hooks
551
+
552
+ ### useJsonApiGet
553
+
554
+ Hook for fetching data with automatic refetching:
555
+
556
+ ```typescript
557
+ const {
558
+ data, // Fetched data or null
559
+ loading, // Loading state
560
+ error, // Error message or null
561
+ response, // Full API response
562
+ refetch, // Manual refetch function
563
+ hasNextPage, // Pagination: has next page
564
+ hasPreviousPage, // Pagination: has previous page
565
+ fetchNextPage, // Fetch next page
566
+ fetchPreviousPage, // Fetch previous page
567
+ } = useJsonApiGet<Article>({
568
+ classKey: Modules.Article,
569
+ endpoint: `/articles/${id}`,
570
+ companyId: companyId,
571
+ options: {
572
+ enabled: !!id, // Conditionally enable
573
+ deps: [someDependency], // Refetch when these change
574
+ },
575
+ });
576
+ ```
577
+
578
+ ### useJsonApiMutation
579
+
580
+ Hook for mutations (POST, PUT, PATCH, DELETE):
581
+
582
+ ```typescript
583
+ const {
584
+ data, // Result data or null
585
+ loading, // Loading state
586
+ error, // Error message or null
587
+ response, // Full API response
588
+ mutate, // Execute the mutation
589
+ reset, // Reset state
590
+ } = useJsonApiMutation<Article>({
591
+ method: "POST",
592
+ classKey: Modules.Article,
593
+ onSuccess: (data) => console.log("Created:", data),
594
+ onError: (error) => console.error("Failed:", error),
595
+ });
596
+
597
+ // Execute mutation
598
+ const result = await mutate({
599
+ endpoint: "/articles",
600
+ body: { title: "New Article" },
601
+ files: { image: imageFile },
602
+ companyId: "company-uuid",
603
+ });
604
+ ```
605
+
606
+ ## Server Requests
607
+
608
+ For server components or API routes, use the server module directly:
609
+
610
+ ```typescript
611
+ import { serverRequest, getServerToken, getCacheProfile } from "@carlonicora/nextjs-jsonapi/server";
612
+
613
+ export async function getArticle(id: string) {
614
+ const token = await getServerToken();
615
+
616
+ const data = await serverRequest({
617
+ method: "GET",
618
+ url: `${process.env.NEXT_PUBLIC_API_URL}/articles/${id}`,
619
+ token,
620
+ cache: getCacheProfile("articles"), // Get cache settings
621
+ language: "en",
622
+ });
623
+
624
+ return data;
625
+ }
626
+ ```
627
+
628
+ ### Cache Profiles
629
+
630
+ The library supports Next.js 16+ caching via `cacheLife()` and `cacheTag()`:
631
+
632
+ ```typescript
633
+ // In your module definition
634
+ export const Modules = {
635
+ Article: {
636
+ type: "articles",
637
+ cache: "articles", // Profile name for caching
638
+ factory: (data: any) => data,
639
+ },
640
+ };
641
+
642
+ // The cache profile is automatically applied when using JsonApiGet
643
+ // on the server side
644
+ ```
645
+
646
+ ## Permissions
647
+
648
+ Check user permissions for protected resources:
649
+
650
+ ```typescript
651
+ import { checkPermission } from "@carlonicora/nextjs-jsonapi/permissions";
652
+
653
+ // Check if user has permission
654
+ const canEdit = checkPermission({
655
+ user: currentUser,
656
+ action: "edit",
657
+ resource: "articles",
658
+ resourceId: article.id,
659
+ });
660
+
661
+ if (!canEdit) {
662
+ return <div>Access denied</div>;
663
+ }
664
+ ```
665
+
666
+ ## shadcn/ui Components
667
+
668
+ The package includes 44 pre-built shadcn/ui components:
669
+
670
+ ### Standard UI Components (41)
671
+
672
+ | Component | Description |
673
+ |-----------|-------------|
674
+ | `Accordion` | Collapsible content sections |
675
+ | `Alert` | Callout for important messages |
676
+ | `AlertDialog` | Modal dialog for confirmations |
677
+ | `Avatar` | User profile images |
678
+ | `Badge` | Status indicators and labels |
679
+ | `Breadcrumb` | Navigation breadcrumbs |
680
+ | `Button` | Click actions with variants |
681
+ | `Calendar` | Date picker calendar |
682
+ | `Card` | Content container |
683
+ | `Carousel` | Sliding content panels |
684
+ | `Chart` | Data visualization (Recharts) |
685
+ | `Checkbox` | Toggle options |
686
+ | `Collapsible` | Expandable sections |
687
+ | `Command` | Command palette (cmdk) |
688
+ | `ContextMenu` | Right-click menus |
689
+ | `Dialog` | Modal windows |
690
+ | `Drawer` | Sliding side panels (Vaul) |
691
+ | `DropdownMenu` | Dropdown menus |
692
+ | `Form` | Form handling (react-hook-form) |
693
+ | `HoverCard` | Hover-triggered cards |
694
+ | `Input` | Text input fields |
695
+ | `Label` | Form labels |
696
+ | `NavigationMenu` | Navigation menus |
697
+ | `Popover` | Floating content |
698
+ | `Progress` | Progress indicators |
699
+ | `RadioGroup` | Radio button groups |
700
+ | `ScrollArea` | Custom scrollbars |
701
+ | `Select` | Dropdown selects |
702
+ | `Separator` | Visual dividers |
703
+ | `Sheet` | Side panels |
704
+ | `Sidebar` | Application sidebars |
705
+ | `Skeleton` | Loading placeholders |
706
+ | `Slider` | Range sliders |
707
+ | `Sonner` | Toast notifications |
708
+ | `Switch` | Toggle switches |
709
+ | `Table` | Data tables |
710
+ | `Tabs` | Tabbed interfaces |
711
+ | `Textarea` | Multi-line text input |
712
+ | `Toggle` | Toggle buttons |
713
+ | `Tooltip` | Hover tooltips |
714
+
715
+ ### Custom Components (3)
716
+
717
+ | Component | Description |
718
+ |-----------|-------------|
719
+ | `Kanban` | Drag-and-drop Kanban board (dnd-kit) |
720
+ | `Link` | next-intl compatible link wrapper |
721
+ | `MultiSelect` | Multi-select dropdown with badges |
722
+
723
+ ### Usage Example
724
+
725
+ ```typescript
726
+ "use client";
727
+
728
+ import {
729
+ Button,
730
+ Card,
731
+ CardHeader,
732
+ CardTitle,
733
+ CardContent,
734
+ Dialog,
735
+ DialogTrigger,
736
+ DialogContent,
737
+ DialogHeader,
738
+ DialogTitle,
739
+ } from "@carlonicora/nextjs-jsonapi/shadcnui";
740
+ import { cn } from "@carlonicora/nextjs-jsonapi/utils";
741
+
742
+ export function ArticleCard({ article, className }) {
743
+ return (
744
+ <Card className={cn("hover:shadow-lg transition-shadow", className)}>
745
+ <CardHeader>
746
+ <CardTitle>{article.title}</CardTitle>
747
+ </CardHeader>
748
+ <CardContent>
749
+ <p>{article.excerpt}</p>
750
+
751
+ <Dialog>
752
+ <DialogTrigger asChild>
753
+ <Button variant="outline">Read More</Button>
754
+ </DialogTrigger>
755
+ <DialogContent>
756
+ <DialogHeader>
757
+ <DialogTitle>{article.title}</DialogTitle>
758
+ </DialogHeader>
759
+ <p>{article.content}</p>
760
+ </DialogContent>
761
+ </Dialog>
762
+ </CardContent>
763
+ </Card>
764
+ );
765
+ }
766
+ ```
767
+
768
+ ## Tailwind CSS Configuration
769
+
770
+ **Important for Tailwind v4**: You must add the `@source` directive to your `globals.css` to ensure Tailwind scans the package's component files:
771
+
772
+ ```css
773
+ /* apps/web/src/app/globals.css */
774
+ @import "tailwindcss";
775
+ @import "tw-animate-css";
776
+
777
+ /* Include package source files for Tailwind to scan */
778
+ @source "../../../../packages/nextjs-jsonapi/src/**/*.{ts,tsx}";
779
+
780
+ @custom-variant dark (&:is(.dark *));
781
+
782
+ @theme inline {
783
+ --color-background: var(--background);
784
+ --color-foreground: var(--foreground);
785
+ /* ... other theme variables */
786
+ }
787
+ ```
788
+
789
+ The `@source` path should be relative from your `globals.css` to the package's `src` directory.
790
+
791
+ ## CSS Variables
792
+
793
+ The shadcn/ui components require CSS variables to be defined in your application. Add these to your `globals.css`:
794
+
795
+ ```css
796
+ :root {
797
+ /* Background & Foreground */
798
+ --background: oklch(1 0 0);
799
+ --foreground: oklch(0.145 0 0);
800
+
801
+ /* Primary */
802
+ --primary: oklch(0.205 0 0);
803
+ --primary-foreground: oklch(0.985 0 0);
804
+
805
+ /* Secondary */
806
+ --secondary: oklch(0.97 0 0);
807
+ --secondary-foreground: oklch(0.205 0 0);
808
+
809
+ /* Muted */
810
+ --muted: oklch(0.97 0 0);
811
+ --muted-foreground: oklch(0.556 0 0);
812
+
813
+ /* Accent */
814
+ --accent: oklch(0.97 0 0);
815
+ --accent-foreground: oklch(0.205 0 0);
816
+
817
+ /* Destructive */
818
+ --destructive: oklch(0.577 0.245 27.325);
819
+ --destructive-foreground: oklch(0.985 0 0);
820
+
821
+ /* Border & Input */
822
+ --border: oklch(0.922 0 0);
823
+ --input: oklch(0.922 0 0);
824
+ --ring: oklch(0.708 0 0);
825
+
826
+ /* Card & Popover */
827
+ --card: oklch(1 0 0);
828
+ --card-foreground: oklch(0.145 0 0);
829
+ --popover: oklch(1 0 0);
830
+ --popover-foreground: oklch(0.145 0 0);
831
+
832
+ /* Charts */
833
+ --chart-1: oklch(0.646 0.222 41.116);
834
+ --chart-2: oklch(0.6 0.118 184.704);
835
+ --chart-3: oklch(0.398 0.07 227.392);
836
+ --chart-4: oklch(0.828 0.189 84.429);
837
+ --chart-5: oklch(0.769 0.188 70.08);
838
+
839
+ /* Sidebar */
840
+ --sidebar: oklch(0.985 0 0);
841
+ --sidebar-foreground: oklch(0.145 0 0);
842
+ --sidebar-primary: oklch(0.205 0 0);
843
+ --sidebar-primary-foreground: oklch(0.985 0 0);
844
+ --sidebar-accent: oklch(0.97 0 0);
845
+ --sidebar-accent-foreground: oklch(0.205 0 0);
846
+ --sidebar-border: oklch(0.922 0 0);
847
+ --sidebar-ring: oklch(0.708 0 0);
848
+
849
+ /* Warning */
850
+ --warning: oklch(0.84 0.16 84);
851
+ --warning-foreground: oklch(0.28 0.07 46);
852
+
853
+ /* Radius */
854
+ --radius: 0.625rem;
855
+ }
856
+
857
+ .dark {
858
+ --background: oklch(0.145 0 0);
859
+ --foreground: oklch(0.985 0 0);
860
+ --card: oklch(0.205 0 0);
861
+ --card-foreground: oklch(0.985 0 0);
862
+ --popover: oklch(0.269 0 0);
863
+ --popover-foreground: oklch(0.985 0 0);
864
+ --primary: oklch(0.922 0 0);
865
+ --primary-foreground: oklch(0.205 0 0);
866
+ --secondary: oklch(0.269 0 0);
867
+ --secondary-foreground: oklch(0.985 0 0);
868
+ --muted: oklch(0.269 0 0);
869
+ --muted-foreground: oklch(0.708 0 0);
870
+ --accent: oklch(0.269 0 0);
871
+ --accent-foreground: oklch(0.985 0 0);
872
+ --destructive: oklch(0.704 0.191 22.216);
873
+ --destructive-foreground: oklch(0.985 0 0);
874
+ --border: oklch(1 0 0 / 10%);
875
+ --input: oklch(1 0 0 / 15%);
876
+ --ring: oklch(0.556 0 0);
877
+ /* ... other dark mode values */
878
+ }
879
+ ```
880
+
881
+ ## License
882
+
883
+ This project is licensed under GPL v3 for open source use.
884
+
885
+ For commercial/closed-source licensing, contact: [@carlonicora](https://github.com/carlonicora)
886
+
887
+ ## Author
888
+
889
+ Carlo Nicora - [@carlonicora](https://github.com/carlonicora)