Better Bookmarks with Notions and IOS Shortcuts
🔖

Better Bookmarks with Notions and IOS Shortcuts

Tags
Notion
IOS Shortcuts
Node.js
Express
Tech
Published
December 19, 2022
Author
Clark Weckmann
notion image
 
Are you tired of cluttered and disorganized bookmarks? Look no further, as this article will show you how to streamline your bookmark management process using Notion and iOS Shortcuts.

Use Case

Saving a website’s URL, Open Graph Image (or screenshot), and appending this data to a Notion Database.
 

Building the API

If you’re feeling lazy or just want to play around skip to the end.
To kick this article off we need to build the API first. Our tech stack for the API is as follows:

Our Tech Stack

  • Express
    • For creating our endpoint and executing code when that endpoint is called.
  • Cloudinary
    • For saving our website preview images once we fetch or generate them.
  • Vercel
    • For hosting our API. Vercel's edge network is a powerful tool for web developers and can help them deliver fast and reliable applications to their users.

Getting Started

To start this project we’ll be creating an NPM project. Navigate to your project’s directory and run npm init.
Create index.js and let’s get to coding.
Our project requires Express, Axios, and Cloudinary V2. Let’s add these dependencies to our index.js file.
const express = require("express"); const app = express(); // initialize express const axios = require("axios"); const cloudinary = require("cloudinary").v2;
Install these dependencies with the -s flag to save them to our package.json
npm install -s express axios cloudinary

Defining our Endpoint

We’re going to use the base URL for access in this case. All of our API calls will be sent to https://YOUR-VERCEL-INSTANCE.vercel.app/
app.get("/", (req, res) => { const url = req.query.url; const api = "https://jsonlink.io/api/extract?url="; // A handy tool you should check out! // Geting Opengraph information from jsonlink axios .get(api + url) .then((response) => { let data = { title: response.data.title ? response.data.title : "No Title", // Getting the title url: url, description: response.data.description ? response.data.description // Geting the description : "none", image: response.data.images[0] ? response.data.images[0] : "https://s.soup.cool/api?url=" + url, // If there is no image we'll grab a screenshot }; // Remember to set your Cloudinary values cloudinary.config({ cloud_name: process.env.CLOUD_NAME, api_key: process.env.API_KEY, api_secret: process.env.API_SECRET, }); // Upload the image URL to Cloudinary in the "bookmarks' folder cloudinary.uploader.upload(data.image,{folder: "bookmarks"}, function (error, result) { if (error) { // If we can't upload to Cloudinary for some reason return the original URL shared. let returnData = { image: data.url, description: data.description, title: data.title, url: data.url }; res.setHeader("Content-Type", "application/json"); res.send(JSON.stringify(returnData)); } else { // Return the hosted image URL. let returnData = { image: result.url, description: data.description, title: data.title, url: data.url }; res.setHeader("Content-Type", "application/json"); res.send(JSON.stringify(returnData)); } }); }) .catch((error) => { res.send(error); }); });

Testing our Code

Let’s test out our code quickly to make sure it’s all up and working. At the bottom of our index.js file specify a port for Express to listen on. Make sure you grab your Cloudinary API keys from https://cloudinary.com/developers.
app.listen(3000, () => { // using port 3000 console.log(`Testing at http://localhost:3000/?url=https://clark.today`); });
Save your code and execute node index.js in your terminal. Visit the URL outputted in your browser and you should see something like this.
{ "image": "http://res.cloudinary.com/clark-host/image/upload/v1671475454/bookmarks/lky8gfz3tc1zlnwnh9sl.jpg", "description": "I'm a DevOps engineer located on Earth, for now. Feel free to ask me questions about any of my projects listed here or anywhere else you find me!", "title": "Clark Weckmann", "url": "https://clark.today" }
If you see a similar output, we’re almost ready to push it to Vercel.
Before deploying your API I recommend securing somehow, one of the simplest ways is checking for a query value to match an environmental variable./
// If wrong key or no key don't allow access. if (req.query.key != process.env.ACCESS_TOKEN || !req.query.key) { res.send("wrong key"); return; }
You can add the above snippet inside our endpoint.
Stop your API (ctrl + c) and run vercel in your terminal. You may have to log in or sign up for Vercel.

Deploying to Vercel

Vercel’s CLI will ask you a couple of questions.
  • Setup and deploy? Y
  • Link an existing project? N
  • In which directory is your code located ./
The default options are what we’ll be using, you can just hit Enter for each question.
 
You should see an output similar to this:
/ Setting up project > Upload [===-----------------] 15% 136.6s - Build Command: `npm run vercel-build` or `npm run build` - Output Directory: `public` if it exists, or `.` - Development Command: None ? Want to override the settings? [y/N]
Again, just hit Enter or enter N
🔗 Linked to user/project (created .vercel and added it to .gitignore) 🔍 Inspect: https://vercel.com/<user>/<project>/<ID> [1s] ✅ Production: https://<project>.vercel.app [copied to clipboard] [7s] 📝 Deployed to production. Run `vercel --prod` to overwrite later (https://vercel.link/2F). 💡 To change the domain or build command, go to https://vercel.com/<user>/<project>/settings
Before we start with the IOS Shortcut, let’s test our API one more time. Visit the last link printed in your console to access your project settings. Navigate over to your project's Environment Variables.
notion image
Add your CLOUD_NAME, API_KEY, API_SECRET, and your ACCESS_TOKEN we’re using to prevent unauthorized use of our API.
Let’s test one more time now, navigate to your project’s domain https://<project>.vercel.app and add in our two queries. ?url=https://someurl.com&key=ACCESS_TOKEN You should get a JSON output containing the URL’s image, description, title, and original URL.

Building the IOS Shortcut

notion image
Our flow for the IOS Shortcut is pretty simple, check it out.
Share SheetGet Contents of URL(API_URL)Set Variable URL to Contents of URL Get Diction from URL Add Page(Dictionary) To Database Nautomate
If you’re unfamiliar with Nautomate I recommend checking out their website.
You can download the completed shortcut here:
If you have trouble with Apple Shortcuts links, you can also download it from here.

Skip The Coding

Click the link above to deploy directly to Vercel and initialize a Git repository with the code ready to roll.