Screenshot 2023-10-25 at 10.48.41 PM.png
Integrating Frappe Backend in NodeJS Applications & Serverless Functions (incl. Firebase Cloud Functions)
In this tutorial, we will use the Frappe JS SDK to integrate our Frappe Framework backend in NodeJS applications. We will also see how to use the SDK in Firebase Cloud Functions.
image7f7d4e.png

By

Md Hussain Nagaria

·

Mar, 1 2023

·

8

min read

Introduction

Frappe Framework has a built-in "automagical" REST API. You can use this API to talk to your Frappe backend in order to perform CRUD operations and more. But calling the API endpoints manually and handling the response can be a tedious process. To solve this problem, we had the frappe-client library, a Python wrapper for the REST API, which made it very easy to use the REST API in Python applications. But not everything is written in Python, isn't it?

JavaScript is one of the most popular programming languages and many great libraries and frameworks have been built on top of it. So, it is time for the Frappe JS SDK! frappe-js-sdk is a JavaScript package, developed by Nikhil Kothari and team, that makes it super easy to integrate a Frappe backend in your JavaScript/TypeScript applications. Yours truly is also a contributor to this library 😆

Prerequisites

This tutorial assumes you know the basics of Frappe (sites, DocTypes etc.) and JavaScript/NodeJS.

Why?

There can be a variety of reasons why you might want to access/integrate your Frappe backend in NodeJS and/or server-less functions:

  • To use your Frappe backend as a data store
  • To integrate your Frappe instance into an existing infrastructure
  • To make Frappe part of your microservices architecture
  • To aggregate data from various Frappe Instances (say for example data processing requirements etc.)

Introduction to Frappe JS SDK

frappe-js-sdk is a JavaScript/TypeScript library that makes it very easy to interact with the Frappe REST API in JavaScript applications. This includes front-end (browser-based) web apps as well as NodeJS based server-side services. You can choose to authenticate using username/password or API tokens (as we will see in this tutorial).

As a bonus, the library is fully written in TypeScript, so you get all the typing and auto-completion magic in your code editors. Enough with the introduction, let's build something now!

Create A Frappe Site

Feel free to skip this step if you already have a live Frappe site. You can also setup and use a site on your local machine.

I am going to use a site hosted on Frappe Cloud. You can easily create a new site by navigating to the Sites tab and clicking on the + New button.

The Expense Scenario

DocTypes in Frappe are basically analogous to database models in other web frameworks like Django, but much more powerful. Once we create a DocType, we can immediately use the REST API to perform CRUD operations on that DocType.

For the purpose of this tutorial, imagine we want to store expenses in our Frappe site. We will create a new DocType to track expenses.

Creating the Expense DocType

  1. Navigate to the DocType list page. You can either use the shortcut link or utilize the awesome bar to search for the page:

  1. Click on the + Add DocType button:

  1. Give the DocType a name and select a module for the DocType. I have created a new Module Def named "Expense Tracker" and selected it.

Add in two fields to this DocType:

  • For - Data - What the expense for?
  • Amount - Currency - How much was it?

I have also enabled the 'In List View' options for both this fields, so they will show up in the Expense DocType's list view.

Done.

Obtaining API Keys

We will need API keys in order to access the REST API (with or without the JavaScript SDK). In order to get the API keys for a particular user in Frappe, we can open that user's document (User DocType) and navigate to the Settings tab:

Click on the Generate Keys button in the "API Access" section. A pop-up will appear with the API Secret, copy it somewhere safe as it can be viewed only once:

Also, note down the API Key:

The authentication token will be formed using both the API Key and the API Secret.

Using The JS SDK In NodeJS Project

Let's create a new directory (named playground in this case) in our system and initialise an empty node project:

$ mkdir playground && cd playground
$ npm init --yes # generates package.json

Now, we can install the frappe-js-sdk library using npm:

$ npm install frappe-js-sdk

Let's also create a JavaScript file where we will write our code:

$ touch index.js

Now, open up the playground/index.js in a code editor of your choice, mine is VSCode. We will start by importing the FrappeApp class and creating a few constants:

import { FrappeApp } from "frappe-js-sdk";

const SITE_URL = "https://hussain.codes"; // Replace with your site's URL
const API_SECRET = "<API-SECRET>";
const API_KEY = "<API-KEY>";

For the sake of brevity, I have stored the API credentials in the code itself, but DO NOT do this in production settings. Store them in environment variables or a secret store.

Initialise And Use The Library

As mentioned earlier, the SDK also supports username/password based auth when run in browser-based environments. But we are going to use token based auth, hence, we set useToken to true and provide a function as the token argument. The API token is formed by adding a : in between the key and the secret as shown below:

const frappe = new FrappeApp(SITE_URL, {
  useToken: true,
  token: () => `${API_KEY}:${API_SECRET}`,
  type: "token", // use "bearer" in case of oauth token
});


const db = frappe.db(); // Initialise the `db` class

Expense CRUD

Creating New Documents

Creating a new expense record is as easy as:

await db.createDoc("Expense", { for: "Coffee", amount: 120 });

Let's run our script using node now:

$ node index.js

After the script finishes executing, open up the Expense List on your Frappe site:

TADA 🎉!

Reading A List Of Documents

We can use the getDocList method to get the list of documents for a given doctype:

const expenses = await db.getDocList("Expense", {
  fields: ["for", "amount", "creation"]
})

console.log(expenses)

When we run the script, we will get the below output:

You can provide a variety of different options for getting the list via the second argument to this method. In the above case, I am specifying the fields I want to fetch. You can also provide more options to perform filter, sort, limit etc.

Updating An Existing Document

You must know the name of the document in order to update or delete it. If we want to update the fields of an existing document, we can use the updateDoc method:

await db.updateDoc("Expense", "aa49085aa3", { amount: 200 }); // Update the value of `amount` to 200

Check your expense list again, the update should get reflected.

Deleting A Document

You might have guessed this one by now, we will use the deleteDoc method to delete a document from our database:

await db.deleteDoc("Expense", "aa49085aa3");

Integrating Frappe In Serverless Functions

As we have seen in the above sections, we can install and use the frappe-js-sdk in a NodeJS environment. This includes server-less (or lambda) functions that support the NodeJS runtime. You can read more about the benefits of server-less functions here.

Server-less functions are, in general, stateless. So they don't store any data and rely on an external data store. Let's use frappe-js-sdk to connect our Frappe instance to a server-less function. I am going to use Firebase Cloud Functions in this tutorial but integrating with other FaaS (Function as a Service) like AWS Lambda, Netlify Functions, etc. should be identical.

Firebase Cloud Functions

Firebase Cloud Functions run in a NodeJS environment and have a generous free-tier. You will need a (free) Firebase account in order to follow along.

Install & Setup Firebase CLI

I will globally install the Firebase CLI using npm:

$ npm install -g firebase-tools

Let's authenticate with our Firebase account:

$ firebase login

Create A New Project

We will now create an empty directory and cd into it:

$ mkdir frappe_firebase && cd frappe_firebase

We can initialize a new Firebase project inside this directory, with only functions feature using the below command:

$ firebase init functions

Running the above command will walk you through a prompt where you will be able to select your Firebase Project (to which the functions will be deployed) and preferred language (JS/TS). Once the wizard is complete, let's open up the directory in VSCode:

Installing frappe-js-sdk

Before we can write any logic, let's install the frappe-js-sdk using npm in our functions directory:

$ cd functions
$ npm install frappe-js-sdk

Writing The Function

Suppose we want to write a function that returns the sum of all our expenses. We will fetch list of all expenses from our Frappe backend, perform the sum and return the result of the calculation as the response of the function.

We will start by deleting the boilerplate code in index.js and importing the required stuff:

const functions = require("firebase-functions");
const { FrappeApp } = require("frappe-js-sdk");
const { defineString } = require("firebase-functions/params");

We can use the defineString function to provide parameters to the function at deploy time. We will use this function to provide the API credentials, so we don't have to store it as plain text in our code:

const SITE_URL = "https://hussain.codes";

const API_KEY = defineString("FRAPPE_API_KEY");
const API_SECRET = defineString("FRAPPE_API_SECRET");

function getToken() {
  return `${API_KEY.value()}:${API_SECRET.value()}`;
}

I have also written a function that returns the auth token by extracting the values (using .value() method) of API key and API secret at runtime. We are ready to write the function now:

exports.totalExpenses = functions.https.onRequest(async (request, response) => {
  const frappe = new FrappeApp(SITE_URL, {
    useToken: true,
    token: getToken, // pass the function itself
    type: "token",
  });
  const db = frappe.db(); // init the `db` class

    // Fetch all expenses (only the "amount" field)
  const expenses = await db.getDocList("Expense", {
    fields: ["amount"],
    limit: 9999 // default is 20
  });

    // Perform the sum
  let sum = 0;
  for (let expense of expenses) {
    sum += expense.amount;
  }

    // Return a JSON object as reponse
  response.send({totalExpense: sum});
});

In the above code, we are first initializing the FrappeApp instance using the site URL and API credentials (token). Then, we fetch the list of expenses and sum all the amounts. In the end, we are returning the sum.

Testing The Function Locally

We can test our newly created function (totalExpense) locally by running the Firebase emulators:

$ firebase emulators:start

Running the above command will start emulating the function locally and show us the endpoint:

If you are running the emulators for the first time, it will prompt you to provide the value for the API_KEY and API_SECRET variables.

Opening the above endpoint in our browser should show the desired result:

Yay! Our Firebase function is now successfully integrated to our Frappe backend!

Resources

Conclusion

That's it for today. I hope you find this tutorial helpful. Let me know if you want to see more content on frappe-js-sdk, for example, integrating in modern front-end frameworks. Will be back! Till then, Ciao!

Published by

Md Hussain Nagaria

on

Mar, 1 2023
0

Share

Add your comment

Success!

Error

Comments

No comments, yet.

Discussion

image7f7d4e.png

Paul Mugambi

·

3 days

ago

Beautiful read, and an insight into an individual I respect and have learned a lot from. Am inspired to trust the process and never give up.

image4c43d6.png

Anna Dane

·

5 days

ago

I must say this is a really amazing post, and for some of my friends who provide Best British Assignment Help, I must recommend this post to them.

Add your comment

Comment