Recliner-JS: A local REST CouchDB in your browser
Anurag Vohra
Posted on March 9, 2024
Recliner-JS
A CouchDB like DB, which runs in your browser.
Access your saved attachments locally with REST API:
<img src=`/recliner/dbname/doc_id/attachment_name`>
Features
- Uses same REST API like Couch DB.
- Implements CouchDB replicator protocol for seem less replication with backend.
- Can Lazy load Blobs(video media) from cloud.
- Partial content and stream supported.
- Uses IndexedDB as its backend DB and hence no storage restrictions.
- Mango like queries for searching and replication.
- Typescript based client to access REST API.
Usage
In your service worker add this:
import {Recliner} from 'recliner-js';
const recliner = new Recliner();//create instance
self.addEventListener("fetch",(e)=>{
const url_path = Recliner.getPathFromUrl(e.request);
//mounts recliner
if(url_path.startsWith("/recliner")){
e.respondWith(recliner.process(e.request));
}else{
// do whatever else
}
});
Now you can access docs and attachments saved in your recliner DB via URL
<img src=`/recliner/dbname/doc_id/attachment_name`>
CRUD with DB
There are two ways to interact with DB:
- Using regular
fetch
in your JS code using REST API similar to CouchDB*.
const getADoc = await fetch(`/recliner/dbname/docid`);
if(getADoc.status === 200){
return await getADoc.json();//{_id,_rev,ok: true}
}
See complete list of Rest API
*Though many , but not all Couch REST API is supported. See Difference from CouchDB section.
- Use the a client instead:
UsageDAO
import {UsageDAO} from 'recliner-js';
//Create
await UsageDAO.postADoc(dbname,{name:"person1",age:30});
//Retrieve
const doc = await UsageDAO.readADoc(dbname,docid);
//Update
await UsageDAO.updateADoc(dbname,doc._id,{name:"person1",age:34});
//Delete
await UsageDAO.deleteADoc(dbname,doc._id);
//query
const findResult = await UsageDOA.findByPagination({
dbanme,
selector:{
age:{$lt: 40},
income: {$within:[10000,20000]},
loc: {$isinpolygon:[]}//has some GIS capability
}
});
//Save Attachments
await UsageDAO.addAttachmentsToDocID(dbname,doc._id,{
"my_doc.pdf":DOC_BLOB,
"my_video.webm":VIDEO_BLOB
});
//Save attach wth a cloud URl.
//this way when such docs get replicated the Attachments are not sent. As they can be downloaded at end system using the cloud URL
await UsageDAO.addAnAttachmentToExistingDoc(dbname,doc,attachment_name,blob,new_edits,cloud_url,content_type);
//CRUD with attachments on DOC is available
//Replication: say fetch last 10 post
await UsageDAO.replicate({
selector:{
post_id
type:"post",
time:{$lt: now}
},
limit: 10,
source:{
url:"/recliner/my_db"
},
target:{
url: "/proxy/couchdb/dbname",
headers:{
token:"some_token"
}
}
});
Partial content and Media streaming
Save the document with a cloud_url
in a _attachments
property.
await fetch("/recliner/dbname/docid",{
method:"PUT",
body:{
_id:docid
name:"person1",
_attachments:{
"my_video.webm":{
cloud_url:"some_valid_cloud_url_which_supports_partial_content",
}
}
}
});
Now this is can be streamed using:
<video src="/recliner/dbname/docid/my_video.webm">
The video player will automatically stream the video via recliner.
Using the cloud_url, the docs will be partially downloaded and saved for offline use, and then streamed to video
element. So next time when user stream the same video, its pulled out from the local cache.
However for all this to work, you need to configure recliner
for what all mime type you want to support for streaming.
import {Recliner} from 'recliner-js';
const recliner = new Recliner(24,{
"video/webm":1000_000,//1MB of partial content size for streaming
"audio/mp3":1000_00//0.1MB of partial content size for streaming
});
When configured this way, then whenever an attachments of type webm
and mp3
are requested, they are automatically streamed.
If partial content of a doc is not present locally, than using the cloud_url
its partial content is first pulled from cloud, saved in indexedDB and then streamed to the corresponding requesting GUI components like : video
and audio
tags. Next time same partial content will be streamed from local DB, instead of fetching it from cloud_url.
REST API supported
"/recliner/:db";
"/recliner/:db/_design/:ddoc";
"/recliner/:db/_find";
"/recliner/:db/_index";
"/recliner/_replicate";
"/recliner/:db/_changes";
"/recliner/:db/_bulk_get";
"/recliner/:db/_revs_diff";
"/recliner/:db/_local/:docid";
"/recliner/:db/:docid";
"/recliner/:db/:docid/:attachment";
"/recliner/:db/_db_design";
"/recliner/:db/_run_update_function";
"/recliner/_delete_recliner";
Gotchas
- For a multientry search the field name muts end with
_m
- Supported query operators:
$lt,$lte,$eq,$ne,$gte,$gt,$exists,$within,$nin,$regex,$in,$isinpolygon,$isnotinpolygon,$nwithin
Difference from CouchDB
- DB level design docs, saved via
UsageDAO.putADBDesign
, can be used to configure various function at DB level :
export interface DBDesignDoc{
//name of the DB
for_db:string;
//before insertion docs are validated using this
doc_validation_function?:string;//this is stringified JS function
//used for map reduce
map_functions?: Record<string,string>;
reduce_functions?:Record<string,string>;
//can be used to mass modify docs using a selector
update_functions?:Record<string,string>;
/**
* Used during remote to local replication using view query as source. [one can pass viewQueryUrl to replication info, to start view based replication]
* This functions are used to filter view result before replicating them to local DB.
*/
view_result_filter_functions?:Record<string,string>;
}
- Views are not supported, however Indexes, Map and Reduce is still supported using
UsageDAO.postQueryToDBDesign<D>(dbname:string, query:MapReduceQuery)
. - Design docs are not replicated by default when doing
remote to local
orlocal to remote
replication. However forlocal to local
replication design docs are copied. By local means database present in the browser. By remote means the one located and accessed viaHTTPS
on DB server(or via a proxy). -
_local
attribute can be added on doc. They remain solely on local machine, and is removed when are replicated. So some values you wanted to keep in doc, but don;t want to send to server can be saved here. CouchDB Local Docs is supported, which are never replicated.
Though the most important API, to deal with docs and attachments and Database is implemented. And many which deemed insignificant where dropped.
Running the Demo
Type this commands in order
-
npm run i
install dev dependencies -
npm run build
builds for typescript system -
npm run predemo
adds ".js" extension to build for demo purpose. -
npm run demo
: openhttp://localhost:8000/demo/index.html
to view the demo
Posted on March 9, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.