TypeScript/JavaScript library for a Frappe Framework backend.
The library currently supports the following features:
- 🔐 Authentication - login with username and password
- 🗄 Database - Get document, get list of documents, get count, create, update and delete documents
- 📄 File upload
- 🤙🏻 API calls
We plan to add the following features in the future:
- 🗝 Authentication with OAuth clients
- Support for common functions like
get_last_doc
,exists
in the database.
The library uses Axios under the hood to make API calls to your Frappe backend.
Maintainer | GitHub | Social |
---|---|---|
Nikhil Kothari | nikkothari22 | @nik_kothari22 |
Janhvi Patil | janhvipatil | @janhvipatil_ |
npm install frappe-js-sdk
or
yarn add frappe-js-sdk
To get started, initialise the library:
import { FrappeApp } from "frappe-js-sdk";
//Add your Frappe backend's URL
const frappe = new FrappeApp("https://test.frappe.cloud")
const auth = frappe.auth()
This makes an API call to the /api/method/login
endpoint.
auth.loginWithUsernamePassword({ username: "admin", password: "my-password" })
.then(response => console.log("Logged in"))
.catch(error => console.error(error))
This makes an API call to the /api/method/frappe.auth.get_logged_user
endpoint.
auth.getLoggedInUser()
.then(user => console.log(`User ${user} is logged in.`))
.catch(error => console.error(error))
This makes an API call to the /api/method/logout
endpoint.
auth.logout()
.then(() => console.log("Logged out."))
.catch(error => console.error(error))
const db = frappe.db()
db.getDoc("DocType", "My DocType Name")
.then(doc => console.log(doc))
.catch(error => console.error(error))
db.getDocList("DocType")
.then(docs => console.log(docs))
.catch(error => console.error(error))
Optionally, a second argument can be provided to filter, sort, limit and paginate results.
db.getDocList("DocType", {
/** Fields to be fetched */
fields: ["name", "creation"],
/** Filters to be applied - SQL AND operation */
filters: [["creation", ">", "2021-10-09"]],
/** Filters to be applied - SQL OR operation */
orFilters: [],
/** Fetch from nth document in filtered and sorted list. Used for pagination */
limit_start: 5,
/** Number of documents to be fetched. Default is 20 */
limit: 10,
/** Sort results by field and order */
orderBy: {
field: "creation",
order: 'desc'
},
/** Fetch documents as a dictionary */
asDict: false
})
.then(docs => console.log(docs))
.catch(error => console.error(error))
Type declarations are available for the second argument in the source code.
const filters = [["creation", ">", "2021-10-09"]];
const useCache = true /** Default is false - Optional **/
const debug = false /** Default is false - Optional **/
db.getCount("DocType", filters, cache, debug)
.then(count => console.log(count))
.catch(error => console.error(error))
To create a new document, pass the name of the DocType and the fields to createDoc
.
db.createDoc("My Custom DocType", {
"name": "Test",
"test_field": "This is a test field"
})
.then(doc => console.log(doc))
.catch(error => console.error(error))
To update an existing document, pass the name of the DocType, name of the document and the fields to be updated to updateDoc
.
db.updateDoc("My Custom DocType", "Test", {
"test_field": "This is an updated test field."
})
.then(doc => console.log(doc))
.catch(error => console.error(error))
To create a new document, pass the name of the DocType and the name of the document to be deleted to deleteDoc
.
db.deleteDoc("My Custom DocType", "Test")
.then(response => console.log(response.message)) // Message will be "ok"
.catch(error => console.error(error))
The library supports Typescript out of the box.
For example, to enforce type on the updateDoc
method:
interface TestDoc {
test_field: string
}
db.updateDoc<TestDoc>("My Custom DocType", "Test", {
"test_field": "This is an updated test field."
})
The library also has an inbuilt type FrappeDoc
which adds the following fields to your type declarations when you use it with the database methods:
export type FrappeDoc<T> = T & {
/** User who created the document */
owner: string;
/** Date and time when the document was created - ISO format */
creation: string;
/** Date and time when the document was last modified - ISO format */
modified: string;
/** User who last modified the document */
modified_by: string;
idx: number;
/** 0 - Saved, 1 - Submitted, 2 - Cancelled */
docstatus: 0 | 1 | 2;
parent?: any;
parentfield?: any;
parenttype?: any;
/** The primary key of the DocType table */
name: string;
};
All document responses are returned as an intersection of FrappeDoc
and the specified type.
const call = frappe.call()
Make sure all endpoints are whitelisted (@frappe.whitelist()
) in your backend
Make a GET request to your endpoint with parameters.
const searchParams = {
doctype: "Currency",
txt: "IN"
}
call.get('frappe.desk.search_link', searchParams)
.then(result => console.log(result))
.catch(error => console.error(error))
Make a POST request to your endpoint with parameters.
const updatedFields = {
"doctype": "User",
"name": "Administrator",
"fieldname": "interest",
"value": "Frappe Framework, ERPNext"
}
call.post('frappe.client.set_value', updatedFields)
.then(result => console.log(result))
.catch(error => console.error(error))
Make a PUT request to your endpoint with parameters.
const updatedFields = {
"doctype": "User",
"name": "Administrator",
"fieldname": "interest",
"value": "Frappe Framework, ERPNext"
}
call.put('frappe.client.set_value', updatedFields)
.then(result => console.log(result))
.catch(error => console.error(error))
Make a DELETE request to your endpoint with parameters.
const documentToBeDeleted = {
"doctype": "Tag",
"name": "Random Tag",
}
call.put('frappe.client.delete', documentToBeDeleted)
.then(result => console.log(result))
.catch(error => console.error(error))
const file = frappe.file()
const myFile; //Your File object
const fileArgs = {
/** If the file access is private then set to TRUE (optional) */
"isPrivate": true,
/** Folder the file exists in (optional) */
"folder": "Home",
/** File URL (optional) */
"file_url": "",
/** Doctype associated with the file (optional) */
"doctype": "User",
/** Docname associated with the file (mandatory if doctype is present) */
"docname": "Administrator"
}
file.uploadFile(
myFile,
fileArgs,
/** Progress Indicator callback function **/
(completedBytes, totalBytes) => console.log(Math.round((c / t) * 100), " completed")
)
.then(() => console.log("File Upload complete"))
.catch(e => console.error(e))
See LICENSE.