frappe/client — data-fetching v3#610
Open
netchampfaris wants to merge 22 commits intomainfrom
Open
Conversation
- useFrappeFetch: reactive HTTP client with smart params handling - defineDoctype: type-safe DocType definitions with getDoc method - Comprehensive test suite (19 tests) with MSW mocking - PRD documenting complete API design
- Add getList method with pagination, filtering, sorting - Support reactive options (MaybeRefOrGetter) with auto-refetch - Add debouncing and request cancellation (AbortController) - Simplify defineDoctype API (remove currying) - Rename result.data to result.json - Add 13 tests for defineDoctype (28 total passing)
- Implement setValue/delete/insert with optimistic updates API - Refactor useFrappeFetch with reactive options and callbacks - Add comprehensive test coverage
- update PRD - log unhandled exception on automatic fetches
- for holding custom options like baseUrl, realtime, onError, etc - returns defineDoctype with custom options applied
- DoctypeDefinition type combines doctype string with DoctypeOptions - defineDoctype() now takes a single definition object instead of (doctype, options) — cleaner API aligned with the new spec - Remove frappe/index.d.ts (moved to index.d.ts.bak) - Update all tests and docs to match the new signature
Implements the core store layer as specified in frappe/client/spec/: - DocStore: in-memory doc store with merge semantics, synchronous subscriptions (per-doc and per-doctype), and cold-start hydration - RequestManager: HTTP layer with GET deduplication, CSRF header, and onRequest/onError/onResponse hooks - FrappeResponseError: structured error class with isNotFound, isPermission, isAuth, isValidation convenience getters - CacheAdapter: write-behind persistence interface + memory impl - SocketManager: bridges Socket.IO doc_update/doc_rename to DocStore - Operation<TParams, TResult>: shared call/callOptimistic contract - CoreDocHandle: single-doc handle with setValue, delete, doc methods, and automatic snapshot revert on optimistic error - CoreListHandle: list handle with DocStore-backed data, pagination, insert/setValue/delete with callOptimistic support - createCoreClient: factory wiring everything together All components have zero Vue dependencies. 62 tests pass. Old WIP files (defineDoctype.ts, getDoc.ts, etc.) are untouched.
- ReactiveOperation: wraps core Operation with loading/error/data refs - vueDocHandle: Vue-reactive single-doc handle (shallowRef, watchEffect, nextTick batching, tryOnScopeDispose, reactive() unwrapping for templates) - vueListHandle: Vue-reactive list handle with per-key and whole-object filter support, auto-optimistic defaults for setValue/delete, InsertOperation with tempDoc placeholder and InsertPosition (start/end/index) - createVueClient: defineDoctype() factory wiring store + requestManager - Operation.callOptimistic updater made optional (auto-defaults in handles) - package.json: add ./frappe/vue export - 42 tests across 3 test files, 190 total passing
…scription
- Add onDocUpdate(doctype, cb) and onDocRename(doctype, cb) to SocketManager interface
- Implement in createSocketManager, createLazySocketManager, createNoopSocketManager
- Use list_update event (correct Frappe event name) instead of doc_update
- Payload shape: { doctype, name, user } — dispatch as list_update:${doctype}
- Reference-counted doctype_subscribe/doctype_unsubscribe for server room membership
- Fix doc_rename payload keys: old/new instead of old_name/doc
- Add fetchDoc(doctype, name) to RequestManager interface and implementation - fetchDoc encodes the name and delegates to fetch() - Add _suppressGlobalError flag to FrappeResponseError - Defer global onError via setTimeout(0) so local handlers can suppress it - Update RequestManager test to use fake timers for the deferred error assertion
- vueListHandle auto-subscribes via socket.onDocUpdate when socket is provided - On list_update: if name is in current page, fetchDoc individually (no flicker) - If name is unknown, reload the page (may be a new insert) - Add ReactiveOperationOptions.onError to suppress global error handler per-op - Use requestManager.fetchDoc() at the call site
…Rename - frappe-socket.ts: initFrappeSocket() connects to Frappe's Socket.IO using site_name and __FRAPPE_SOCKETIO_PORT__ injected by the Vite plugin - createVueClient: switch to createLazySocketManager with initFrappeSocket - Add newDoc(), getCount(), bulkDelete, bulkUpdate to VueDoctypeInstance - Add onUpdate(cb)/onRename(cb) convenience methods on the doctype instance - New files: vueNewDocHandle.ts, vueCountHandle.ts, uploadFile.ts - Export new handles and types from vue/index.ts
…tests - frappeProxy: inject __FRAPPE_SOCKETIO_PORT__ from common_site_config.json - frappe/index.js: export initFrappeSocket - core/__tests__/mocks.ts: add bulk_delete, bulk_update, and count MSW handlers - vue/__tests__/bulkAndRealtime.test.ts: tests for bulkDelete, bulkUpdate, realtime list_update and doc_rename - vue/__tests__/vueNewDocAndCount.test.ts: tests for newDoc and getCount
- Add createIDBCacheAdapter() using idb-keyval with per-app custom stores - Add createDefaultCacheAdapter() that picks IDB in browsers, falls back to memory - Add DocStore.subscribeAll() for write-behind cache updates - Wire connectCache() to subscribe before hydration to avoid missed writes - Expose cache factories from frappe/client/vue entry point - Accept optional cache option in createClient()
- Rewrite api.md, core.md, vue.md to reflect actual API surface - Remove wip implementation files and tests from frappe/client/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
frappe/client — data-fetching v3
A typed, reactive data-fetching client for Frappe Framework backends. Built on Vue 3 Composition API with a shared
DocStoreat the core.Setup
createClientoptionsbaseUrl?string''.realtime?booleanlist_update/doc_renameevents via Socket.IO.cache?CacheAdapteronRequest?fnonResponse?fnonError?(error: FrappeResponseError) => voidDefining a DocType
getDoc— single documentnamecan be aref,computed, or getter — changing it automatically fetches the new document.Options
enabled?MaybeRefOrGetter<boolean>false.transform?(doc: T) => TonSuccess?(doc: T) => voidonError?(err) => voidonErrorhandler.getList— document listFilters are fully reactive — any
ref,computed, or getter value is watched and triggers a debounced refetch.Options
fields?string[]filters?ReactiveFilters | MaybeRefOrGetter<object>undefinedvalues are omitted.orderBy?string'modified desc'.limit?number20.start?number0.debounce?numberenabled?MaybeRefOrGetter<boolean>false.transform?(doc: T) => TonSuccess?(docs: T[]) => voidonError?(err) => voidonErrorhandler.newDoc— draft documentLocal-only reactive document. Nothing is sent to the server until
insert.call().getCount— reactive countController methods
Methods defined in
controllerMethodsare exposed directly on the instance:Realtime
Subscribe to live events for a doctype:
Both return an unsubscribe function.
Bulk operations
Optimistic updates
All mutations expose
.callOptimistic()alongside.call(). The store is updated immediately and rolled back automatically if the request fails.getDocmutationsgetListmutationsFile upload
Persistent cache
Pass a
cacheadapter tocreateClient()to persist theDocStoreto IndexedDB. On the next page load, docs are hydrated instantly before any network requests fire — no loading flash.createDefaultCacheAdapteruses IndexedDB in browsers and falls back to in-memory in non-browser environments. Each app should pass a uniquedbNameto avoid collisions.