Frappe React SDK

repository·main·Indexed 41 days ago

https://github.com/frappe/frappe-react-sdk

A React hooks library for interfacing with a Frappe Framework backend. Version 1.17.1 provides high-level hooks for authentication (useFrappeAuth), database operations (useFrappeGetDoc, useFrappeGetDocList), and API calls (useFrappeGetCall, useFrappePostCall, useFrappePutCall, useFrappeDeleteCall), leveraging SWR for reactive data fetching.

Tokens
16.5K
Snippets
44
Records
48
Agent score
46%

What's inside Frappe React SDK

  1. Overview of Frappe React SDK

    main

    Frappe React SDK is a collection of React hooks designed to build React applications with a Frappe Framework backend. It encapsulates complex data-fetching logic—including loading states, revalidation, progress indicators, and debouncing—into simple hooks.

    Under the hood, the library uses frappe-js-sdk for API communication and SWR for data fetching and caching strategies.

  2. How SWR integration works in Frappe React SDK

    main

    The library leverages SWR for its data fetching strategy, utilizing cache invalidation and background updates to ensure the UI remains fast and reactive.

    While the hooks use the default SWR configuration, you can pass your own custom SWR configuration options to any hook in the library to control behavior like revalidation frequency or cache settings. For a full list of available options, refer to the SWR API Options.

  3. Core features of Frappe React SDK

    main

    The SDK provides specialized hooks for the following core functionalities:

    • Authentication: Cookie-based login (username/password) and token-based authentication, including logout and user state management.
    • Database Operations: Hooks to fetch single documents, fetch lists of documents, get counts, and perform CRUD (Create, Update, Delete) operations.
    • File Upload: A hook to upload files to the Frappe filesystem that tracks loading, progress, and error states.
    • API Calls: Hooks to invoke whitelisted backend functions while maintaining request state.
    • Search: A debounced search hook for querying documents in your database.
  4. Run your SPA in local development mode

    main

    To develop your SPA locally, you need to run both the Frappe bench and the SPA's development server.

    1. Start Frappe: Run bench start in your bench directory.
    2. Start SPA: Navigate to your SPA directory (e.g., cd dashboard) and run yarn dev.

    By default, the SPA development server runs on port 8080. You can access it at <site>:8080.

    # In bench directory
    bench start
    
    # In SPA directory (e.g., dashboard)
    yarn dev
  5. Install Doppio for SPA management

    main

    Doppio is a Frappe app (CLI) used to setup and manage Single Page Applications (React or Vue 3) on custom Frappe apps. Installing it enables custom bench commands to automate the attachment of an SPA to your Frappe application.

    Run this command in your bench directory to install:

    bench get-app https://github.com/NagariaHussain/doppio
  6. Set up a React or Vue SPA with Doppio

    main

    You can scaffold a new Single Page Application (SPA) using the bench add-spa command. This command automates the creation of the application directory, framework scaffolding, and Frappe integration.

    Command Options

    • <app-name>: The name of your existing Frappe app where the SPA will reside.
    • --tailwindcss: Optionally installs and configures TailwindCSS.
    • --typescript: Optionally configures the project to use TypeScript.

    Interactive Mode

    If you run bench add-spa without arguments, you will be prompted for:

    1. SPA Name: This becomes the directory name inside your app and the URI path (e.g., dashboard results in /dashboard).
    2. Framework: Select either React or Vue.
    3. Language: Select Typescript or Javascript.

    React Scaffolding Details

    When selecting React, Doppio performs the following:

    1. Scaffolds a React starter application using Vite.
    2. Installs and configures frappe-react-sdk for backend connectivity.
    3. Configures Vite proxy options (check proxyOptions.js) to proxy requests to your Frappe bench server during development.
    4. Optionally sets up TailwindCSS.
    5. Updates the website_route_rules hook in your app's hooks.py to handle SPA routing.
    bench add-spa --app <app-name> [--tailwindcss] [--typescript]
    
    # Or use the interactive prompt
    bench add-spa
  7. Set up local development for the Nextra Docs Template

    main

    To run the documentation template locally, install the dependencies using pnpm and then start the development server.

    1. Install dependencies: pnpm i
    2. Start development server: pnpm dev
    3. Access the site at http://localhost:3000
    pnpm i
    pnpm dev
  8. Initialise the library with FrappeProvider

    main

    To use the library, wrap your application with the FrappeProvider. If your Frappe server is hosted on a different URL than your web app, you must provide the url parameter.

    By default, the library uses cookie-based authentication. If you need to use token-based authentication (such as OAuth bearer tokens or API key/secret pairs), configure the tokenParams option.

    import { FrappeProvider } from "frappe-react-sdk";
    
    function App() {
        return (
        <FrappeProvider url='https://my-frappe-server.frappe.cloud'>
        {/** Your other app components **/}
        </FrappeProvider>
      )
    }
  9. Configure token-based authentication in FrappeProvider

    main

    To use token-based authentication, pass a tokenParams object to the FrappeProvider. This allows you to provide a custom token retrieval function and specify the token type.

    tokenParams options:

    • useToken: Boolean indicating if token authentication should be used.
    • token: A function that returns the token as a string (e.g., from LocalStorage or an external auth provider).
    • type: The type of token to use, either "Bearer" or "token".
    import { FrappeProvider } from 'frappe-react-sdk';
    
    function App() {
      return (
        <FrappeProvider 
          url='https://my-frappe-server.frappe.cloud' 
          tokenParams={
            useToken: true,
            // Pass a custom function that returns the token as a string
            token: getTokenFromLocalStorage(),
            // This can be "Bearer" or "token"
            type: "Bearer"
          } 
        >
          {/** Your other app components **/}
        </FrappeProvider>
      );
    }
  10. Initialize the library with FrappeProvider

    main

    To use the SDK, you must wrap your application (or a part of it) in the FrappeProvider. This component initializes the connection to your Frappe server and provides the necessary context for all other hooks.

    Key configuration options:

    • url: The URL of your Frappe server (required if different from the current window location).
    • tokenParams: Configuration for token-based authentication.
    • siteName: Required for Socket connections in Frappe v15+.
    • enableSocket: Boolean to enable/disable socket connections (defaults to true).
    • swrConfig: Global SWR configuration options.
    import { FrappeProvider } from 'frappe-react-sdk';
    
    function App() {
      return (
        <FrappeProvider 
          url="https://your-frappe-site.com"
          siteName="your-site-name"
        >
          <YourComponents />
        </FrappeProvider>
      );
    }
  11. Create a new document with an attachment

    main

    To create a document and attach a file to it, follow this pattern:

    1. Use createDoc to create the base document.
    2. Use the returned document's name (doc.name) to call upload from useFrappeFileUpload.
    3. Use updateDoc to save the resulting file_url back into the document's specific field.
    export const CreateUser = () => {
      const { upload, error, loading } = useFrappeFileUpload();
      const { createDoc } = useFrappeCreateDoc();
      const { updateDoc } = useFrappeUpdateDoc();
    
      const [email, setEmail] = useState<string>("");
      const [firstName, setFirstName] = useState<string>("");
      const [file, setFile] = useState<File>();
    
      const createUser = () => {
        if (file) {
          createDoc("User", {
            email: email,
            first_name: firstName,
          })
            .then((doc) => {
              return upload(file, {
                isPrivate: true,
                doctype: "User",
                docname: doc.name,
                fieldname: "user_image",
              });
            })
            .then((fileResponse) => {
              return updateDoc("User", doc.name, {
                user_image: fileResponse.file_url,
              });
            })
            .then(() => {
              alert("User created");
            });
        }
      };
    
      return (
        <div>
          {loading && <p>Uploading...</p>}
          {error && <p>{error}</p>}
          <input type="text" onChange={(e) => setEmail(e.target.value)} />
          <input type="text" onChange={(e) => setFirstName(e.target.value)} />
          <input type="file" onChange={(e) => setFile(e.target.files?.[0])} />
          <button onClick={createUser}>Create</button>
        </div>
      );
    };