API: body vs body_html

Hi all,

I am writing a small script which queries the Evernote API and the Joplin API and uses these to resolve Evernote internal links (evernote://) in imported notes, and convert them to Joplin links (using the Evernote GUID + creation date to find the Joplin note correponding to the evernote:// link).

In doing so I ran across a couple of issues - firstly, could I clarify how body and body_html are used? Is the latter only used by the web clipper? Should I expect either markdown or html in the body element? Is there a way to distinguish which kind of note I retrieve? To know up front if I am dealing with an html or md note?

Also, when using the /note/ endpoint with body_html as the field, I get a 500 server error.

Any clarification would be great! Once I've finished the code I'll push to GitHub and post to the #apps category.

Thanks! Mark

  1. Body vs body_html: Most of the time, when you want to manipulate note data, you should operate markdown instead of html. The latter is only automatically rendered by preview. I am not sure whether markdown will be automatically modified when body_html is modified. This needs to be asked @laurent
  2. If you encounter an error, please confirm what appears in the joplin console first, and display them to better locate the error. Refer to: https://joplinapp.org/debugging/#desktop-application.

In addition, if possible, you can try to use the existing sdk instead of directly calling the original rest api, currently there is a nodejs/python version

They should help you reduce some troubles (if you have relevant language knowledge)

Thanks @rxliuli - when importing Evernote .enex files, Joplin can either convert to markdown or keep the existing HTML, so I need to deal with links in both.

I think, from poking around, that I should always use the body field, and can check for an imported note by looking for the <en-note> tag at the start.

I did look at the python API package, but since my use case is very simple and I was trying to reduce the number of dependencies, I have so far simply use requests to handle it.

Thanks for the tip on debugging - if I have time I'll go back and see if there was any more info around the 500 error!

body_html indeed is only used by the clipper when posting a note. When you retrieve a note, you'll never get a body_html field, it's more like a virtual field - to avoid having too much processing to do in the clipper, we send the whole HTML body to the app, which then converts it to Markdown. Once it's processed, the body_html is discarded, which is why you can't get it back.

In the body element you should expect Markdown if markup_language = 1, or HTML is markup_language = 2. Of course keep in mind that Markdown notes may also contain HTML code, since HTML is valid inside Markdown. But HTML notes will only contain HTML.

It's expected that it fails in this case, but in general when you get an error code you should inspect the response body, as it should contain more information. Actually could you check if you got any response body? If not, I'll take a look as that could be an error somewhere in the app.

Thanks @laurent - with that info I think I'm set - I see markdown_language and it looks to be set correctly for my notes.

Re the error, indeed I was not correctly raising an exception when the API call worked, but the response was "bad" - for the 500 error before, the full response is:

'Internal Server Error: Error: SQLITE_ERROR: no such column: body_html: SELECT `body_html` FROM `notes` WHERE is_conflict = 0 ORDER BY `user_updated_time` DESC, `user_created_time` DESC, `title` DESC, `id` DESC: : \n\nError: Error: SQLITE_ERROR: no such column: body_html: SELECT `body_html` FROM `notes` WHERE is_conflict = 0 ORDER BY `user_updated_time` DESC, `user_created_time` DESC, `title` DESC, `id` DESC: \n    at DatabaseDriverNode.sqliteErrorToJsError (/tmp/.mount_JoplinFq9O2U/resources/app.asar/lib/database-driver-node.js:21:18)\n    at JoplinDatabase.sqliteErrorToJsError (/tmp/.mount_JoplinFq9O2U/resources/app.asar/lib/database.js:25:24)\n    at JoplinDatabase.tryCall (/tmp/.mount_JoplinFq9O2U/resources/app.asar/lib/database.js:113:17)'}

Hi @laurent - everything is now working, and I can fix the broken links in MD and HTML notes - but I realise that in doing so I of course reset the note updated times, which is not ideal because immediately after import these are quite valuable meta-data.

I tried to manually set the user_updated_time and/or updated_time via the API but, logically enough, the updates reset the times to the current one.

Is there any way around this? Short of messing around in the database, I guess? Thanks!

I'd need to see the request you're making but basically every time you update the note, you should pass the required user_updated_time and perhaps user_created_time if you don't want it to change. So when you create the note, but also when you update it.

OK, and the UI / note list etc. will respect user_updated_time (rather than updated_time)? My aim is that I can still order the notes by updated time, ever after the re-writing of the body.

I'll try again and report back!

user_updated_time is what's displayed everywhere in the UI. And updated_time is what is used by the app to know when something has changed, in particular for sync.

Hmm, I'm still not having any luck. I'm making a PUT request to, for example:


with the data containing the body and user_updated_time, e.g.

{"body": "<en-note>.... </en_note>","user_updated_time": 1548754813000}

and I get back an updated note with "user_updated_time":1603274978951.

Am I doing someting dumb? Thanks!

Indeed it was supported when creating a note, but not when updating one. It should work in the next pre-release.

Fantastic, many thanks!

Yep, in 1.3.8 this works a treat - with the ENEX import issues squashed, I think I'm all set tomorrow to do the Big Import! Thanks!