@@ -10,6 +10,33 @@ export interface FrappeResponse<T = unknown> {
1010 _server_messages ?: string ;
1111}
1212
13+ // Cache CSRF token per request context
14+ const csrfTokenCache = new WeakMap < APIRequestContext , string > ( ) ;
15+
16+ /**
17+ * Get CSRF token from Frappe.
18+ * Makes a request to get the token from cookies/response.
19+ */
20+ async function getCsrfToken ( request : APIRequestContext ) : Promise < string > {
21+ // Check cache first
22+ const cached = csrfTokenCache . get ( request ) ;
23+ if ( cached ) {
24+ return cached ;
25+ }
26+
27+ // Get CSRF token from Frappe's session info endpoint
28+ const response = await request . get ( '/api/method/frappe.auth.get_csrf_token' ) ;
29+
30+ if ( response . ok ( ) ) {
31+ const data = await response . json ( ) ;
32+ const token = data . message || '' ;
33+ csrfTokenCache . set ( request , token ) ;
34+ return token ;
35+ }
36+
37+ return '' ;
38+ }
39+
1340/**
1441 * Create a new document via Frappe REST API.
1542 */
@@ -18,8 +45,14 @@ export async function createDoc<T = Record<string, unknown>>(
1845 doctype : string ,
1946 doc : Record < string , unknown >
2047) : Promise < T > {
48+ const csrfToken = await getCsrfToken ( request ) ;
49+
2150 const response = await request . post ( `/api/resource/${ doctype } ` , {
2251 data : doc ,
52+ headers : {
53+ 'Content-Type' : 'application/json' ,
54+ ...( csrfToken ? { 'X-Frappe-CSRF-Token' : csrfToken } : { } ) ,
55+ } ,
2356 } ) ;
2457
2558 if ( ! response . ok ( ) ) {
@@ -61,9 +94,17 @@ export async function updateDoc<T = Record<string, unknown>>(
6194 name : string ,
6295 updates : Record < string , unknown >
6396) : Promise < T > {
97+ const csrfToken = await getCsrfToken ( request ) ;
98+
6499 const response = await request . put (
65100 `/api/resource/${ doctype } /${ encodeURIComponent ( name ) } ` ,
66- { data : updates }
101+ {
102+ data : updates ,
103+ headers : {
104+ 'Content-Type' : 'application/json' ,
105+ ...( csrfToken ? { 'X-Frappe-CSRF-Token' : csrfToken } : { } ) ,
106+ } ,
107+ }
67108 ) ;
68109
69110 if ( ! response . ok ( ) ) {
@@ -83,8 +124,15 @@ export async function deleteDoc(
83124 doctype : string ,
84125 name : string
85126) : Promise < void > {
127+ const csrfToken = await getCsrfToken ( request ) ;
128+
86129 const response = await request . delete (
87- `/api/resource/${ doctype } /${ encodeURIComponent ( name ) } `
130+ `/api/resource/${ doctype } /${ encodeURIComponent ( name ) } ` ,
131+ {
132+ headers : {
133+ ...( csrfToken ? { 'X-Frappe-CSRF-Token' : csrfToken } : { } ) ,
134+ } ,
135+ }
88136 ) ;
89137
90138 if ( ! response . ok ( ) ) {
@@ -101,8 +149,14 @@ export async function callMethod<T = unknown>(
101149 method : string ,
102150 args : Record < string , unknown > = { }
103151) : Promise < T > {
152+ const csrfToken = await getCsrfToken ( request ) ;
153+
104154 const response = await request . post ( `/api/method/${ method } ` , {
105155 data : args ,
156+ headers : {
157+ 'Content-Type' : 'application/json' ,
158+ ...( csrfToken ? { 'X-Frappe-CSRF-Token' : csrfToken } : { } ) ,
159+ } ,
106160 } ) ;
107161
108162 if ( ! response . ok ( ) ) {
0 commit comments