Check if a record exists using the API

Hello all

Using the API (both REST and RPC), I’m trying to import users from a JSON file. However I only want to create new users if they do not already exist. I’m trying to figure out which is the most efficient way of checking whether a user (and more generically any record) exists.

I notice that the RPC method frappe.client.get does not throw an error when the record does not exist and returns a JSON object with a top-level key:value pair of “exc_type”:“DoesNotExistError”

curl -s -H "${cHA}" -H "${cHC}" -H "${cHT}" -G ${cUM}frappe.client.get -d 'doctype=User' -d 'name=Admin'

which returns

{"exc_type":"DoesNotExistError","_server_messages":"[\"{\\\"message\\\": \\\"User Admin not found\\\",...

and when the user does exist (eg Administrator, not Admin), the return then is also a JSON object but with a top level key of “message”.

{"message":{"name":"Administrator",...

Currently I use this differentiation on the top-level key to determine whether the record exists or not, before I invoke the REST call to create a new user, but it feels very convoluted and I somehow believe there should be a more efficient way of doing it.

What does the experts recommend?

Here’s how I do it:

declare HOST="dev.erpnext.host"
declare AUTH_TOKEN="Authorization: token 21???????????1d:10???????????ee"
declare RESULT=/tmp/result.log
declare USER="Administrator"
declare HTTP_CODE=$(curl -w "%{http_code}" -s -o ${RESULT} "https://${HOST}/api/resource/User/${USER}" -H "${AUTH_TOKEN}");
if [[ ${HTTP_CODE} -eq 200 ]]; then cat ${RESULT} | jq -r '.data.name'; else echo -e "Uh oh! [${HTTP_CODE}]"; fi;

Returns …

Administrator
declare USER="Admin"
declare HTTP_CODE=$(curl -w "%{http_code}" -s -o ${RESULT} "https://${HOST}/api/resource/User/${USER}" -H "${AUTH_TOKEN}");
if [[ ${HTTP_CODE} -eq 200 ]]; then cat ${RESULT} | jq -r '.data.name'; else echo -e "Uh oh! [${HTTP_CODE}]"; fi;

Returns…

Uh oh! [404]

Set the first two environment variables correctly, and the following commands ought to serve you with no other edits needed.

curl switches:

  • -w return only the indicated parts of the HTTP Response
  • -s silent mode
  • -o send output to the indicated file

jq switches:

  • -r output valid quoted JSON.

However

I bitterly regret ever using shell scripting for configuring an ERPNext site!

Don’t do it!

I have an unmanageable mess of scripts in which everything is 10 times harder to do than it would be in Python.

Recently, I created a setup app that I install as soon as I have a working ERPNext instance.

Instead of slowly pushing data in through the API, I call a whitelisted method on my app that reaches out and pulls in the data I want to load. Programming is easier, cleaner, understandable, maintainable … and fast!

def install_widgets(company):
    LG("Getting values for company :: {}".format(company))
    prepareGlobals(company)

    prepareTemporaryTables()

    LOG("\n\nLoad {} warehouses.".format(NAME_COMPANY))

      :      :      :      :      :      :      :      

For shorties

@frappe.whitelist()
def installwidgets(company):
    return install_widgets(company)

For large batches that time out

@frappe.whitelist()
def queueInstallStockEntries(company):
    frappe.enqueue(
        'setup.setup.doctype.setup.widget.install_stock_entries',
        company=company, is_async=True, timeout=240000)
    return "Enqueued"
1 Like

Ah @MartinHBramwell , this is precisely what I thought I’d get from you : A well constructed, in-depth answer. Thanks so much for your contributions on this forum :laughing:

1 Like

Yeah! It’s called “Obsessive Compulsive Disorder”, but it has its benefits.

Can you share what you use frappe Api’s for?

I’m studying coding and would to hear real world examples.

I use it for importing data from a legacy system.

I prepare initial examples in Postman, trimming the POST bodies down to the minimum required set of attributes then reformat the output of the legacy system to the corresponding JSON.

I can then iterate the JSON, calling the API for each Item, in which ever language suits the moment JS, Python, shell, etc.