forked from sheetjs/docs.sheetjs.com
		
	gsheet-api
This commit is contained in:
		
							parent
							
								
									4438bd86cd
								
							
						
					
					
						commit
						8914e8f714
					
				@ -9,12 +9,11 @@ import CodeBlock from '@theme/CodeBlock';
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was last tested on 2023 April 18 with `react-data-grid 7.0.0-beta.28`,
 | 
			
		||||
`create-react-app` 5.0.1 and React 18.2.0.
 | 
			
		||||
This demo was last tested on 2023 September 17 with `react-data-grid` version
 | 
			
		||||
`7.0.0-beta.39` and React 18.2.0.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
The demo creates a site that looks like the screenshot below:
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
@ -117,7 +116,7 @@ cd sheetjs-rdg
 | 
			
		||||
2) Install dependencies:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid`}
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-grid@7.0.0-beta.39`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
3) Download [`App.tsx`](pathname:///rdg/App.tsx) and replace `src/App.tsx`.
 | 
			
		||||
@ -126,5 +125,15 @@ npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz react-data-
 | 
			
		||||
curl -L -o src/App.tsx https://docs.sheetjs.com/rdg/App.tsx
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
4) run `npm start`.  When you load the page in the browser, it will attempt to
 | 
			
		||||
   fetch <https://sheetjs.com/pres.numbers> and load the data.
 | 
			
		||||
4) Start the development server:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
npm start
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Testing
 | 
			
		||||
 | 
			
		||||
5) When the page loads, it will fetch <https://sheetjs.com/pres.numbers>, parse
 | 
			
		||||
with SheetJS, and load the data in the data grid.
 | 
			
		||||
 | 
			
		||||
6) Click one of the "export" buttons to export the grid data to file.
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,7 @@
 | 
			
		||||
---
 | 
			
		||||
title: Quasar
 | 
			
		||||
title: Data in Quasar Apps
 | 
			
		||||
sidebar_label: Quasar
 | 
			
		||||
description: Build data-intensive mobile apps with Quasar and Cordova. Seamlessly integrate spreadsheets into your app using SheetJS. Let data in your Excel spreadsheets shine.
 | 
			
		||||
pagination_prev: demos/static/index
 | 
			
		||||
pagination_next: demos/desktop/index
 | 
			
		||||
sidebar_position: 3
 | 
			
		||||
@ -10,10 +12,17 @@ sidebar_custom_props:
 | 
			
		||||
import current from '/version.js';
 | 
			
		||||
import CodeBlock from '@theme/CodeBlock';
 | 
			
		||||
 | 
			
		||||
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
 | 
			
		||||
from the main entrypoint or any script in the project.
 | 
			
		||||
[Quasar](https://quasar.dev/) is a VueJS framework for building iOS and Android
 | 
			
		||||
apps with the Cordova platform.
 | 
			
		||||
 | 
			
		||||
The "Complete Example" creates an app that looks like the screenshots below:
 | 
			
		||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
 | 
			
		||||
data from spreadsheets.
 | 
			
		||||
 | 
			
		||||
This demo uses Quasar and SheetJS to process data and generate spreadsheets.
 | 
			
		||||
We'll explore how to load SheetJS in an Quasar app and use Quasar and Cordova
 | 
			
		||||
features to extract data from, and write data to, spreadsheets on the device.
 | 
			
		||||
 | 
			
		||||
The ["Demo"](#demo) creates an app that looks like the screenshots below:
 | 
			
		||||
 | 
			
		||||
<table><thead><tr>
 | 
			
		||||
  <th><a href="#demo">iOS</a></th>
 | 
			
		||||
@ -30,10 +39,14 @@ The "Complete Example" creates an app that looks like the screenshots below:
 | 
			
		||||
 | 
			
		||||
### Integration Details
 | 
			
		||||
 | 
			
		||||
The [NodeJS Module](/docs/getting-started/installation/nodejs) can be imported
 | 
			
		||||
from the main entrypoint or any script in the project.
 | 
			
		||||
 | 
			
		||||
This demo will use the Quasar ViteJS starter project with VueJS and Cordova.
 | 
			
		||||
The starter places the backing Cordova project in the `src-cordova` folder.
 | 
			
		||||
 | 
			
		||||
The complete solution uses `cordova-plugin-file` for file operations.  It can
 | 
			
		||||
be installed like any other Cordova plugin:
 | 
			
		||||
be installed from the Cordova folder:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cd src-cordova
 | 
			
		||||
@ -41,15 +54,21 @@ cordova plugin add cordova-plugin-file
 | 
			
		||||
cd ..
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
#### Reading data
 | 
			
		||||
### Reading data
 | 
			
		||||
 | 
			
		||||
The `q-file` component presents an API reminiscent of File Input elements:
 | 
			
		||||
The QFile[^1] component presents an API reminiscent of File Input elements:
 | 
			
		||||
 | 
			
		||||
```html
 | 
			
		||||
<q-file label="Load File" filled label-color="orange" @input="updateFile"/>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
When binding to the `input` element, the callback receives an `Event` object:
 | 
			
		||||
When binding to the `input` element, the callback receives an `Event` object.
 | 
			
		||||
Using standard DOM operations, the file data can be pulled into an `ArrayBuffer`
 | 
			
		||||
and parsed using the SheetJS `read` method[^2]. `read` returns a workbook[^3]
 | 
			
		||||
object that holds data and metadata for each worksheet.
 | 
			
		||||
 | 
			
		||||
This snippet reads a workbook, pulls the first worksheet, generates an array of
 | 
			
		||||
objects using the SheetJS `sheet_to_json`[^4] method, and updates state:
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
import { read } from 'xlsx';
 | 
			
		||||
@ -74,14 +93,33 @@ async function updateFile(v) { try {
 | 
			
		||||
 | 
			
		||||
#### Writing data
 | 
			
		||||
 | 
			
		||||
The API is shaped like the File and Directory Entries API.  For clarity, since
 | 
			
		||||
the code is a "pyramid of doom", the error handlers are omitted:
 | 
			
		||||
Starting from an array of objects, the SheetJS `json_to_sheet` method[^5]
 | 
			
		||||
generates a SheetJS worksheet object. The `book_append_sheet` and `book_new`
 | 
			
		||||
helper functions[^6] create a SheetJS workbook object that can be exported:
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
```js
 | 
			
		||||
import { utils } from 'xlsx';
 | 
			
		||||
 | 
			
		||||
// assuming `todos` is a VueJS ref whose value is an array of objects
 | 
			
		||||
const ws = utils.json_to_sheet(todos.value);
 | 
			
		||||
const wb = utils.book_new();
 | 
			
		||||
utils.book_append_sheet(wb, ws, "SheetJSQuasar");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The SheetJS `write` function[^7] with the option `type: "buffer"` will generate
 | 
			
		||||
`Uint8Array` objects that can be converted to blobs and exported:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
import { write } from 'xlsx';
 | 
			
		||||
 | 
			
		||||
// on iOS and android, `XLSX.write` with type "buffer" returns a `Uint8Array`
 | 
			
		||||
const u8: Uint8Array = write(wb, {bookType: "xlsx", type: "buffer"});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `cordova-plugin-file` API writes the data to the filesystem. The code uses
 | 
			
		||||
the File and Directory Entries API[^8]:
 | 
			
		||||
 | 
			
		||||
```ts
 | 
			
		||||
// Request filesystem access for persistent storage
 | 
			
		||||
window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
 | 
			
		||||
  // Request a handle to "SheetJSQuasar.xlsx", making a new file if necessary
 | 
			
		||||
@ -107,12 +145,11 @@ window.requestFileSystem(window.PERSISTENT, 0, function(fs) {
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was tested on an Intel Mac on 2023 April 08. `create-quasar@1.1.2`
 | 
			
		||||
was installed during app creation.  The app used Quasar version `2.11.10`.
 | 
			
		||||
The Android demo was last tested on 2023 September 18 with Quasar `2.12.7` on an
 | 
			
		||||
emulated Pixel 3 + Android 13 ("Tiramisu") API 33.
 | 
			
		||||
 | 
			
		||||
The iOS simulator runs iOS 16.2 on an iPhone 14 Pro Max.
 | 
			
		||||
 | 
			
		||||
The Android simulator runs Android 12.0 (S) API 31 on a Pixel 3.
 | 
			
		||||
The iOS demo was last tested on 2023 September 18 with Quasar `2.12.7` on an
 | 
			
		||||
emulated iPhone SE (3rd generation) + iOS 16.4.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
@ -196,20 +233,38 @@ Return to the project directory:
 | 
			
		||||
cd ..
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
4) Start the development server:
 | 
			
		||||
11) Enable file sharing and make the documents folder visible in the iOS app.
 | 
			
		||||
The following lines must be added to `src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist`:
 | 
			
		||||
 | 
			
		||||
```xml title="src-cordova/platforms/ios/SheetJSQuasar/SheetJSQuasar-Info.plist (add to file)"
 | 
			
		||||
<plist version="1.0">
 | 
			
		||||
<dict>
 | 
			
		||||
<!-- highlight-start -->
 | 
			
		||||
  <key>UIFileSharingEnabled</key>
 | 
			
		||||
  <true/>
 | 
			
		||||
  <key>LSSupportsOpeningDocumentsInPlace</key>
 | 
			
		||||
  <true/>
 | 
			
		||||
<!-- highlight-end -->
 | 
			
		||||
  <key>CFBundleDevelopmentRegion</key>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
(The root element of the document is `plist` and it contains one `dict` child)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
5) Start the development server:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
quasar dev -m ios
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::caution
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
If the app is blank or not refreshing, delete the app and close the simulator,
 | 
			
		||||
then restart the development process.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
5) Add the Dialog plugin to `quasar.config.js`:
 | 
			
		||||
6) Add the Dialog plugin to `quasar.config.js`:
 | 
			
		||||
 | 
			
		||||
```js title="quasar.config.js"
 | 
			
		||||
    framework: {
 | 
			
		||||
@ -221,7 +276,7 @@ then restart the development process.
 | 
			
		||||
    },
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
6) In the template section of `src/pages/IndexPage.vue`, replace the example
 | 
			
		||||
7) In the template section of `src/pages/IndexPage.vue`, replace the example
 | 
			
		||||
   with a Table, Save button and Load file picker component:
 | 
			
		||||
 | 
			
		||||
```html title="src/pages/IndexPage.vue"
 | 
			
		||||
@ -266,7 +321,7 @@ then restart the development process.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
7) Wire up the `updateFile` function:
 | 
			
		||||
8) Wire up the `updateFile` function:
 | 
			
		||||
 | 
			
		||||
```ts title="src/pages/IndexPage.vue"
 | 
			
		||||
import { defineComponent, ref } from 'vue';
 | 
			
		||||
@ -315,7 +370,7 @@ To test that reading works:
 | 
			
		||||
 | 
			
		||||
Once selected, the screen should refresh with new contents.
 | 
			
		||||
 | 
			
		||||
8) Wire up the `saveFile` function:
 | 
			
		||||
9) Wire up the `saveFile` function:
 | 
			
		||||
 | 
			
		||||
```ts title="src/pages/IndexPage.vue"
 | 
			
		||||
    function saveFile() {
 | 
			
		||||
@ -401,7 +456,7 @@ id,content
 | 
			
		||||
 | 
			
		||||
**Android**
 | 
			
		||||
 | 
			
		||||
9) Create the Android project:
 | 
			
		||||
10) Create the Android project:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
cd src-cordova
 | 
			
		||||
@ -409,24 +464,12 @@ cordova platform add android
 | 
			
		||||
cd ..
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
10) Start the simulator:
 | 
			
		||||
11) Start the simulator:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
quasar dev -m android
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
In local testing, the Quasar build process threw an error:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
  java.lang.IllegalArgumentException: Unsupported class file major version 63
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This was resolved by rolling back to Java 1.8
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
To test that reading works:
 | 
			
		||||
 | 
			
		||||
- Click and drag `pres.numbers` from a Finder window into the simulator.
 | 
			
		||||
@ -442,3 +485,12 @@ To test that writing works:
 | 
			
		||||
adb exec-out run-as org.sheetjs.quasar cat files/files/SheetJSQuasar.xlsx > /tmp/SheetJSQuasar.xlsx
 | 
			
		||||
npx xlsx-cli /tmp/SheetJSQuasar.xlsx
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
[^1]: See ["File Picker"](https://quasar.dev/vue-components/file-picker) in the Quasar documentation.
 | 
			
		||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
 | 
			
		||||
[^3]: See ["SheetJS Data Model"](/docs/csf/) for more details on workbooks, worksheets, and other concepts.
 | 
			
		||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
 | 
			
		||||
[^5]: See [`json_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-objects-input)
 | 
			
		||||
[^6]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
 | 
			
		||||
[^7]: See [`write` in "Writing Files"](/docs/api/write-options)
 | 
			
		||||
[^8]: See [`requestFileSystem`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestFileSystem) in the MDN Web Docs for more details.
 | 
			
		||||
@ -183,7 +183,9 @@ The following Web APIs are featured in separate demos:
 | 
			
		||||
    <a href={item.href}>{item.label}</a>{item.customProps?.summary && (" - " + item.customProps.summary)}
 | 
			
		||||
  </li>);
 | 
			
		||||
})}
 | 
			
		||||
<li><a href="/docs/demos/local/indexeddb">IndexedDB API</a></li></ul>
 | 
			
		||||
<li><a href="/docs/demos/local/storageapi">Local Storage API</a></li>
 | 
			
		||||
<li><a href="/docs/demos/local/indexeddb">IndexedDB API</a></li>
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
### SQL Databases
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,9 @@
 | 
			
		||||
---
 | 
			
		||||
title: Local Storage API
 | 
			
		||||
pagination_prev: demos/desktop/index
 | 
			
		||||
pagination_next: demos/local/index
 | 
			
		||||
pagination_prev: demos/data/index
 | 
			
		||||
pagination_next: demos/cloud/index
 | 
			
		||||
sidebar_custom_props:
 | 
			
		||||
  type: web
 | 
			
		||||
  summary: Reading and writing data in an in-browser Key-Value store
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
The Storage API, encompassing `localStorage` and `sessionStorage`, describes
 | 
			
		||||
@ -14,6 +14,17 @@ This demo covers two common use patterns:
 | 
			
		||||
- "Row Objects" shows a simple convention for loading and storing row objects
 | 
			
		||||
- "Simple Strings" discusses how to persist and recover a raw Storage
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
Each browser demo was tested in the following environments:
 | 
			
		||||
 | 
			
		||||
| Browser     | Date       |
 | 
			
		||||
|:------------|:-----------|
 | 
			
		||||
| Chrome 116  | 2023-09-17 |
 | 
			
		||||
| Safari 16.6 | 2023-09-17 |
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Row Objects
 | 
			
		||||
 | 
			
		||||
Consider the following array of objects of data:
 | 
			
		||||
@ -70,12 +81,6 @@ function localStorage_to_sheet() {
 | 
			
		||||
 | 
			
		||||
### Live Demo
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was last tested on 2023 April 09.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
This example will fetch <https://sheetjs.com/pres.numbers>, fill `localStorage`
 | 
			
		||||
with rows, then generate a worksheet from the rows and write to a new file.
 | 
			
		||||
 | 
			
		||||
@ -84,13 +89,15 @@ After saving the exported file, the Local Storage can be inspected in the
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
:::caution
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
This example is for illustration purposes. If array of objects is available, it
 | 
			
		||||
is strongly recommended to convert that array to a worksheet directly.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Live Demo</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
```jsx live
 | 
			
		||||
function SheetJStorage() {
 | 
			
		||||
  const [url, setUrl] = React.useState("https://sheetjs.com/pres.numbers");
 | 
			
		||||
@ -129,6 +136,8 @@ function SheetJStorage() {
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
## Simple Strings
 | 
			
		||||
 | 
			
		||||
The ["Row Objects" approach](#row-objects) is strongly recommended when trying
 | 
			
		||||
@ -155,7 +164,7 @@ The natural representation is an array of arrays:
 | 
			
		||||
 | 
			
		||||
#### Exporting Storage
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
Web Storage iteration order is not defined.  By using indices as keys, the row
 | 
			
		||||
objects approach has an ordering.  That does not apply to the general case.
 | 
			
		||||
@ -187,15 +196,11 @@ function ws_to_localStorage(ws) {
 | 
			
		||||
 | 
			
		||||
### Live Demo
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
This demo was last tested on 2023 April 09.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
This example fills `localStorage` with 10 random keys and 10 random values,
 | 
			
		||||
generates a worksheet from the data and writes to a new file.
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Live Demo</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
```jsx live
 | 
			
		||||
function SheetJSRandomStorage() {
 | 
			
		||||
  const [out, setOut] = React.useState("");
 | 
			
		||||
@ -231,3 +236,5 @@ function SheetJSRandomStorage() {
 | 
			
		||||
  </> );
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
@ -3,7 +3,7 @@ title: IndexedDB API
 | 
			
		||||
pagination_prev: demos/desktop/index
 | 
			
		||||
pagination_next: demos/local/index
 | 
			
		||||
sidebar_custom_props:
 | 
			
		||||
  summary: Reading and writing data in browser storage
 | 
			
		||||
  summary: Reading and writing data in an in-browser NoSQL database
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
<head>
 | 
			
		||||
 | 
			
		||||
@ -9,16 +9,7 @@ import Tabs from '@theme/Tabs';
 | 
			
		||||
import TabItem from '@theme/TabItem';
 | 
			
		||||
import CodeBlock from '@theme/CodeBlock';
 | 
			
		||||
 | 
			
		||||
This demo uses `node-google-spreadsheet` to interact with Google Sheets v4 API.
 | 
			
		||||
 | 
			
		||||
:::caution
 | 
			
		||||
 | 
			
		||||
Google Sheets deprecates APIs quickly and there is no guarantee that the
 | 
			
		||||
referenced API version will be available in the future.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
This demo focuses on external data processing.  For Google Apps Script custom
 | 
			
		||||
functions, [the "Google Sheets" extension demo](/docs/demos/extensions/gsheet)
 | 
			
		||||
@ -26,127 +17,123 @@ covers Apps Script integration.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Initial Configuration
 | 
			
		||||
[Google Sheets](https://google.com/sheets/about/) is a collaborative spreadsheet
 | 
			
		||||
service with powerful external APIs for automation.
 | 
			
		||||
 | 
			
		||||
Install the dependencies:
 | 
			
		||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
 | 
			
		||||
data from spreadsheets.
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet@3.3.0`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
This demo uses SheetJS to properly exchange data with spreadsheet files. We'll
 | 
			
		||||
explore how to use NodeJS integration libraries and SheetJS in three data flows:
 | 
			
		||||
 | 
			
		||||
The library README has a [guide](https://theoephraim.github.io/node-google-spreadsheet/#/getting-started/authentication)
 | 
			
		||||
for configuring a service worker with write access to the document.  Following
 | 
			
		||||
the service worker guide, the JSON key should be saved to `key.json`.
 | 
			
		||||
- "Importing data": Data in a NUMBERS spreadsheet will be parsed using SheetJS
 | 
			
		||||
libraries and written to a Google Sheets Document
 | 
			
		||||
 | 
			
		||||
The following helper function attempts to authenticate and access the specified
 | 
			
		||||
sheet by ID.  The code should be copied and saved to `common.js`:
 | 
			
		||||
- "Exporting data": Data in Google Sheets will be pulled into arrays of objects.
 | 
			
		||||
A workbook will be assembled and exported to Excel Binary workbooks (XLSB).
 | 
			
		||||
 | 
			
		||||
<details><summary><b>Code</b> (click to show)</summary>
 | 
			
		||||
- "Exporting files": SheetJS libraries will read XLSX and ODS files exported by
 | 
			
		||||
Google Sheets and generate CSV rows from every worksheet.
 | 
			
		||||
 | 
			
		||||
```js title=common.js
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
const { GoogleSpreadsheet } = require('google-spreadsheet');
 | 
			
		||||
:::warning pass
 | 
			
		||||
 | 
			
		||||
module.exports = async(ID) => {
 | 
			
		||||
  /* get credentials */
 | 
			
		||||
  const creds = JSON.parse(fs.readFileSync('key.json'));
 | 
			
		||||
It is strongly recommended to create a new Google account for testing.
 | 
			
		||||
 | 
			
		||||
  /* initialize sheet and authenticate */
 | 
			
		||||
  const doc = new GoogleSpreadsheet(ID);
 | 
			
		||||
  await doc.useServiceAccountAuth(creds);
 | 
			
		||||
  await doc.loadInfo();
 | 
			
		||||
  return doc;
 | 
			
		||||
}
 | 
			
		||||
**One small mistake could result in a block or ban from Google services.**
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
Google Sheets deprecates APIs quickly and there is no guarantee that the
 | 
			
		||||
referenced APIs will be available in the future.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
## Integration Details
 | 
			
		||||
 | 
			
		||||
This demo uses the following NodeJS modules:
 | 
			
		||||
 | 
			
		||||
- `google-auth-library`[^1] to authenticate with Google APIs
 | 
			
		||||
- `node-google-spreadsheet`[^2] to interact with Google Sheets v4 API
 | 
			
		||||
 | 
			
		||||
:::info Initial Setup
 | 
			
		||||
 | 
			
		||||
There are a number of steps to enable the Google Sheets API and Google Drive API
 | 
			
		||||
for an account. The [Complete Example](#complete-example) covers the process.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### Authentication
 | 
			
		||||
 | 
			
		||||
It is strongly recommended to use a service account for Google API operations.
 | 
			
		||||
The ["Service Account Setup" section](#service-account-setup) covers how to
 | 
			
		||||
create a service account and generate a JSON key file.
 | 
			
		||||
 | 
			
		||||
```js title="Authenticate using a JSON key file"
 | 
			
		||||
import { JWT } from 'google-auth-library'
 | 
			
		||||
import { GoogleSpreadsheet } from 'google-spreadsheet';
 | 
			
		||||
 | 
			
		||||
// adjust the path to the actual key file.
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
 | 
			
		||||
 | 
			
		||||
const jwt = new JWT({
 | 
			
		||||
  email: creds.client_email,
 | 
			
		||||
  key: creds.private_key,
 | 
			
		||||
  scopes: [
 | 
			
		||||
    'https://www.googleapis.com/auth/spreadsheets',
 | 
			
		||||
    'https://www.googleapis.com/auth/drive.file',
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
### Connecting to Documents
 | 
			
		||||
 | 
			
		||||
To connect to existing documents, the document ID must be specified. This ID can
 | 
			
		||||
be found from the edit URL.
 | 
			
		||||
 | 
			
		||||
The edit URL starts with `https://docs.google.com/spreadsheets/d/` and includes
 | 
			
		||||
`/edit`. The ID is the string of characters between the slashes.  For example:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
https://docs.google.com/spreadsheets/d/a_long_string_of_characters/edit#gid=0
 | 
			
		||||
---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^--- ID
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The `GoogleSpreadsheet` constructor accepts a document ID and auth object:
 | 
			
		||||
 | 
			
		||||
```js title="Connect to a document"
 | 
			
		||||
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
 | 
			
		||||
await doc.loadInfo();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Creating New Documents
 | 
			
		||||
 | 
			
		||||
The `createNewSpreadsheetDocument` makes a request to create a new document. It
 | 
			
		||||
is strongly recommended to create a blank sheet.
 | 
			
		||||
 | 
			
		||||
```js title="Create a new document with blank sheet"
 | 
			
		||||
const doc = await GoogleSpreadsheet.createNewSpreadsheetDocument(jwt, { title: 'Document Title' });
 | 
			
		||||
const newSheet = await doc.addSheet({ title: 'Sheet Title' });
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Array of Arrays
 | 
			
		||||
 | 
			
		||||
["Arrays of Arrays"](/docs/api/utilities/array#array-of-arrays) are the main
 | 
			
		||||
data format for interchange with Google Sheets. The outer array object includes
 | 
			
		||||
row arrays, and each row array includes data.
 | 
			
		||||
 | 
			
		||||
SheetJS provides methods for working with Arrays of Arrays:
 | 
			
		||||
 | 
			
		||||
- `aoa_to_sheet`[^3] creates SheetJS worksheet objects from arrays of arrays
 | 
			
		||||
- `sheet_to_json`[^4] can generate arrays of arrays from SheetJS worksheets
 | 
			
		||||
 | 
			
		||||
## Export Document Data
 | 
			
		||||
 | 
			
		||||
The goal is to create an XLSB export from a Google Sheet.  Google Sheets does
 | 
			
		||||
not natively support the XLSB format.  SheetJS fills the gap.
 | 
			
		||||
 | 
			
		||||
<details><summary><b>How to run locally</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
0) Follow the [Initial Configuration](#initial-configuration).
 | 
			
		||||
 | 
			
		||||
1) Create a new Google Sheet and share with the generated service account.  It
 | 
			
		||||
should be granted the "Editor" role
 | 
			
		||||
 | 
			
		||||
2) Install the dependencies:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet@3.3.0`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
2) Save the following snippet to `common.js`:
 | 
			
		||||
 | 
			
		||||
```js title=common.js
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
const { GoogleSpreadsheet } = require('google-spreadsheet');
 | 
			
		||||
 | 
			
		||||
module.exports = async(ID) => {
 | 
			
		||||
  /* get credentials */
 | 
			
		||||
  const creds = JSON.parse(fs.readFileSync('key.json'));
 | 
			
		||||
 | 
			
		||||
  /* initialize sheet and authenticate */
 | 
			
		||||
  const doc = new GoogleSpreadsheet(ID);
 | 
			
		||||
  await doc.useServiceAccountAuth(creds);
 | 
			
		||||
  await doc.loadInfo();
 | 
			
		||||
  return doc;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
3) Save the following snippet to `pull.js`:
 | 
			
		||||
 | 
			
		||||
```js title=pull.js
 | 
			
		||||
const XLSX = require("xlsx");
 | 
			
		||||
 | 
			
		||||
/* create a blank workbook */
 | 
			
		||||
const wb = XLSX.utils.book_new();
 | 
			
		||||
 | 
			
		||||
const init = require("./common");
 | 
			
		||||
const ID = "<google sheet ID>";
 | 
			
		||||
 | 
			
		||||
(async() => {
 | 
			
		||||
 | 
			
		||||
  const doc = await init(ID);
 | 
			
		||||
 | 
			
		||||
  for(let i = 0; i < doc.sheetsByIndex.length; ++i) {
 | 
			
		||||
    const sheet = doc.sheetsByIndex[i];
 | 
			
		||||
    const name = sheet.title;
 | 
			
		||||
 | 
			
		||||
    /* get the header and data rows */
 | 
			
		||||
    await sheet.loadHeaderRow();
 | 
			
		||||
    const header = sheet.headerValues;
 | 
			
		||||
    const rows = await sheet.getRows();
 | 
			
		||||
    const aoa = [header].concat(rows.map(r => r._rawData));
 | 
			
		||||
 | 
			
		||||
    /* generate a SheetJS Worksheet */
 | 
			
		||||
    const ws = XLSX.utils.aoa_to_sheet(aoa);
 | 
			
		||||
 | 
			
		||||
    /* add to workbook */
 | 
			
		||||
    XLSX.utils.book_append_sheet(wb, ws, name);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* write to SheetJS.xlsb */
 | 
			
		||||
  XLSX.writeFile(wb, "SheetJS.xlsb");
 | 
			
		||||
 | 
			
		||||
})();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
4) Replace `<google sheet ID>` with the ID of the actual document.
 | 
			
		||||
 | 
			
		||||
5) Run `node pull.js` once. It will create `SheetJS.xlsb`.
 | 
			
		||||
 | 
			
		||||
6) Open `SheetJS.xlsb` and confirm the contents are the same as Google Sheets.
 | 
			
		||||
 | 
			
		||||
7) Change some cells in the Google Sheets Document.
 | 
			
		||||
 | 
			
		||||
8) Run `node pull.js` again and reopen `SheetJS.xlsb` to confirm value changes.
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
 | 
			
		||||
### Convert a Single Sheet
 | 
			
		||||
 | 
			
		||||
The idea is to extract the raw data from the Google Sheet headers and combine
 | 
			
		||||
@ -196,115 +183,8 @@ async function doc_to_wb(doc) {
 | 
			
		||||
 | 
			
		||||
## Update Document Data
 | 
			
		||||
 | 
			
		||||
The goal is to refresh a Google Sheet based on a local file.
 | 
			
		||||
 | 
			
		||||
<details><summary><b>How to run locally</b> (click to show)</summary>
 | 
			
		||||
 | 
			
		||||
0) Follow the [Initial Configuration](#initial-configuration).
 | 
			
		||||
 | 
			
		||||
1) Create a new Google Sheet and share with the generated service account.  It
 | 
			
		||||
should be granted the "Editor" role
 | 
			
		||||
 | 
			
		||||
2) Install the dependencies:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i -S https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet@3.3.0`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
2) Save the following snippet to `common.js`:
 | 
			
		||||
 | 
			
		||||
```js title=common.js
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
const { GoogleSpreadsheet } = require('google-spreadsheet');
 | 
			
		||||
 | 
			
		||||
module.exports = async(ID) => {
 | 
			
		||||
  /* get credentials */
 | 
			
		||||
  const creds = JSON.parse(fs.readFileSync('key.json'));
 | 
			
		||||
 | 
			
		||||
  /* initialize sheet and authenticate */
 | 
			
		||||
  const doc = new GoogleSpreadsheet(ID);
 | 
			
		||||
  await doc.useServiceAccountAuth(creds);
 | 
			
		||||
  await doc.loadInfo();
 | 
			
		||||
  return doc;
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
3) Save the following snippet to `push.js`:
 | 
			
		||||
 | 
			
		||||
```js title=push.js
 | 
			
		||||
const XLSX = require("xlsx");
 | 
			
		||||
const fs = require("fs");
 | 
			
		||||
/* create dummy worksheet if `sheetjs.xlsx` does not exist */
 | 
			
		||||
if(!fs.existsSync("sheetjs.xlsx")) {
 | 
			
		||||
  const wb = XLSX.utils.book_new();
 | 
			
		||||
  const ws1 = XLSX.utils.aoa_to_sheet([["a","b","c"],[1,2,3]]); XLSX.utils.book_append_sheet(wb, ws1, "Sheet1");
 | 
			
		||||
  const ws2 = XLSX.utils.aoa_to_sheet([["a","b","c"],[4,5,6]]); XLSX.utils.book_append_sheet(wb, ws2, "Sheet2");
 | 
			
		||||
  XLSX.writeFile(wb, "sheetjs.xlsx");
 | 
			
		||||
}
 | 
			
		||||
/* read and parse sheetjs.xlsx */
 | 
			
		||||
const wb = XLSX.readFile("sheetjs.xlsx");
 | 
			
		||||
 | 
			
		||||
const init = require("./common");
 | 
			
		||||
const ID = "<google sheet ID>";
 | 
			
		||||
 | 
			
		||||
(async() => {
 | 
			
		||||
 | 
			
		||||
  const doc = await init(ID);
 | 
			
		||||
 | 
			
		||||
  /* clear workbook */
 | 
			
		||||
  {
 | 
			
		||||
    /* delete all sheets after the first sheet */
 | 
			
		||||
    const old_sheets = doc.sheetsByIndex;
 | 
			
		||||
    for(let i = 1; i < old_sheets.length; ++i) {
 | 
			
		||||
      await old_sheets[i].delete();
 | 
			
		||||
    }
 | 
			
		||||
    /* clear first worksheet */
 | 
			
		||||
    old_sheets[0].clear();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* write worksheets */
 | 
			
		||||
  {
 | 
			
		||||
    const name = wb.SheetNames[0];
 | 
			
		||||
    const ws = wb.Sheets[name];
 | 
			
		||||
    /* first worksheet already exists */
 | 
			
		||||
    const sheet = doc.sheetsByIndex[0];
 | 
			
		||||
 | 
			
		||||
    /* update worksheet name */
 | 
			
		||||
    await sheet.updateProperties({title: name});
 | 
			
		||||
 | 
			
		||||
    /* generate array of arrays from the first worksheet */
 | 
			
		||||
    const aoa = XLSX.utils.sheet_to_json(ws, {header: 1});
 | 
			
		||||
 | 
			
		||||
    /* set document header row to first row of the AOA */
 | 
			
		||||
    await sheet.setHeaderRow(aoa[0])
 | 
			
		||||
 | 
			
		||||
    /* add the remaining rows */
 | 
			
		||||
    await sheet.addRows(aoa.slice(1));
 | 
			
		||||
 | 
			
		||||
    /* the other worksheets must be created manually */
 | 
			
		||||
    for(let i = 1; i < wb.SheetNames.length; ++i) {
 | 
			
		||||
      const name = wb.SheetNames[i];
 | 
			
		||||
      const ws = wb.Sheets[name];
 | 
			
		||||
 | 
			
		||||
      const sheet = await doc.addSheet({title: name});
 | 
			
		||||
      const aoa = XLSX.utils.sheet_to_json(ws, {header: 1});
 | 
			
		||||
      await sheet.setHeaderRow(aoa[0])
 | 
			
		||||
      await sheet.addRows(aoa.slice(1));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
})();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
4) Replace `<google sheet ID>` with the ID of the actual document.
 | 
			
		||||
 | 
			
		||||
5) Run `node push.js` once. It will create `sheetjs.xlsx` and update the sheet.
 | 
			
		||||
 | 
			
		||||
6) Edit `sheetjs.xlsx` with some new data
 | 
			
		||||
 | 
			
		||||
7) Run `node push.js` again and watch the Google Sheet update!
 | 
			
		||||
 | 
			
		||||
</details>
 | 
			
		||||
The goal is to import data from a NUMBERS file to Google Sheets. Google Sheets
 | 
			
		||||
does not natively support the NUMBERS format.  SheetJS fills the gap.
 | 
			
		||||
 | 
			
		||||
### Clear the Document
 | 
			
		||||
 | 
			
		||||
@ -428,75 +308,476 @@ async function sheet_load_from_ws(sheet, ws) {
 | 
			
		||||
 | 
			
		||||
## Raw File Exports
 | 
			
		||||
 | 
			
		||||
`node-google-spreadsheet` can download the XLSX or ODS export of the document.
 | 
			
		||||
The functions return NodeJS `Buffer` data that can be parsed using SheetJS.
 | 
			
		||||
In the web interface, Google Sheets can export documents to `XLSX` or `ODS`. The
 | 
			
		||||
NodeJS library includes similar methods to perform the download[^5]:
 | 
			
		||||
 | 
			
		||||
This example prints the worksheet names and CSV exports of each sheet.
 | 
			
		||||
| Format | Google Sheets Description | Method           |
 | 
			
		||||
|:-------|:--------------------------|:-----------------|
 | 
			
		||||
| XLSX   | Microsoft Excel (.xlsx)   | `downloadAsXLSX` |
 | 
			
		||||
| ODS    | OpenDocument (.ods)       | `downloadAsODS`  |
 | 
			
		||||
 | 
			
		||||
<Tabs>
 | 
			
		||||
  <TabItem value="xlsx" label="XLSX">
 | 
			
		||||
The functions resolve to `Buffer` data. The `Buffer` objects can be parsed using
 | 
			
		||||
the SheetJS `read`[^6] method:
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const XLSX = require("xlsx");
 | 
			
		||||
/* download XLSX */
 | 
			
		||||
const ab = await doc.downloadAsXLSX();
 | 
			
		||||
 | 
			
		||||
(async() => {
 | 
			
		||||
  /* Connect to Google Sheet */
 | 
			
		||||
  const ID = "<google sheet id>";
 | 
			
		||||
  const doc = await require("./common")(ID);
 | 
			
		||||
 | 
			
		||||
  /* Get file export */
 | 
			
		||||
  // highlight-next-line
 | 
			
		||||
  const buf = await doc.downloadAsXLSX();
 | 
			
		||||
 | 
			
		||||
  /* Parse with SheetJS */
 | 
			
		||||
  const wb = XLSX.read(buf);
 | 
			
		||||
 | 
			
		||||
  /* Loop over the worksheet names */
 | 
			
		||||
  wb.SheetNames.forEach(name => {
 | 
			
		||||
    /* Print the name to the console */
 | 
			
		||||
    console.log(name);
 | 
			
		||||
 | 
			
		||||
    /* Get the corresponding worksheet object */
 | 
			
		||||
    const sheet = wb.Sheets[name];
 | 
			
		||||
 | 
			
		||||
    /* Print a CSV export of the worksheet */
 | 
			
		||||
    console.log(XLSX.utils.sheet_to_csv(sheet));
 | 
			
		||||
  });
 | 
			
		||||
})();
 | 
			
		||||
/* parse */
 | 
			
		||||
const wb = XLSX.read(buf);
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
  </TabItem>
 | 
			
		||||
At this point `wb` is a SheetJS workbook object[^7].
 | 
			
		||||
 | 
			
		||||
  <TabItem value="ods" label="ODS">
 | 
			
		||||
## Complete Example
 | 
			
		||||
 | 
			
		||||
```js
 | 
			
		||||
const XLSX = require("xlsx");
 | 
			
		||||
:::note
 | 
			
		||||
 | 
			
		||||
(async() => {
 | 
			
		||||
  /* Connect to Google Sheet */
 | 
			
		||||
  const ID = "<google sheet id>";
 | 
			
		||||
  const doc = await require("./common")(ID);
 | 
			
		||||
This demo was last tested on 2023 September 17 using `google-auth-library` for
 | 
			
		||||
authentication (`v8.9.0`) and `google-spreadsheet` for API access (`v4.1.0`).
 | 
			
		||||
 | 
			
		||||
  /* Get file export */
 | 
			
		||||
  // highlight-next-line
 | 
			
		||||
  const buf = await doc.downloadAsODS();
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
  /* Parse with SheetJS */
 | 
			
		||||
  const wb = XLSX.read(buf);
 | 
			
		||||
### Account Setup
 | 
			
		||||
 | 
			
		||||
  /* Loop over the worksheet names */
 | 
			
		||||
  wb.SheetNames.forEach(name => {
 | 
			
		||||
    /* Print the name to the console */
 | 
			
		||||
    console.log(name);
 | 
			
		||||
0) Create a new Google account or log into an existing account.
 | 
			
		||||
 | 
			
		||||
    /* Get the corresponding worksheet object */
 | 
			
		||||
    const sheet = wb.Sheets[name];
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
    /* Print a CSV export of the worksheet */
 | 
			
		||||
    console.log(XLSX.utils.sheet_to_csv(sheet));
 | 
			
		||||
  });
 | 
			
		||||
})();
 | 
			
		||||
A valid phone number (for SMS verification) may be required.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
1) Open <https://console.cloud.google.com> in a web browser. Review the Google
 | 
			
		||||
Cloud Platform Terms of Service.
 | 
			
		||||
 | 
			
		||||
:::warning pass
 | 
			
		||||
 | 
			
		||||
You must agree to the Google Cloud Platform Terms of Service to use the APIs.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### Project Setup
 | 
			
		||||
 | 
			
		||||
2) Create a new Project.
 | 
			
		||||
 | 
			
		||||
If the account does not have an existing project, click "CREATE PROJECT"
 | 
			
		||||
 | 
			
		||||
If the account has an existing project, click the project selector (`:·` icon)
 | 
			
		||||
and click "NEW PROJECT" in the modal.
 | 
			
		||||
 | 
			
		||||
3) In the New Project screen, enter "SheetJS Test" in the Project name textbox
 | 
			
		||||
and select "No organization" in the Location box. Click "CREATE"
 | 
			
		||||
 | 
			
		||||
### API Setup
 | 
			
		||||
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
The goal of this section is to enable Google Sheets API and Google Drive API.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
4) Click the Project Selector (`:·` icon) and select "SheetJS Test"
 | 
			
		||||
 | 
			
		||||
5) In the left sidebar, click "Enabled APIs and services".
 | 
			
		||||
 | 
			
		||||
6) Near the top of the page, click "+ ENABLE APIS AND SERVICES".
 | 
			
		||||
 | 
			
		||||
7) In the search bar near the middle of the page, type "Sheets" and look for
 | 
			
		||||
"Google Sheets API". Click the card
 | 
			
		||||
 | 
			
		||||
8) In the Product Details screen, click the blue "ENABLE" button.
 | 
			
		||||
 | 
			
		||||
9) Click the left arrow (`<-`) next to API/Service details.
 | 
			
		||||
 | 
			
		||||
10) In the search bar near the middle of the page, type "Drive" and look for
 | 
			
		||||
"Google Drive API". Click the card.
 | 
			
		||||
 | 
			
		||||
11) In the Product Details screen, click the blue "ENABLE" button.
 | 
			
		||||
 | 
			
		||||
### Service Account Setup
 | 
			
		||||
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
The goal of this section is to create a service account and generate a JSON key.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
#### Create Service Account
 | 
			
		||||
 | 
			
		||||
12) Go to <https://console.cloud.google.com>.
 | 
			
		||||
 | 
			
		||||
13) Click the Project Selector (`:·` icon) and select "SheetJS Test".
 | 
			
		||||
 | 
			
		||||
14) Click "Dashboard".
 | 
			
		||||
 | 
			
		||||
15) In the left sidebar, hover over "APIs and Services" and select "Credentials"
 | 
			
		||||
 | 
			
		||||
16) Click "+ CREATE CREDENTIALS". In the dropdown, select "Service Account"
 | 
			
		||||
 | 
			
		||||
17) Enter "SheetJService" for Service account name. Click "CREATE AND CONTINUE"
 | 
			
		||||
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
The Service account ID is generated automatically.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
18) In Step 2 "Grant this service account access to project", click CONTINUE
 | 
			
		||||
 | 
			
		||||
19) In Step 3 click "DONE". You will be taken back to the credentials screen
 | 
			
		||||
 | 
			
		||||
#### Create JSON Key
 | 
			
		||||
 | 
			
		||||
20) Look for "SheetJService" in the "Service Accounts" table and click the email
 | 
			
		||||
address in the row
 | 
			
		||||
 | 
			
		||||
21) Click the email address of the account in the "Service Accounts" table.
 | 
			
		||||
 | 
			
		||||
22) Click "KEYS" in the horizontal bar near the top of the page.
 | 
			
		||||
 | 
			
		||||
23) Click "ADD KEY" and select "Create new key" in the dropdown.
 | 
			
		||||
 | 
			
		||||
24) In the popup, select the "JSON" radio button and click "CREATE". The page
 | 
			
		||||
will download a JSON file.
 | 
			
		||||
 | 
			
		||||
25) Click "CLOSE"
 | 
			
		||||
 | 
			
		||||
### Create Document
 | 
			
		||||
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
The goal of this section is to create a document from the service account and
 | 
			
		||||
share with the main account.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
26) Create a `SheetJSGS` folder and initialize:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
mkdir SheetJSGS
 | 
			
		||||
cd SheetJSGS
 | 
			
		||||
npm init -y
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
  </TabItem>
 | 
			
		||||
</Tabs>
 | 
			
		||||
27) Copy the JSON file from step 24 into the project folder.
 | 
			
		||||
 | 
			
		||||
28) Install dependencies:
 | 
			
		||||
 | 
			
		||||
<CodeBlock language="bash">{`\
 | 
			
		||||
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz google-spreadsheet google-auth-library`}
 | 
			
		||||
</CodeBlock>
 | 
			
		||||
 | 
			
		||||
29) Save the following script to `init.mjs`:
 | 
			
		||||
 | 
			
		||||
```js title="init.mjs"
 | 
			
		||||
import { JWT } from 'google-auth-library'
 | 
			
		||||
import { GoogleSpreadsheet } from 'google-spreadsheet';
 | 
			
		||||
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
 | 
			
		||||
 | 
			
		||||
const jwt = new JWT({
 | 
			
		||||
  email: creds.client_email,
 | 
			
		||||
  key: creds.private_key,
 | 
			
		||||
  scopes: [
 | 
			
		||||
    'https://www.googleapis.com/auth/spreadsheets',
 | 
			
		||||
    'https://www.googleapis.com/auth/drive.file',
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const doc = await GoogleSpreadsheet.createNewSpreadsheetDocument(jwt, { title: 'test from NodeJS' });
 | 
			
		||||
const newSheet = await doc.addSheet({ title: 'SheetJSTest' });
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
await doc.share('YOUR_ADDRESS@gmail.com');
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Edit the highlighted lines as follows:
 | 
			
		||||
 | 
			
		||||
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
 | 
			
		||||
JSON file in step 27. The `./` prefix is required!
 | 
			
		||||
 | 
			
		||||
- `'YOUR_ADDRESS@gmail.com'` should be replaced with the Google Account email
 | 
			
		||||
address from step 0.
 | 
			
		||||
 | 
			
		||||
30) Run the script:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
node init.mjs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
31) Sign into Google Sheets. A shared document "test from NodeJS" should be
 | 
			
		||||
displayed in the table. It will be owned by the service account.
 | 
			
		||||
 | 
			
		||||
32) Open the shared document from step 31.
 | 
			
		||||
 | 
			
		||||
33) Copy the URL and extract the document ID.
 | 
			
		||||
 | 
			
		||||
The URL of the document will look like
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
https://docs.google.com/spreadsheets/d/a_long_string_of_characters/edit#gid=0
 | 
			
		||||
---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^--- ID
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The ID is a long string of letters and numbers and underscore characters (`_`)
 | 
			
		||||
just before the `/edit` part of the URL.
 | 
			
		||||
 | 
			
		||||
### Load Data from NUMBERS
 | 
			
		||||
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
The goal of this section is to update the new document with data from a sample
 | 
			
		||||
NUMBERS file.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
34) Download the [test file `pres.numbers`](https://sheetjs.com/pres.numbers):
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
curl -LO https://sheetjs.com/pres.numbers
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
35) Save the following script to `load.mjs`:
 | 
			
		||||
 | 
			
		||||
```js title="load.mjs"
 | 
			
		||||
import { JWT } from 'google-auth-library'
 | 
			
		||||
import { GoogleSpreadsheet } from 'google-spreadsheet';
 | 
			
		||||
 | 
			
		||||
import { set_fs, readFile, utils } from 'xlsx';
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
set_fs(fs);
 | 
			
		||||
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
 | 
			
		||||
 | 
			
		||||
const jwt = new JWT({
 | 
			
		||||
  email: creds.client_email,
 | 
			
		||||
  key: creds.private_key,
 | 
			
		||||
  scopes: [
 | 
			
		||||
    'https://www.googleapis.com/auth/spreadsheets',
 | 
			
		||||
    'https://www.googleapis.com/auth/drive.file',
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
 | 
			
		||||
await doc.loadInfo();
 | 
			
		||||
 | 
			
		||||
const wb = readFile("pres.numbers");
 | 
			
		||||
 | 
			
		||||
/* clear workbook */
 | 
			
		||||
{
 | 
			
		||||
  /* delete all sheets after the first sheet */
 | 
			
		||||
  const old_sheets = doc.sheetsByIndex;
 | 
			
		||||
  for(let i = 1; i < old_sheets.length; ++i) {
 | 
			
		||||
    await old_sheets[i].delete();
 | 
			
		||||
  }
 | 
			
		||||
  /* clear first worksheet */
 | 
			
		||||
  old_sheets[0].clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* write worksheets */
 | 
			
		||||
{
 | 
			
		||||
  const name = wb.SheetNames[0];
 | 
			
		||||
  const ws = wb.Sheets[name];
 | 
			
		||||
  /* first worksheet already exists */
 | 
			
		||||
  const sheet = doc.sheetsByIndex[0];
 | 
			
		||||
 | 
			
		||||
  /* update worksheet name */
 | 
			
		||||
  await sheet.updateProperties({title: name});
 | 
			
		||||
 | 
			
		||||
  /* generate array of arrays from the first worksheet */
 | 
			
		||||
  const aoa = utils.sheet_to_json(ws, {header: 1});
 | 
			
		||||
 | 
			
		||||
  /* set document header row to first row of the AOA */
 | 
			
		||||
  await sheet.setHeaderRow(aoa[0])
 | 
			
		||||
 | 
			
		||||
  /* add the remaining rows */
 | 
			
		||||
  await sheet.addRows(aoa.slice(1));
 | 
			
		||||
 | 
			
		||||
  /* the other worksheets must be created manually */
 | 
			
		||||
  for(let i = 1; i < wb.SheetNames.length; ++i) {
 | 
			
		||||
    const name = wb.SheetNames[i];
 | 
			
		||||
    const ws = wb.Sheets[name];
 | 
			
		||||
 | 
			
		||||
    const sheet = await doc.addSheet({title: name});
 | 
			
		||||
    const aoa = utils.sheet_to_json(ws, {header: 1});
 | 
			
		||||
    await sheet.setHeaderRow(aoa[0])
 | 
			
		||||
    await sheet.addRows(aoa.slice(1));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Edit the highlighted lines as follows:
 | 
			
		||||
 | 
			
		||||
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
 | 
			
		||||
JSON file in step 27. The `./` prefix is required!
 | 
			
		||||
 | 
			
		||||
- `'DOCUMENT_ID'` should be replaced with the Document ID from step 33.
 | 
			
		||||
 | 
			
		||||
36) Run the script:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
node load.mjs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
37) Sign into Google Sheets and open the "test from NodeJS" shared document. It
 | 
			
		||||
should show a list of Presidents, matching the contents of the test file.
 | 
			
		||||
 | 
			
		||||
### Export Data to XLSB
 | 
			
		||||
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
The goal of this section is to export the raw data from Google Sheets to XLSB.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
38) Save the following script to `dump.mjs`:
 | 
			
		||||
 | 
			
		||||
```js title="dump.mjs"
 | 
			
		||||
import { JWT } from 'google-auth-library'
 | 
			
		||||
import { GoogleSpreadsheet } from 'google-spreadsheet';
 | 
			
		||||
 | 
			
		||||
import { set_fs, writeFile, utils } from 'xlsx';
 | 
			
		||||
import * as fs from 'fs';
 | 
			
		||||
set_fs(fs);
 | 
			
		||||
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
 | 
			
		||||
 | 
			
		||||
const jwt = new JWT({
 | 
			
		||||
  email: creds.client_email,
 | 
			
		||||
  key: creds.private_key,
 | 
			
		||||
  scopes: [
 | 
			
		||||
    'https://www.googleapis.com/auth/spreadsheets',
 | 
			
		||||
    'https://www.googleapis.com/auth/drive.file',
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
 | 
			
		||||
await doc.loadInfo();
 | 
			
		||||
 | 
			
		||||
const wb = utils.book_new();
 | 
			
		||||
 | 
			
		||||
for(let i = 0; i < doc.sheetsByIndex.length; ++i) {
 | 
			
		||||
  const sheet = doc.sheetsByIndex[i];
 | 
			
		||||
  const name = sheet.title;
 | 
			
		||||
 | 
			
		||||
  /* get the header and data rows */
 | 
			
		||||
  await sheet.loadHeaderRow();
 | 
			
		||||
  const header = sheet.headerValues;
 | 
			
		||||
  const rows = await sheet.getRows();
 | 
			
		||||
  const aoa = [header].concat(rows.map(r => r._rawData));
 | 
			
		||||
 | 
			
		||||
  /* generate a SheetJS Worksheet */
 | 
			
		||||
  const ws = utils.aoa_to_sheet(aoa);
 | 
			
		||||
 | 
			
		||||
  /* add to workbook */
 | 
			
		||||
  utils.book_append_sheet(wb, ws, name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* write to SheetJS.xlsb */
 | 
			
		||||
writeFile(wb, "SheetJS.xlsb");
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Edit the highlighted lines as follows:
 | 
			
		||||
 | 
			
		||||
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
 | 
			
		||||
JSON file in step 27. The `./` prefix is required!
 | 
			
		||||
 | 
			
		||||
- `'DOCUMENT_ID'` should be replaced with the Document ID from step 33.
 | 
			
		||||
 | 
			
		||||
39) Run the script:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
node load.mjs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The script should create a file `SheetJS.xlsb` in the project folder. This file
 | 
			
		||||
can be opened in Excel
 | 
			
		||||
 | 
			
		||||
40) Sign into Google Sheets and open the "test from NodeJS" shared document. It
 | 
			
		||||
should show a list of Presidents, matching the contents of the test file.
 | 
			
		||||
 | 
			
		||||
### Export Raw Files
 | 
			
		||||
 | 
			
		||||
:::info pass
 | 
			
		||||
 | 
			
		||||
The goal of this section is to parse the Google Sheets XLSX export and generate
 | 
			
		||||
CSV files for each worksheet.
 | 
			
		||||
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
41) Sign into Google Sheets and open the "test from NodeJS" shared document.
 | 
			
		||||
 | 
			
		||||
42) Click the Plus (`+`) icon in the lower left corner to create a new worksheet.
 | 
			
		||||
 | 
			
		||||
43) In the new worksheet, set cell A1 to the formula `=SEQUENCE(3,5)`. This will
 | 
			
		||||
assign a grid of values
 | 
			
		||||
 | 
			
		||||
44) Save the following script to `raw.mjs`:
 | 
			
		||||
 | 
			
		||||
```js title="raw.mjs"
 | 
			
		||||
import { JWT } from 'google-auth-library'
 | 
			
		||||
import { GoogleSpreadsheet } from 'google-spreadsheet';
 | 
			
		||||
 | 
			
		||||
import { read, utils } from 'xlsx';
 | 
			
		||||
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
import creds from './sheetjs-test-726272627262.json' assert { type: "json" };
 | 
			
		||||
 | 
			
		||||
const jwt = new JWT({
 | 
			
		||||
  email: creds.client_email,
 | 
			
		||||
  key: creds.private_key,
 | 
			
		||||
  scopes: [
 | 
			
		||||
    'https://www.googleapis.com/auth/spreadsheets',
 | 
			
		||||
    'https://www.googleapis.com/auth/drive.file',
 | 
			
		||||
  ]
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// highlight-next-line
 | 
			
		||||
const doc = new GoogleSpreadsheet('DOCUMENT_ID', jwt);
 | 
			
		||||
await doc.loadInfo();
 | 
			
		||||
 | 
			
		||||
const buf = await doc.downloadAsXLSX();
 | 
			
		||||
 | 
			
		||||
/* Parse with SheetJS */
 | 
			
		||||
const wb = read(buf);
 | 
			
		||||
 | 
			
		||||
/* Loop over the worksheet names */
 | 
			
		||||
wb.SheetNames.forEach(name => {
 | 
			
		||||
  /* Print the name to the console */
 | 
			
		||||
  console.log(name);
 | 
			
		||||
 | 
			
		||||
  /* Get the corresponding worksheet object */
 | 
			
		||||
  const sheet = wb.Sheets[name];
 | 
			
		||||
 | 
			
		||||
  /* Print a CSV export of the worksheet */
 | 
			
		||||
  console.log(utils.sheet_to_csv(sheet));
 | 
			
		||||
});
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Edit the highlighted lines as follows:
 | 
			
		||||
 | 
			
		||||
- `'./sheetjs-test-726272627262.json'` should be replaced with the name of the
 | 
			
		||||
JSON file in step 27. The `./` prefix is required!
 | 
			
		||||
 | 
			
		||||
- `'DOCUMENT_ID'` should be replaced with the Document ID from step 33.
 | 
			
		||||
 | 
			
		||||
45) Run the script:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
node raw.mjs
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The script will display the sheet names and CSV rows from both worksheets.
 | 
			
		||||
 | 
			
		||||
[^1]: The package name is [`google-auth-library`](https://www.npmjs.com/package/google-auth-library)
 | 
			
		||||
[^2]: The project name is [`node-google-spreadsheet`](https://github.com/theoephraim/node-google-spreadsheet) but the module name is `google-spreadsheet`.
 | 
			
		||||
[^3]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
 | 
			
		||||
[^4]: See [`sheet_to_json` in "Utilities"](/docs/api/utilities/array#array-output)
 | 
			
		||||
[^5]: See ["Exporting Data"](https://theoephraim.github.io/node-google-spreadsheet/#/guides/exports) in the `node-google-spreadsheet` documentation
 | 
			
		||||
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
 | 
			
		||||
[^7]: See ["Workbook Object"](/docs/csf/book) for a description of the workbook object or ["API Reference"](/docs/api) for various methods to work with workbook and sheet objects.
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ pagination_next: demos/bigdata/index
 | 
			
		||||
import current from '/version.js';
 | 
			
		||||
import CodeBlock from '@theme/CodeBlock';
 | 
			
		||||
 | 
			
		||||
:::note
 | 
			
		||||
:::note pass
 | 
			
		||||
 | 
			
		||||
This demo focuses on Google Apps Script custom functions.  For external data
 | 
			
		||||
processing, [the "Google Sheets" cloud data demo](/docs/demos/cloud/gsheet)
 | 
			
		||||
@ -150,7 +150,7 @@ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js`}
 | 
			
		||||
npx @google/clasp push
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
:::caution
 | 
			
		||||
:::caution pass
 | 
			
		||||
 | 
			
		||||
If the Google Apps Script API is not enabled, the command will display an object
 | 
			
		||||
with `code: 403` and an error message about the Apps Script API:
 | 
			
		||||
 | 
			
		||||
@ -59,7 +59,7 @@ NodeJS push streams were introduced in 2012. The text streaming methods `to_csv`
 | 
			
		||||
and `to_html` are supported in NodeJS v0.10 and later while the object streaming
 | 
			
		||||
method `to_json` is supported in NodeJS v0.12 and later.
 | 
			
		||||
 | 
			
		||||
The first streaming write function, `to_csv`, was introduced in April 2017.  It
 | 
			
		||||
The first streaming write function, `to_csv`, was introduced in early 2017. It
 | 
			
		||||
used and still uses the same NodeJS streaming API.
 | 
			
		||||
 | 
			
		||||
Years later, browser vendors are settling on a different stream API.
 | 
			
		||||
 | 
			
		||||
@ -214,6 +214,7 @@ const config = {
 | 
			
		||||
        { from: '/docs/demos/clipboard', to: '/docs/demos/local/clipboard/' },
 | 
			
		||||
        { from: '/docs/demos/localfile', to: '/docs/demos/local/file/' },
 | 
			
		||||
        { from: '/docs/demos/data/indexeddb', to: '/docs/demos/local/indexeddb/' },
 | 
			
		||||
        { from: '/docs/demos/data/storageapi', to: '/docs/demos/local/storageapi/' },
 | 
			
		||||
        /* desktop */
 | 
			
		||||
        { from: '/docs/demos/cli', to: '/docs/demos/desktop/cli/' },
 | 
			
		||||
        { from: '/docs/getting-started/demos/cli', to: '/docs/demos/desktop/cli/' },
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 103 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 72 KiB  | 
		Loading…
	
		Reference in New Issue
	
	Block a user