-
Notifications
You must be signed in to change notification settings - Fork 586
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add webform demo app to /examples (#437)
* add fullstack webform example app * update package.json detail * make linter happy * delete old folder (name change * Update examples/web-form-with-express/public/client.js Co-authored-by: Ron Yehoshua <[email protected]> * update var naming for improved readbility * add console back to match appendApiResponse() --------- Co-authored-by: Ron Yehoshua <[email protected]>
- Loading branch information
1 parent
f2845b3
commit b71ec28
Showing
12 changed files
with
603 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
NOTION_KEY=<your-notion-api-key> | ||
NOTION_PAGE_ID=<notion-page-id> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
# Notion internal integration full-stack example | ||
|
||
## Use web forms to create new databases, pages, page content, and comments | ||
|
||
### About the integration | ||
|
||
This demo shows how to build an internal integration that allows users to fill out a web form to create new Notion databases, pages, blocks (page content), and comments. | ||
|
||
![Database form submitted successfully](./public/assets/home-screen.png) | ||
|
||
This demo is referenced in the [Create an integration guide](https://developers.notion.com/docs/create-a-notion-integration) -- an introductory guide to building internal integrations and working with Notion's public API. | ||
|
||
The goal of this integration is to show how to build a full-stack app where user interactions on the frontend will trigger public API requests, and, as a result, make the corresponding updates in your Notion workspace. | ||
|
||
### File structure | ||
|
||
On the frontend, this demo includes: | ||
|
||
- `views/index.html`, which represents the app's webpage content. Users will interact with the HTML elements in this page. | ||
- `public/client.js`, the client-side JavaScript added to handle HTML form `submit` events. | ||
- `public/style.css` contains the styles for `views/index.html`. | ||
|
||
On the backend, this demo includes: | ||
|
||
- `server.js`, which serves `index.html` and defines the endpoints used in the client-side JS code. All Notion public API usage (Notion SDK for JavaScript) is included in this file. | ||
|
||
#### Notion endpoints used | ||
|
||
This demo includes the following Notion endpoint usage: | ||
|
||
- [Create a database](https://developers.notion.com/reference/create-a-database) | ||
- [Create a page](https://developers.notion.com/reference/post-page) | ||
- [Append block children](https://developers.notion.com/reference/patch-block-children) | ||
- [Create a comment](https://developers.notion.com/reference/create-a-comment) | ||
|
||
This demo can be expanded further to test other endpoints, as well. For example, you could add a button retrieve all database pages or to delete existing pages. | ||
|
||
Some "real-world" applications include expanding this demo to be a blog and using a Notion workspace as a CMS. Additionally, the functionality could also be repurposed to receive app feedback from users. | ||
|
||
--- | ||
|
||
### Running locally | ||
|
||
#### 1. Set up your local project | ||
|
||
```zsh | ||
# Clone this repository locally | ||
git clone https://github.com/makenotion/notion-sdk-js.git | ||
|
||
# Switch into this project | ||
cd notion-sdk-js/examples/web-form-with-express/ | ||
|
||
# Install the dependencies | ||
npm install | ||
``` | ||
|
||
#### 2. Set your environment variables in a `.env` file | ||
|
||
A `.env.example` file has been included and can be renamed `.env`. Update the environment variables below: | ||
|
||
```zsh | ||
NOTION_KEY=<your-notion-api-key> | ||
NOTION_PAGE_ID=<notion-page-id> | ||
``` | ||
|
||
`NOTION_KEY`: Create a new integration in the [integrations dashboard](https://www.notion.com/my-integrations) and retrieve the API key from the integration's `Secrets` page. | ||
|
||
`NOTION_PAGE_ID`: Use the ID of any Notion page you want to add databases to. This page will be the parent of all content created through this integration. | ||
|
||
The page ID is the 32 character string at the end of any page URL. | ||
![A Notion page URL with the ID highlighted](./public/assets//page_id.png) | ||
|
||
#### 3. Give the integration access to your page | ||
|
||
Your Notion integration will need permission to create new databases, etc. To provide access, do the following: | ||
|
||
1. Go to the page in your workspace. | ||
2. Click the `•••` (more menu) on the top-right corner of the page. | ||
3. Scroll to the bottom of the menu and click `Add connections`. | ||
4. Search for and select your integration in the `Search for connections...` menu. | ||
|
||
Once selected, your integration will have permission to read/write content on the page. | ||
|
||
**Note**: For the `Add a comment` form to work, you must give your integration permission to read/write comments. To update integration capabilities, visit the `Capabilities` tab in the [integrations dashboard](https://www.notion.com/my-integrations). | ||
|
||
#### 4. Run code | ||
|
||
Run the following command: | ||
|
||
```zsh | ||
node server.js | ||
``` | ||
|
||
Check the terminal response to see which port to use when viewing the app locally in your browser of choice (`localhost:<port>`). | ||
|
||
Keep the browser console open to see API responses, including errors. | ||
|
||
### Using this app | ||
|
||
To use this demo app, start by creating a new database via the database form: | ||
![Database form UI](./public/assets/home-screen.png) | ||
|
||
The ID of the new database can be used in the next form to create a new page: | ||
![Page form UI](./public/assets/page-form.png) | ||
|
||
The blocks and comment forms will accept the page ID that is returned from the page form to create new page content (blocks) and comments. | ||
![Blocks form UI](./public/assets/blocks-form.png) | ||
![Comments form UI](./public/assets/comment-form.png) | ||
|
||
If you have the IDs for other databases/pages, you can use them as long as you have shared the target databases/pages with the internal integration. | ||
|
||
To learn more about this demo, read the [Create an integration guide](https://developers.notion.com/docs/create-a-notion-integration), which steps through how this code works. | ||
|
||
(Thanks to [Glitch](https://glitch.com/) for the starter app used while creating this demo!) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"name": "web-form-with-express-example", | ||
"version": "1.0.0", | ||
"description": "Internal Notion intergration demo app using Express.js for a server.", | ||
"main": "server.js", | ||
"scripts": { | ||
"start": "node server.js" | ||
}, | ||
"dependencies": { | ||
"@notionhq/client": "^2.2.7", | ||
"dotenv": "^16.3.1", | ||
"express": "^4.18.2" | ||
}, | ||
"engines": { | ||
"node": "14.x" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/makenotion/notion-sdk-js.git" | ||
}, | ||
"author": "Jess Mitchell", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/makenotion/notion-sdk-js/issues" | ||
}, | ||
"keywords": [ | ||
"node", | ||
"notion", | ||
"express" | ||
] | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
// This file is run by the browser each time your view template is loaded | ||
|
||
/** | ||
* Define variables that reference elements included in /views/index.html: | ||
*/ | ||
|
||
// Forms | ||
const dbForm = document.getElementById("databaseForm") | ||
const pageForm = document.getElementById("pageForm") | ||
const blocksForm = document.getElementById("blocksForm") | ||
const commentForm = document.getElementById("commentForm") | ||
|
||
// Table cells where API responses will be appended | ||
const dbResponseEl = document.getElementById("dbResponse") | ||
const pageResponseEl = document.getElementById("pageResponse") | ||
const blocksResponseEl = document.getElementById("blocksResponse") | ||
const commentResponseEl = document.getElementById("commentResponse") | ||
|
||
/** | ||
* Functions to handle appending new content to /views/index.html | ||
*/ | ||
|
||
// Appends the API response to the UI | ||
const appendApiResponse = function (apiResponse, el) { | ||
console.log(apiResponse) | ||
|
||
// Add success message to UI | ||
const newParagraphSuccessMsg = document.createElement("p") | ||
newParagraphSuccessMsg.innerHTML = "Result: " + apiResponse.message | ||
el.appendChild(newParagraphSuccessMsg) | ||
// See browser console for more information | ||
if (apiResponse.message === "error") return | ||
|
||
// Add ID of Notion item (db, page, comment) to UI | ||
const newParagraphId = document.createElement("p") | ||
newParagraphId.innerHTML = "ID: " + apiResponse.data.id | ||
el.appendChild(newParagraphId) | ||
|
||
// Add URL of Notion item (db, page) to UI | ||
if (apiResponse.data.url) { | ||
const newAnchorTag = document.createElement("a") | ||
newAnchorTag.setAttribute("href", apiResponse.data.url) | ||
newAnchorTag.innerText = apiResponse.data.url | ||
el.appendChild(newAnchorTag) | ||
} | ||
} | ||
|
||
// Appends the blocks API response to the UI | ||
const appendBlocksResponse = function (apiResponse, el) { | ||
console.log(apiResponse) | ||
|
||
// Add success message to UI | ||
const newParagraphSuccessMsg = document.createElement("p") | ||
newParagraphSuccessMsg.innerHTML = "Result: " + apiResponse.message | ||
el.appendChild(newParagraphSuccessMsg) | ||
|
||
// Add block ID to UI | ||
const newParagraphId = document.createElement("p") | ||
newParagraphId.innerHTML = "ID: " + apiResponse.data.results[0].id | ||
el.appendChild(newParagraphId) | ||
} | ||
|
||
/** | ||
* Attach submit event handlers to each form included in /views/index.html | ||
*/ | ||
|
||
// Attach submit event to each form | ||
dbForm.onsubmit = async function (event) { | ||
event.preventDefault() | ||
|
||
const dbName = event.target.dbName.value | ||
const body = JSON.stringify({ dbName }) | ||
|
||
const newDBResponse = await fetch("/databases", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body, | ||
}) | ||
const newDBData = await newDBResponse.json() | ||
|
||
appendApiResponse(newDBData, dbResponseEl) | ||
} | ||
|
||
pageForm.onsubmit = async function (event) { | ||
event.preventDefault() | ||
|
||
const dbID = event.target.newPageDB.value | ||
const pageName = event.target.newPageName.value | ||
const header = event.target.header.value | ||
const body = JSON.stringify({ dbID, pageName, header }) | ||
|
||
const newPageResponse = await fetch("/pages", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body, | ||
}) | ||
|
||
const newPageData = await newPageResponse.json() | ||
appendApiResponse(newPageData, pageResponseEl) | ||
} | ||
|
||
blocksForm.onsubmit = async function (event) { | ||
event.preventDefault() | ||
|
||
const pageID = event.target.pageID.value | ||
const content = event.target.content.value | ||
const body = JSON.stringify({ pageID, content }) | ||
|
||
const newBlockResponse = await fetch("/blocks", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body, | ||
}) | ||
|
||
const newBlockData = await newBlockResponse.json() | ||
appendBlocksResponse(newBlockData, blocksResponseEl) | ||
} | ||
|
||
commentForm.onsubmit = async function (event) { | ||
event.preventDefault() | ||
|
||
const pageID = event.target.pageIDComment.value | ||
const comment = event.target.comment.value | ||
const body = JSON.stringify({ pageID, comment }) | ||
|
||
const newCommentResponse = await fetch("/comments", { | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
}, | ||
body, | ||
}) | ||
|
||
const newCommentData = await newCommentResponse.json() | ||
appendApiResponse(newCommentData, commentResponseEl) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/* this file is loaded by index.html and styles the page */ | ||
|
||
* { | ||
box-sizing: border-box; | ||
} | ||
|
||
body { | ||
font-family: sans-serif; | ||
margin: 2em 1em; | ||
line-height: 1.5em; | ||
} | ||
|
||
h1 { | ||
color: #7171d1; | ||
max-width: calc(100% - 5rem); | ||
line-height: 1.1; | ||
} | ||
|
||
table { | ||
width: 100%; | ||
} | ||
|
||
th { | ||
text-align: left; | ||
} | ||
|
||
td { | ||
width: 50%; | ||
word-break: break-all; | ||
} | ||
|
||
form { | ||
background-color: #eee; | ||
display: grid; | ||
grid-gap: 1em; | ||
padding: 1em; | ||
max-width: 40ch; | ||
} | ||
|
||
input { | ||
border: 1px solid silver; | ||
display: block; | ||
font-size: 16px; | ||
margin-bottom: 10px; | ||
padding: 5px; | ||
width: 100%; | ||
} | ||
|
||
form input[type="submit"] { | ||
background-color: #bbbbf2; | ||
border: 2px solid currentColor; | ||
border-radius: 0.25em; | ||
cursor: pointer; | ||
font-size: inherit; | ||
line-height: 1.4em; | ||
padding: 0.25em 1em; | ||
max-width: 20ch; | ||
cursor: pointer; | ||
margin-left: auto; | ||
} | ||
|
||
form input[type="submit"]:hover { | ||
background-color: #9292d3; | ||
} | ||
|
||
form input[type="submit"]:active { | ||
background-color: #b2b2d5; | ||
} | ||
|
||
footer { | ||
margin-top: 3em; | ||
padding-top: 1.5em; | ||
border-top: 1px solid lightgrey; | ||
} |
Oops, something went wrong.