-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathrx-storage-opfs.html
79 lines (77 loc) · 68.7 KB
/
rx-storage-opfs.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<!doctype html>
<html lang="en" dir="ltr" class="docs-wrapper plugin-docs plugin-id-default docs-version-current docs-doc-page docs-doc-id-rx-storage-opfs" data-has-hydrated="false">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Supercharged OPFS Database with RxDB | RxDB - JavaScript Database</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://rxdb.info/img/rxdb_social_card.png"><meta data-rh="true" name="twitter:image" content="https://rxdb.info/img/rxdb_social_card.png"><meta data-rh="true" property="og:url" content="https://rxdb.info/rx-storage-opfs.html"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Supercharged OPFS Database with RxDB | RxDB - JavaScript Database"><meta data-rh="true" name="description" content="Discover how to harness the Origin Private File System with RxDB's OPFS RxStorage for unrivaled performance and security in client-side data storage."><meta data-rh="true" property="og:description" content="Discover how to harness the Origin Private File System with RxDB's OPFS RxStorage for unrivaled performance and security in client-side data storage."><link data-rh="true" rel="icon" href="/img/favicon.ico"><link data-rh="true" rel="canonical" href="https://rxdb.info/rx-storage-opfs.html"><link data-rh="true" rel="alternate" href="https://rxdb.info/rx-storage-opfs.html" hreflang="en"><link data-rh="true" rel="alternate" href="https://rxdb.info/rx-storage-opfs.html" hreflang="x-default"><link rel="preconnect" href="https://www.google-analytics.com">
<link rel="preconnect" href="https://www.googletagmanager.com">
<script async src="https://www.googletagmanager.com/gtag/js?id=G-62D63SY3S0"></script>
<script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-62D63SY3S0",{})</script>
<link rel="preconnect" href="https://www.googletagmanager.com">
<script>window.dataLayer=window.dataLayer||[]</script>
<script>!function(e,t,a,n){e[n]=e[n]||[],e[n].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var g=t.getElementsByTagName(a)[0],m=t.createElement(a);m.async=!0,m.src="https://www.googletagmanager.com/gtm.js?id=GTM-PL63TR5",g.parentNode.insertBefore(m,g)}(window,document,"script","dataLayer")</script><link rel="stylesheet" href="/assets/css/styles.bc483400.css">
<script src="/assets/js/runtime~main.68ae4ffa.js" defer="defer"></script>
<script src="/assets/js/main.90e814da.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PL63TR5" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();t(null!==e?e:"dark")}(),function(){try{const n=new URLSearchParams(window.location.search).entries();for(var[t,e]of n)if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id="__docusaurus"><div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/"><div class="navbar__logo"><img src="/files/logo/logo.svg" alt="RxDB Logo" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="/files/logo/logo.svg" alt="RxDB Logo" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">RxDB</b></a></div><div class="navbar__items navbar__items--right"><a class="navbar__item navbar__link" href="/consulting/">Support</a><a class="navbar__item navbar__link" href="/premium/">Premium</a><a class="navbar__item navbar__link" href="/overview.html">Docs</a><div class="navbarSearchContainer_Bca1"></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div style="position:fixed;display:block;z-index:10;height:1.5px;background-color:var(--color-top);top:62.5px;border-top-right-radius:2px;border-bottom-right-radius:2px;width:0%"></div><div id="__docusaurus_skipToContent_fallback" class="main-wrapper mainWrapper_z2l0"><div class="docsWrapper_hBAB"><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docRoot_UBD9"><aside class="theme-doc-sidebar-container docSidebarContainer_YfHR"><div class="sidebarViewport_aRkj"><div class="sidebar_njMd"><nav aria-label="Docs sidebar" class="menu thin-scrollbar menu_SIkG"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/overview.html">Getting Started with RxDB</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/overview.html">Overview</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/quickstart.html">🚀 Quickstart</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/install.html">Installation</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/dev-mode.html">Development Mode</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/tutorials/typescript.html">TypeScript Setup</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/rx-database.html">Core Entities</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-database.html">RxDatabase</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-schema.html">RxSchema</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-collection.html">RxCollection</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-document.html">RxDocument</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-query.html">RxQuery</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret menu__link--active" role="button" aria-expanded="true" href="/rx-storage.html">💾 Storages</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage.html">RxStorage Overview</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-dexie.html">Dexie.js</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-indexeddb.html">IndexedDB 👑</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/rx-storage-opfs.html">OPFS 👑</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-memory.html">Memory</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-sqlite.html">SQLite 👑</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-filesystem-node.html">Filesystem Node 👑</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-mongodb.html">MongoDB</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-denokv.html">DenoKV</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-storage-foundationdb.html">FoundationDB</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/schema-validation.html">Storage Wrappers</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/replication.html">🔄 Replication</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/rx-server.html">Server</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/transactions-conflicts-revisions.html">How RxDB works</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/migration-schema.html">Advanced Features</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/rx-storage-performance.html">Performance</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/releases/16.0.0.html">Releases</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/offline-first.html">Articles</a></div></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/contribution.html">Contribute & Innovate with RxDB</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/consulting/">Contact</a></div></li></ul></nav></div></div></aside><main class="docMainContainer_TBSr"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_VOVn"><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="Breadcrumbs"><ul class="breadcrumbs" itemscope="" itemtype="https://schema.org/BreadcrumbList"><li class="breadcrumbs__item"><a aria-label="Home page" class="breadcrumbs__link" href="/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_YNFT"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li class="breadcrumbs__item"><span class="breadcrumbs__link">💾 Storages</span><meta itemprop="position" content="1"></li><li itemscope="" itemprop="itemListElement" itemtype="https://schema.org/ListItem" class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link" itemprop="name">OPFS 👑</span><meta itemprop="position" content="2"></li></ul></nav><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><header><h1>Origin Private File System (OPFS) Database with the RxDB OPFS-RxStorage</h1></header>
<p>With the <a href="https://rxdb.info/" target="_blank" rel="noopener noreferrer">RxDB</a> OPFS storage you can build a fully featured database on top of the <a href="https://web.dev/opfs" target="_blank" rel="noopener noreferrer">Origin Private File System</a> (OPFS) browser API. Compared to other storage solutions, it has a way better performance.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-opfs">What is OPFS<a href="#what-is-opfs" class="hash-link" aria-label="Direct link to What is OPFS" title="Direct link to What is OPFS"></a></h2>
<p>The <strong>Origin Private File System (OPFS)</strong> is a native browser storage API that allows web applications to manage files in a private, sandboxed, <strong>origin-specific virtual filesystem</strong>. Unlike <a href="/rx-storage-indexeddb.html">IndexedDB</a> and <a href="/articles/localstorage.html">LocalStorage</a>, which are optimized as object/key-value storage, OPFS provides more granular control for file operations, enabling byte-by-byte access, file streaming, and even low-level manipulations.
OPFS is ideal for applications requiring <strong>high-performance</strong> file operations (<strong>3x-4x faster compared to IndexedDB</strong>) inside of a client-side application, offering advantages like improved speed, more efficient use of resources, and enhanced security and privacy features.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="opfs-limitations">OPFS limitations<a href="#opfs-limitations" class="hash-link" aria-label="Direct link to OPFS limitations" title="Direct link to OPFS limitations"></a></h3>
<p>From the beginning of 2023, the Origin Private File System API is supported by <a href="https://caniuse.com/native-filesystem-api" target="_blank" rel="noopener noreferrer">all modern browsers</a> like Safari, Chrome, Edge and Firefox. Only Internet Explorer is not supported and likely will never get support.</p>
<p>It is important to know that the most performant synchronous methods like <a href="https://developer.mozilla.org/en-US/docs/Web/API/FileSystemSyncAccessHandle/read" target="_blank" rel="noopener noreferrer"><code>read()</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/FileSystemSyncAccessHandle/write" target="_blank" rel="noopener noreferrer"><code>write()</code></a> of the OPFS API are <strong>only available inside of a <a href="/rx-storage-worker.html">WebWorker</a></strong>.
They cannot be used in the main thread, an iFrame or even a <a href="/rx-storage-shared-worker.html">SharedWorker</a>.
The OPFS <a href="https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createSyncAccessHandle" target="_blank" rel="noopener noreferrer"><code>createSyncAccessHandle()</code></a> method that gives you access to the synchronous methods is not exposed in the main thread, only in a Worker.</p>
<p>While there is no concrete <strong>data size limit</strong> defined by the API, browsers will refuse to store more <a href="/articles/indexeddb-max-storage-limit.html">data at some point</a>.
If no more data can be written, a <code>QuotaExceededError</code> is thrown which should be handled by the application, like showing an error message to the user.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-the-opfs-api-works">How the OPFS API works<a href="#how-the-opfs-api-works" class="hash-link" aria-label="Direct link to How the OPFS API works" title="Direct link to How the OPFS API works"></a></h2>
<p>The OPFS API is pretty straightforward to use. First you get the root filesystem. Then you can create files and directories on that. Notice that whenever you <em>synchronously</em> write to, or read from a file, an <code>ArrayBuffer</code> must be used that contains the data. It is not possible to synchronously write plain strings or objects into the file. Therefore the <code>TextEncoder</code> and <code>TextDecoder</code> API must be used.</p>
<p>Also notice that some of the methods of <code>FileSystemSyncAccessHandle</code> <a href="https://developer.chrome.com/blog/sync-methods-for-accesshandles" target="_blank" rel="noopener noreferrer">have been asynchronous</a> in the past, but are synchronous since Chromium 108. To make it less confusing, we just use <code>await</code> in front of them, so it will work in both cases.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// Access the root directory of the origin's private file system.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> root </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> navigator</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">storage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getDirectory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Create a subdirectory.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> diaryDirectory </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> root</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getDirectoryHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'subfolder'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> create</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Create a new file named 'example.txt'.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> fileHandle </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> diaryDirectory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getFileHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'example.txt'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> create</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Create a FileSystemSyncAccessHandle on the file.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> accessHandle </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> fileHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">createSyncAccessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Write a sentence to the file.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">let</span><span class="token plain"> writeBuffer </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">TextEncoder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">encode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'Hello from RxDB'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> writeSize </span><span class="token operator">=</span><span class="token plain"> accessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">write</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">writeBuffer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Read file and transform data to string.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> readBuffer </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Uint8Array</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">writeSize</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> readSize </span><span class="token operator">=</span><span class="token plain"> accessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">read</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">readBuffer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> at</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">0</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> contentAsString </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">TextDecoder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">decode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">readBuffer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Write an exclamation mark to the end of the file.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">writeBuffer </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">TextEncoder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">encode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">'!'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">accessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">write</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">writeBuffer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> at</span><span class="token operator">:</span><span class="token plain"> readSize </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Truncate file to 10 bytes.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> accessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">truncate</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">10</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Get the new size of the file.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> fileSize </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> accessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getSize</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Persist changes to disk.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> accessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">flush</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">// Always close FileSystemSyncAccessHandle if done, so others can open the file again.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> accessHandle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">close</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>A more detailed description of the OPFS API can be found <a href="https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system" target="_blank" rel="noopener noreferrer">on MDN</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="opfs-performance">OPFS performance<a href="#opfs-performance" class="hash-link" aria-label="Direct link to OPFS performance" title="Direct link to OPFS performance"></a></h2>
<p>Because the Origin Private File System API provides low-level access to binary files, it is much faster compared to <a href="/slow-indexeddb.html">IndexedDB</a> or <a href="/articles/localstorage.html">localStorage</a>. According to the <a href="https://pubkey.github.io/client-side-databases/database-comparison/index.html" target="_blank" rel="noopener noreferrer">storage performance test</a>, OPFS is up to 2x times faster on plain inserts when a new file is created on each write. Reads are even faster.</p>
<p>A good comparison about real world scenarios, are the <a href="/rx-storage-performance.html">performance results</a> of the various RxDB storages. Here it shows that reads are up to 4x faster compared to IndexedDB, even with complex queries:</p>
<p align="center"><img src="./files/rx-storage-performance-browser.png" alt="RxStorage performance - browser" width="700"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-opfs-as-rxstorage-in-rxdb">Using OPFS as RxStorage in RxDB<a href="#using-opfs-as-rxstorage-in-rxdb" class="hash-link" aria-label="Direct link to Using OPFS as RxStorage in RxDB" title="Direct link to Using OPFS as RxStorage in RxDB"></a></h2>
<p>The OPFS <a href="/rx-storage.html">RxStorage</a> itself must run inside a WebWorker. Therefore we use the <a href="/rx-storage-worker.html">Worker RxStorage</a> and let it point to the prebuild <code>opfs.worker.js</code> file that comes shipped with RxDB Premium 👑.</p>
<p>Notice that the OPFS RxStorage is part of the <a href="/premium/">RxDB Premium 👑</a> plugin that must be purchased.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> createRxDatabase</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> getRxStorageWorker </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb-premium/plugins/storage-worker'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> database </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">createRxDatabase</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> name</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'mydatabase'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> storage</span><span class="token operator">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getRxStorageWorker</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token doc-comment comment" style="color:rgb(98, 114, 164)">/**</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * This file must be statically served from a webserver.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * You might want to first copy it somewhere outside of</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * your node_modules folder.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> workerInput</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'node_modules/rxdb-premium/dist/workers/opfs.worker.js'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-opfs-in-the-main-thread-instead-of-a-worker">Using OPFS in the main thread instead of a worker<a href="#using-opfs-in-the-main-thread-instead-of-a-worker" class="hash-link" aria-label="Direct link to Using OPFS in the main thread instead of a worker" title="Direct link to Using OPFS in the main thread instead of a worker"></a></h2>
<p>The <code>createSyncAccessHandle</code> method from the Filesystem API is only available inside of a Webworker. Therefore you cannot use <code>getRxStorageOPFS()</code> in the main thread. But there is a slightly slower way to access the virtual filesystem from the main thread. RxDB support the <code>getRxStorageOPFSMainThread()</code> for that. Notice that this uses the <a href="https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createWritable" target="_blank" rel="noopener noreferrer">createWritable</a> function which is not supported in safari.</p>
<p>Using OPFS from the main thread can have benefits because not having to cross the worker bridge can reduce latence in reads and writes.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> createRxDatabase </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> getRxStorageOPFSMainThread </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb-premium/plugins/storage-worker'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> database </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">createRxDatabase</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> name</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'mydatabase'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> storage</span><span class="token operator">:</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getRxStorageOPFSMainThread</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="building-a-custom-workerjs">Building a custom <code>worker.js</code><a href="#building-a-custom-workerjs" class="hash-link" aria-label="Direct link to building-a-custom-workerjs" title="Direct link to building-a-custom-workerjs"></a></h2>
<p>When you want to run additional plugins like storage wrappers or replication <strong>inside</strong> of the worker, you have to build your own <code>worker.js</code> file. You can do that similar to other workers by calling <code>exposeWorkerRxStorage</code> like described in the <a href="/rx-storage-worker.html">worker storage plugin</a>.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// inside of the worker.js file</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> getRxStorageOPFS </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb-premium/plugins/storage-opfs'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> exposeWorkerRxStorage </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb-premium/plugins/storage-worker'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> storage </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getRxStorageOPFS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token function" style="color:rgb(80, 250, 123)">exposeWorkerRxStorage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> storage</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker">Setting <code>usesRxDatabaseInWorker</code> when a RxDatabase is also used inside of the worker<a href="#setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker" class="hash-link" aria-label="Direct link to setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker" title="Direct link to setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker"></a></h2>
<p>When you use the OPFS inside of a worker, it will internally use strings to represent operation results. This has the benefit that transferring strings from the worker to the main thread, is way faster compared to complex json objects. The <code>getRxStorageWorker()</code> will automatically decode these strings on the main thread so that the data can be used by the RxDatabase.</p>
<p>But using a RxDatabase <strong>inside</strong> of your worker can make sense for example when you want to move the <a href="/replication.html">replication</a> with a server. To enable this, you have to set <code>usesRxDatabaseInWorker</code> to <code>true</code>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">// inside of the worker.js file</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> getRxStorageOPFS </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb-premium/plugins/storage-opfs'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> storage </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getRxStorageOPFS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> usesRxDatabaseInWorker</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>If you forget to set this and still create and use a <a href="/rx-database.html">RxDatabase</a> inside of the worker, you might get the error message<code>or</code>Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'length')`.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="opfs-in-electron-react-native-or-capacitorjs">OPFS in Electron, React-Native or Capacitor.js<a href="#opfs-in-electron-react-native-or-capacitorjs" class="hash-link" aria-label="Direct link to OPFS in Electron, React-Native or Capacitor.js" title="Direct link to OPFS in Electron, React-Native or Capacitor.js"></a></h2>
<p>Origin Private File System is a browser API that is only accessible in browsers. Other JavaScript like React-Native or Node.js, do not support it.</p>
<p><strong>Electron</strong> has two JavaScript contexts: the browser (chromium) context and the Node.js context. While you could use the OPFS API in the browser context, it is not recommended. Instead you should use the Filesystem API of Node.js and then only transfer the relevant data with the <a href="https://www.electronjs.org/de/docs/latest/api/ipc-renderer" target="_blank" rel="noopener noreferrer">ipcRenderer</a>. With RxDB that is pretty easy to configure:</p>
<ul>
<li>In the <code>main.js</code>, expose the <a href="/rx-storage-filesystem-node.html">Node Filesystem</a> storage with the <code>exposeIpcMainRxStorage()</code> that comes with the <a href="/electron.html">electron plugin</a></li>
<li>In the browser context, access the main storage with the <code>getRxStorageIpcRenderer()</code> method.</li>
</ul>
<p><strong>React Native</strong> (and Expo) does not have an OPFS API. You could use the ReactNative Filesystem to directly write data. But to get a fully featured database like RxDB it is easier to use the <a href="/rx-storage-sqlite.html">SQLite RxStorage</a> which starts an SQLite database inside of the ReactNative app and uses that to do the database operations.</p>
<p><strong>Capacitor.js</strong> is able to access the OPFS API.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="difference-between-file-system-access-api-and-origin-private-file-system-opfs">Difference between <code>File System Access API</code> and <code>Origin Private File System (OPFS)</code><a href="#difference-between-file-system-access-api-and-origin-private-file-system-opfs" class="hash-link" aria-label="Direct link to difference-between-file-system-access-api-and-origin-private-file-system-opfs" title="Direct link to difference-between-file-system-access-api-and-origin-private-file-system-opfs"></a></h2>
<p>Often developers are confused with the differences between the <code>File System Access API</code> and the <code>Origin Private File System (OPFS)</code>.</p>
<ul>
<li>The <code>File System Access API</code> provides access to the files on the device file system, like the ones shown in the file explorer of the operating system. To use the File System API, the user has to actively select the files from a filepicker.</li>
<li><code>Origin Private File System (OPFS)</code> is a sub-part of the <code>File System Standard</code> and it only describes the things you can do with the filesystem root from <code>navigator.storage.getDirectory()</code>. OPFS writes to a <strong>sandboxed</strong> filesystem, not visible to the user. Therefore the user does not have to actively select or allow the data access.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="learn-more-about-opfs">Learn more about OPFS:<a href="#learn-more-about-opfs" class="hash-link" aria-label="Direct link to Learn more about OPFS:" title="Direct link to Learn more about OPFS:"></a></h2>
<ul>
<li><a href="https://webkit.org/blog/12257/the-file-system-access-api-with-origin-private-file-system/" target="_blank" rel="noopener noreferrer">WebKit: The File System API with Origin Private File System</a></li>
<li><a href="https://caniuse.com/native-filesystem-api" target="_blank" rel="noopener noreferrer">Browser Support</a></li>
<li><a href="https://pubkey.github.io/client-side-databases/database-comparison/index.html" target="_blank" rel="noopener noreferrer">Performance Test Tool</a></li>
</ul></div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages"><a class="pagination-nav__link pagination-nav__link--prev" href="/rx-storage-indexeddb.html"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">IndexedDB 👑</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/rx-storage-memory.html"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Memory</div></a></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#what-is-opfs" class="table-of-contents__link toc-highlight">What is OPFS</a><ul><li><a href="#opfs-limitations" class="table-of-contents__link toc-highlight">OPFS limitations</a></li></ul></li><li><a href="#how-the-opfs-api-works" class="table-of-contents__link toc-highlight">How the OPFS API works</a></li><li><a href="#opfs-performance" class="table-of-contents__link toc-highlight">OPFS performance</a></li><li><a href="#using-opfs-as-rxstorage-in-rxdb" class="table-of-contents__link toc-highlight">Using OPFS as RxStorage in RxDB</a></li><li><a href="#using-opfs-in-the-main-thread-instead-of-a-worker" class="table-of-contents__link toc-highlight">Using OPFS in the main thread instead of a worker</a></li><li><a href="#building-a-custom-workerjs" class="table-of-contents__link toc-highlight">Building a custom <code>worker.js</code></a></li><li><a href="#setting-usesrxdatabaseinworker-when-a-rxdatabase-is-also-used-inside-of-the-worker" class="table-of-contents__link toc-highlight">Setting <code>usesRxDatabaseInWorker</code> when a RxDatabase is also used inside of the worker</a></li><li><a href="#opfs-in-electron-react-native-or-capacitorjs" class="table-of-contents__link toc-highlight">OPFS in Electron, React-Native or Capacitor.js</a></li><li><a href="#difference-between-file-system-access-api-and-origin-private-file-system-opfs" class="table-of-contents__link toc-highlight">Difference between <code>File System Access API</code> and <code>Origin Private File System (OPFS)</code></a></li><li><a href="#learn-more-about-opfs" class="table-of-contents__link toc-highlight">Learn more about OPFS:</a></li></ul></div></div></div></div></main></div></div></div><div class="block footer"><div class="footer-block"><div class="footer-links"><span><a variant="text" href="/" class="footer-logo-button"><img src="/files/logo/logo.svg" alt="RxDB" loading="lazy"><div>RxDB</div></a><div class="footer-community-links"><a variant="text" href="/chat/" target="_blank"><img src="/img/community-links/discord-logo.svg" alt="RxDB Discord" loading="lazy"></a><a variant="text" href="/code/" target="_blank"><img src="/img/community-links/github-logo.svg" alt="RxDB Github" loading="lazy"></a><a variant="text" href="https://twitter.com/intent/user?screen_name=rxdbjs" target="_blank"><img src="/img/community-links/x-logo.svg" alt="RxDB Twitter" loading="lazy"></a><a variant="text" href="https://www.linkedin.com/company/rxdb" target="_blank"><img src="/img/community-links/linkedin-logo.svg" alt="RxDB LinkedIn" loading="lazy"></a><a variant="text" href="https://stackoverflow.com/questions/tagged/rxdb" target="_blank"><img src="/img/community-links/stack-overflow-logo.svg" alt="RxDB Stack Overflow" loading="lazy"></a></div></span><div class="footer-nav-links"><a variant="text" href="/premium/" target="">Premium</a><a variant="text" href="/consulting/" target="">Support</a><a variant="text" href="/overview.html" target="">Documentation</a><a variant="text" href="/chat/" target="_blank">Discord</a><a variant="text" href="/code/" target="_blank">Github</a><a variant="text" href="https://twitter.com/intent/user?screen_name=rxdbjs" target="_blank">Twitter</a><a variant="text" href="https://www.linkedin.com/company/rxdb" target="_blank">LinkedIn</a></div></div><div class="footer-policy"><div><a variant="text" href="/legal-notice/" target="_blank">Legal Notice</a></div><span class="footer-rights">© 2025 RxDB. All rights reserved.</span></div><img class="footer-img desktop-img" src="/img/footer-column.svg" alt="columns" loading="lazy"></div></div><div class="call-to-action-popup"><div class="close"><div class="text">✕</div></div></div></div>
</body>
</html>