{"id":72194,"date":"2019-10-25T04:19:27","date_gmt":"2019-10-25T11:19:27","guid":{"rendered":"http:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/"},"modified":"2019-10-25T04:19:27","modified_gmt":"2019-10-25T11:19:27","slug":"create-a-bookmarking-application-with-faunadb-netlify-and-11ty","status":"publish","type":"post","link":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/","title":{"rendered":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty"},"content":{"rendered":"<link rel=\"canonical\" href=\"https:\/\/www.smashingmagazine.com\/2019\/10\/bookmarking-application-faunadb-netlify-11ty\/\"><title>Create A Bookmarking Application With FaunaDB, Netlify And 11ty<\/title><\/p>\n<article>\n<header>\n<h1>Create A Bookmarking Application With FaunaDB, Netlify And 11ty<\/h1>\n<address>Bryan Robinson<\/address>\n<p>                  <time datetime=\"2019-10-24T13:30:59+02:00\">2019-10-24T13:30:59+02:00<\/time><time datetime=\"2019-10-25T11:07:01+00:00\">2019-10-25T11:07:01+00:00<\/time><\/header>\n<p>The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your data as flat files: Markdown, YAML, JSON, HTML, and so on. Sometimes, managing data this way can be overly complicated. Sometimes, we still need a database.<\/p>\n<p>With that in mind, Netlify \u2014 a static site host and FaunaDB \u2014 a serverless cloud database \u2014 collaborated to make combining both systems easier.\u00a0<\/p>\n<h3>Why A Bookmarking Site?<\/h3>\n<p>The JAMstack is great for many professional uses, but one of my favorite aspects of this set of technology is its low barrier to entry for personal tools and projects.<\/p>\n<p>There are plenty of good products on the market for most applications I could come up with, but none would be exactly set up for me. None would give me full control over my content. None would come without a cost (monetary or informational).<\/p>\n<p>With that in mind, we can create our own mini-services using JAMstack methods. In this case, we\u2019ll be creating a site to store and publish any interesting articles I come across in my daily technology reading.<\/p>\n<p>I spend a lot of time reading articles that have been shared on Twitter. When I like one, I hit the \u201cheart\u201d icon. Then, within a few days, it\u2019s nearly impossible to find with the influx of new favorites. I want to build something as close to the ease of the \u201cheart,\u201d but that I own and control.<\/p>\n<p>How are we going to do that? I\u2019m glad you asked.<\/p>\n<blockquote><p>Interested in getting the code? You can <a href=\"https:\/\/github.com\/brob\/netlify-fauna-bookmarks\">grab it on Github<\/a> or just deploy straight to Netlify from that repository! Take a look at the <a href=\"https:\/\/fauna-bookmarks.netlify.com\/\">finished product here<\/a>.<\/p><\/blockquote>\n<h3>Our Technologies<\/h3>\n<h4>Hosting And Serverless Functions: Netlify<\/h4>\n<p>For hosting and serverless functions, we\u2019ll be utilizing Netlify. As an added bonus, with the new collaboration mentioned above, Netlify\u2019s CLI \u2014 \u201cNetlify Dev\u201d \u2014 will automatically connect to FaunaDB and store our API keys as environment variables.<\/p>\n<h4>Database: FaunaDB<\/h4>\n<p>FaunaDB is a \u201cserverless\u201d NoSQL database. We\u2019ll be using it to store our bookmarks data.<\/p>\n<h4>Static Site Generator: 11ty<\/h4>\n<p>I\u2019m a big believer in HTML. Because of this, the tutorial won\u2019t be using front-end JavaScript to render our bookmarks. Instead, we\u2019ll utilize 11ty as a static site generator. 11ty has built-in data functionality that makes fetching data from an API as easy as writing a couple of short JavaScript functions.<\/p>\n<h4>iOS Shortcuts<\/h4>\n<p>We\u2019ll need an easy way to post data to our database. In this case, we\u2019ll use iOS\u2019s Shortcuts app. This could be converted to an Android or desktop JavaScript bookmarklet, as well.<\/p>\n<div data-component=\"FeaturePanel\" data-audience=\"non-subscriber\" data-remove=\"true\"><\/div>\n<h3>Setting Up FaunaDB Via Netlify Dev<\/h3>\n<p>Whether you have already signed up for FaunaDB or you need to create a new account, the easiest way to set up a link between FaunaDB and Netlify is via Netlify\u2019s CLI: Netlify Dev. You can find full instructions <a href=\"https:\/\/docs.fauna.com\/fauna\/current\/start\/netlify\">from FaunaDB here<\/a> or follow along below.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/167ab831-afbf-4c2e-9242-db2547709b3d\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg?ssl=1\"><\/p>\n<p>    <img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/167ab831-afbf-4c2e-9242-db2547709b3d\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/167ab831-afbf-4c2e-9242-db2547709b3d\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/167ab831-afbf-4c2e-9242-db2547709b3d\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/167ab831-afbf-4c2e-9242-db2547709b3d\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg?w=900\" sizes=\"100vw\" alt=\"Netlify Dev running in the final project with our environment variable names showing\"><\/a><figcaption>\n      Netlify Dev running in the final project with our environment variable names showing (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/167ab831-afbf-4c2e-9242-db2547709b3d\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<p>If you don\u2019t already have this installed, you can run the following command in Terminal:<\/p>\n<pre><code>npm install netlify-cli -g<\/code><\/pre>\n<p>From within your project directory, run through the following commands:<\/p>\n<div>\n<pre><code>netlify init \/\/ This will connect your project to a Netlify project\n\nnetlify addons:create fauna \/\/ This will install the FaunaDB \"addon\"\n\nnetlify addons:auth fauna \/\/ This command will run you through connecting your account or setting up an account\n<\/code><\/pre>\n<\/div>\n<p>Once this is all connected, you can run <code>netlify dev<\/code> in your project. This will run any build scripts we set up, but also connect to the Netlify and FaunaDB services and grab any necessary environment variables. Handy!<\/p>\n<h4>Creating Our First Data<\/h4>\n<p>From here, we\u2019ll log into FaunaDB and create our first data set.\u00a0We\u2019ll start by creating a new Database called \u201cbookmarks.\u201d Inside a Database, we have Collections, Documents and Indexes.<\/p>\n<figure><a href=\"https:\/\/i0.wp.com\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/ec14f2d9-0fa8-4245-bfaf-fed66229a540\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg?ssl=1\"><\/p>\n<p>    <img data-recalc-dims=\"1\" decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/ec14f2d9-0fa8-4245-bfaf-fed66229a540\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/ec14f2d9-0fa8-4245-bfaf-fed66229a540\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/ec14f2d9-0fa8-4245-bfaf-fed66229a540\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/ec14f2d9-0fa8-4245-bfaf-fed66229a540\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg 2000w\" src=\"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg?w=900\" sizes=\"100vw\" alt=\"A screenshot of the FaunaDB console with data\"><\/a><figcaption>\n      A screenshot of the FaunaDB console with data (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/ec14f2d9-0fa8-4245-bfaf-fed66229a540\/bookmarking-application-faunadb-netlify-11ty-fauna-console-with-links.jpg\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<p>A Collection is a categorized group of data. Each piece of data takes the form of a Document. A Document is a \u201csingle, changeable record within a FaunaDB database,\u201d according to Fauna\u2019s documentation. You can think of Collections as a traditional database table and a Document as a row.<\/p>\n<p>For our application, we need one Collection, which we\u2019ll call \u201clinks.\u201d Each document within the \u201clinks\u201d Collection will be a simple JSON object with three properties. To start, we\u2019ll add a new Document that we\u2019ll use to build our first data fetch.<\/p>\n<div>\n<pre><code>{\n\u00a0\u00a0\"url\": \"https:\/\/css-irl.info\/debugging-css-grid-part-2-what-the-fraction\/\",\n\u00a0\u00a0\"pageTitle\": \"CSS { In Real Life } | Debugging CSS Grid \u2013 Part 2: What the Fr(action)?\",\n\u00a0\u00a0\"description\": \"CSS In Real Life is a blog covering CSS topics and useful snippets on the web\u2019s most beautiful language. Published by Michelle Barker, front end developer at Ordoo and CSS superfan.\"\n}<\/code><\/pre>\n<\/div>\n<p>This creates the basis for the information we\u2019ll need to pull from our bookmarks as well as provides us with our first set of data to pull into our template.<\/p>\n<p>If you\u2019re like me, you want to see the fruits of your labor right away. Let\u2019s get something on the page!<\/p>\n<h3>Installing 11ty And Pulling Data Into A Template<\/h3>\n<p>Since we want the bookmarks to be rendered in HTML and not fetched by the browser, we\u2019ll need something to do the rendering. There are many great ways of doing it, but for ease and power, I love using the 11ty static site generator.<\/p>\n<p>Since 11ty is a JavaScript static site generator, we can install it via NPM.<\/p>\n<pre><code>npm install --save @11ty\/eleventy<\/code><\/pre>\n<p>From that installation, we can run <code>eleventy<\/code> or <code>eleventy --serve<\/code> in our project to get up and running.<\/p>\n<p>Netlify Dev will often detect 11ty as a requirement and run the command for us. To have this work &#8211; and make sure we\u2019re ready to deploy, we can also create \u201cserve\u201d and \u201cbuild\u201d commands in our <code>package.json<\/code>.<\/p>\n<pre><code>\"scripts\": {\n    \"build\": \"npx eleventy\",\n    \"serve\": \"npx eleventy --serve\"\n  }<\/code><\/pre>\n<h4>11ty\u2019s Data Files<\/h4>\n<p>Most static site generators have an idea of a \u201cdata file\u201d built-in. Usually, these files will be JSON or YAML files that allow you to add extra information to your site.<\/p>\n<p>In 11ty, you can use JSON data files or JavaScript data files. By utilizing a JavaScript file, we can actually make our API calls and return the data directly into a template.<\/p>\n<p>By default, 11ty wants data files stored in a <code>_data<\/code> directory. You can then access the data by using the file name as a variable in your templates. In our case, we\u2019ll create a file at <code>_data\/bookmarks.js<\/code> and access that via the <code>{{ bookmarks }}<\/code> variable name.<\/p>\n<p>If you want to dig deeper into data file configuration, you can read through examples in the <a href=\"https:\/\/www.11ty.io\/docs\/data\/\">11ty documentation<\/a> or check out this tutorial on <a href=\"https:\/\/bryanlrobinson.com\/blog\/using-eleventys-javascript-data-files\/\">using 11ty data files with the Meetup API<\/a>.<\/p>\n<p>The file will be a JavaScript module. So in order to have anything work, we need to export either our data or a function. In our case, we\u2019ll export a function.<\/p>\n<pre><code>module.exports = async function() {  \n    const data = mapBookmarks(await getBookmarks());  \n\n    return data.reverse()  \n}<\/code><\/pre>\n<p>Let\u2019s break that down. We have two functions doing our main work here: <code>mapBookmarks()<\/code> and <code>getBookmarks()<\/code>.\u00a0<\/p>\n<p>The <code>getBookmarks()<\/code> function will go fetch our data from our FaunaDB database and <code>mapBookmarks()<\/code> will take an array of bookmarks and restructure it to work better for our template.<\/p>\n<p>Let\u2019s dig deeper into <code>getBookmarks()<\/code>.<\/p>\n<h4><code>getBookmarks()<\/code><\/h4>\n<p>First, we\u2019ll need to install and initialize an instance of the FaunaDB JavaScript driver.<\/p>\n<pre><code>npm install --save faunadb<\/code><\/pre>\n<p>Now that we\u2019ve installed it, let\u2019s add it to the top of our data file. This code is <a href=\"https:\/\/docs.fauna.com\/fauna\/current\/drivers\/javascript#requiring-the-driver\">straight from Fauna\u2019s docs<\/a>.<\/p>\n<div>\n<pre><code>\/\/ Requires the Fauna module and sets up the query module, which we can use to create custom queries.\nconst faunadb = require('faunadb'),  \n      q = faunadb.query;\n\n\/\/ Once required, we need a new instance with our secret\nvar adminClient = new faunadb.Client({  \n   secret: process.env.FAUNADB_SERVER_SECRET  \n});\n<\/code><\/pre>\n<\/div>\n<p>After that, we can create our function. We\u2019ll start by building our first query using built-in methods on the driver. This first bit of code will return the database references we can use to get full data for all of our bookmarked links. We use the <code>Paginate<\/code> method, as a helper to manage cursor state should we decide to paginate the data before handing it to 11ty. In our case, we\u2019ll just return all the references.<\/p>\n<p>In this example, I\u2019m assuming you installed and connected FaunaDB via the Netlify Dev CLI. Using this process, you get local environment variables of the FaunaDB secrets. If you didn\u2019t install it this way or aren\u2019t running <code>netlify dev<\/code> in your project, you\u2019ll need a package like <code>dotenv<\/code> to create the environment variables. You\u2019ll also need to add your environment variables to your Netlify site configuration to make deploys work later.<\/p>\n<div>\n<pre><code>adminClient.query(q.Paginate(  \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0q.Match( \/\/ Match the reference below\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0q.Ref(\"indexes\/all_links\") \/\/ Reference to match, in this case, our all_links index  \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)  \n\u00a0\u00a0\u00a0))  \n\u00a0\u00a0\u00a0.then( response => { ... })<\/code><\/pre>\n<\/div>\n<p>This code will return an array of all of our links in reference form. We can now build a query list to send to our database.<\/p>\n<div>\n<pre><code>adminClient.query(...)\n    .then((response) => {  \n        const linkRefs = response.data; \/\/ Get just the references for the links from the response \n        const getAllLinksDataQuery = linkRefs.map((ref) => {  \n        return q.Get(ref) \/\/ Return a Get query based on the reference passed in  \n\u00a0\u00a0\u00a0})  \n\nreturn adminClient.query(getAllLinksDataQuery).then(ret => {  \n    return ret \/\/ Return an array of all the links with full data  \n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0})  \n\u00a0\u00a0\u00a0}).catch(...)<\/code><\/pre>\n<\/div>\n<p>From here, we just need to clean up the data returned. That\u2019s where <code>mapBookmarks()<\/code> comes in!<\/p>\n<h4><code>mapBookmarks()<\/code><\/h4>\n<p>In this function, we deal with two aspects of the data.<\/p>\n<p>First, we get a free dateTime in FaunaDB. For any data created, there\u2019s a timestamp (<code>ts<\/code>) property. It\u2019s not formatted in a way that makes Liquid\u2019s default date filter happy, so let\u2019s fix that.<\/p>\n<pre><code>function mapBookmarks(data) {\n    return data.map(bookmark => {\n        const dateTime = new Date(bookmark.ts \/ 1000);\n        ...\n    })\n}<\/code><\/pre>\n<p>With that out of the way, we can build a new object for our data. In this case, it will have a <code>time<\/code> property, and we\u2019ll use the Spread operator to destructure our <code>data<\/code> object to make them all live at one level.<\/p>\n<pre><code>function mapBookmarks(data) {\n    return data.map(bookmark => {\n        const dateTime = new Date(bookmark.ts \/ 1000);\n\n        return { time: dateTime, ...bookmark.data }\n    })\n}<\/code><\/pre>\n<p>Here\u2019s our data before our function:<\/p>\n<pre><code>{ \n  ref: Ref(Collection(\"links\"), \"244778237839802888\"),\n  ts: 1569697568650000,\n  \n  data: { \n    url: 'https:\/\/sample.com',\n    pageTitle: 'Sample title',\n    description: 'An escaped description goes here' \n  } \n}\n<\/code><\/pre>\n<p>Here\u2019s our data after our function:<\/p>\n<pre><code>{\n    time: 1569697568650,\n    url: 'https:\/\/sample.com',\n    pageTitle: 'Sample title'\n    description: 'An escaped description goes here'\n}\n<\/code><\/pre>\n<p>Now, we\u2019ve got well-formatted data that\u2019s ready for our template!<\/p>\n<p>Let\u2019s write a simple template. We\u2019ll loop through our bookmarks and validate that each has a <code>pageTitle<\/code> and a <code>url<\/code> so we don\u2019t look silly.<\/p>\n<div>\n<pre><code><div class=\"bookmarks\">  \n   {% for link in bookmarks %}  \n       {% if link.url and link.pageTitle %} \/\/ confirms there\u2019s both title AND url for safety\n\n        <div class=\"bookmark\">  \n            <h2><a href=\"{{ link.url }}\">{{ link.pageTitle }}<\/a><\/h2>  \n            <p>Saved on {{ link.time | date: \"%b %d, %Y\"  }}<\/p>\n            {% if link.description != \"\" %}  \n                <p>{{ link.description }}<\/p>  \n            {% endif %}\n        <\/div>  \n\n       {% endif %}  \n   {% endfor %}  \n<\/div>\n<\/code><\/pre>\n<\/div>\n<p>We\u2019re now ingesting and displaying data from FaunaDB. Let\u2019s take a moment and think about how nice it is that this renders out pure HTML and there\u2019s no need to fetch data on the client side!<\/p>\n<p>But that\u2019s not really enough to make this a useful app for us. Let\u2019s figure out a better way than adding a bookmark in the FaunaDB console.<\/p>\n<div><\/div>\n<h3>Enter Netlify Functions<\/h3>\n<p>Netlify\u2019s Functions add-on is one of the easier ways to deploy AWS lambda functions. Since there\u2019s no configuration step, it\u2019s perfect for DIY projects where you just want to write the code.<\/p>\n<p>This function will live at a URL in your project that looks like this: <code>https:\/\/myproject.com\/.netlify\/functions\/bookmarks<\/code> assuming the file we create in our functions folder is <code>bookmarks.js<\/code>.<\/p>\n<h4>Basic Flow<\/h4>\n<ol>\n<li>Pass a URL as a query parameter to our function URL.<\/li>\n<li>Use the function to load the URL and scrape the page\u2019s title and description if available.<\/li>\n<li>Format the details for FaunaDB.<\/li>\n<li>Push the details to our FaunaDB Collection.<\/li>\n<li>Rebuild the site.<\/li>\n<\/ol>\n<h4>Requirements<\/h4>\n<p>We\u2019ve got a few packages we\u2019ll need as we build this out. We\u2019ll use the netlify-lambda CLI to build our functions locally. <code>request-promise<\/code> is the package we\u2019ll use for making requests. Cheerio.js is the package we\u2019ll use to scrape specific items from our requested page (think jQuery for Node). And finally, we\u2019ll need FaunaDb (which should already be installed.<\/p>\n<pre><code>npm install --save netlify-lambda request-promise cheerio<\/code><\/pre>\n<p>Once that\u2019s installed, let\u2019s configure our project to build and serve the functions locally.<\/p>\n<p>We\u2019ll modify our \u201cbuild\u201d and \u201cserve\u201d scripts in our <code>package.json<\/code> to look like this:<\/p>\n<div>\n<pre><code>\"scripts\": {\n    \"build\": \"npx netlify-lambda build lambda --config .\/webpack.functions.js && npx eleventy\",\n    \"serve\": \"npx netlify-lambda build lambda --config .\/webpack.functions.js && npx eleventy --serve\"\n}\n<\/code><\/pre>\n<\/div>\n<p><strong>Warning:<\/strong> <em>There\u2019s an error with Fauna\u2019s NodeJS driver when compiling with Webpack, which Netlify\u2019s Functions use to build. To <a href=\"https:\/\/github.com\/netlify\/netlify-faunadb-example\/issues\/8\">get around this<\/a>, we need to define a configuration file for Webpack. You can save the following code to a new<\/em> \u2014 <em>or existing<\/em> \u2014 <code>webpack.config.js<\/code>.<\/p>\n<pre><code>const webpack = require('webpack');\n\nmodule.exports = {\n  plugins: [ new webpack.DefinePlugin({ \"global.GENTLY\": false }) ]\n};<\/code><\/pre>\n<p>Once this file exists, when we use the <code>netlify-lambda<\/code> command, we\u2019ll need to tell it to run from this configuration. This is why our \u201cserve\u201d and \u201cbuild scripts use the <code>--config<\/code> value for that command.<\/p>\n<h4>Function Housekeeping<\/h4>\n<p>In order to keep our main Function file as clean as possible, we\u2019ll create our functions in a separate <code>bookmarks<\/code> directory and import them into our main Function file.<\/p>\n<pre><code>import { getDetails, saveBookmark } from \".\/bookmarks\/create\";<\/code><\/pre>\n<h4><code>getDetails(url)<\/code><\/h4>\n<p>The <code>getDetails()<\/code> function will take a URL, passed in from our exported handler. From there, we\u2019ll reach out to the site at that URL and grab relevant parts of the page to store as data for our bookmark.<\/p>\n<p>We start by requiring the NPM packages we need:<\/p>\n<pre><code>const rp = require('request-promise');  \nconst cheerio = require('cheerio');<\/code><\/pre>\n<p>Then, we\u2019ll use the <code>request-promise<\/code> module to return an HTML string for the requested page and pass that into <code>cheerio<\/code> to give us a very jQuery-esque interface.<\/p>\n<pre><code>const getDetails = async function(url) {  \n    const data = rp(url).then(function(htmlString) {  \n        const $ = cheerio.load(htmlString);  \n        ...  \n}<\/code><\/pre>\n<p>From here, we need to get the page title and a meta description. To do that, we\u2019ll use selectors like you would in jQuery.\u00a0<\/p>\n<p><strong>Note:<\/strong> <em>In this code, we use<\/em> <code>'head > title'<\/code> <em>as the selector to get the title of the page. If you don\u2019t specify this, you may end up getting<\/em> <code><title><\/code> <em>tags inside of all SVGs on the page, which is less than ideal.<\/em><\/p>\n<div>\n<pre><code>const getDetails = async function(url) {\n  const data = rp(url).then(function(htmlString) {\n    const $ = cheerio.load(htmlString);\n    const title = $('head > title').text(); \/\/ Get the text inside the tag  \n    const description = $('meta[name=\"description\"]').attr('content'); \/\/ Get the text of the content attribute\n\n\/\/ Return out the data in the structure we expect  \n    return {\n      pageTitle: title,\n      description: description\n    };\n  });\n  return data \/\/return to our main function  \n}<\/code><\/pre>\n<\/div>\n<p>With data in hand, it\u2019s time to send our bookmark off to our Collection in FaunaDB!<\/p>\n<h4><code>saveBookmark(details)<\/code><\/h4>\n<p>For our save function, we\u2019ll want to pass the details we acquired from <code>getDetails<\/code> as well as the URL as a singular object. The Spread operator strikes again!<\/p>\n<pre><code>const savedResponse = await saveBookmark({url, ...details});<\/code><\/pre>\n<p>In our <code>create.js<\/code> file, we also need to require and setup our FaunaDB driver. This should look very familiar from our 11ty data file.<\/p>\n<pre><code>const faunadb = require('faunadb'),  \n      q = faunadb.query;  \n\nconst adminClient = new faunadb.Client({  \n   secret: process.env.FAUNADB_SERVER_SECRET  \n});\n<\/code><\/pre>\n<p>Once we\u2019ve got that out of the way, we can code.<\/p>\n<div><\/div>\n<p>First, we need to format our details into a data structure that Fauna is expecting for our query. Fauna expects an object with a data property containing the data we wish to store.<\/p>\n<pre><code>const saveBookmark = async function(details) {  \nconst data = {  \n\u00a0\u00a0\u00a0data: details  \n};\n\n...\n\n}<\/code><\/pre>\n<p>Then we\u2019ll open a new query to add to our Collection. In this case, we\u2019ll use our query helper and use the Create method. Create() takes two arguments. First is the Collection in which we want to store our data and the second is the data itself.<\/p>\n<p>After we save, we return either success or failure to our handler.<\/p>\n<div>\n<pre><code>const saveBookmark = async function(details) {  \nconst data = {  \n   data: details  \n};\n\nreturn adminClient.query(q.Create(q.Collection(\"links\"), data))  \n   .then((response) => {  \n        \/* Success! return the response with statusCode 200 *\/  \n        return {  \n             statusCode: 200,  \n             body: JSON.stringify(response)  \n         }  \n     }).catch((error) => {  \n        \/* Error! return the error with statusCode 400 *\/  \n        return  {  \n             statusCode: 400,  \n             body: JSON.stringify(error)  \n         }  \n     })  \n}<\/code><\/pre>\n<\/div>\n<p>Let\u2019s take a look at the full Function file.<\/p>\n<div>\n<pre><code>import { getDetails, saveBookmark } from \".\/bookmarks\/create\";  \nimport { rebuildSite } from \".\/utilities\/rebuild\"; \/\/ For rebuilding the site (more on that in a minute)\n\nexports.handler = async function(event, context) {  \n    try {  \n        const url = event.queryStringParameters.url; \/\/ Grab the URL  \n\n        const details = await getDetails(url); \/\/ Get the details of the page  \n        const savedResponse = await saveBookmark({url, ...details}); \/\/Save the URL and the details to Fauna  \n\n        if (savedResponse.statusCode === 200) { \n            \/\/ If successful, return success and trigger a Netlify build  \n            await rebuildSite();  \n            return { statusCode: 200, body: savedResponse.body }  \n         } else {  \n            return savedResponse \/\/or else return the error  \n         }  \n     } catch (err) {  \n        return { statusCode: 500, body: `Error: ${err}` };  \n     }  \n};\n<\/code><\/pre>\n<\/div>\n<h4><code>rebuildSite()<\/code><\/h4>\n<p>The discerning eye will notice that we have one more function imported into our handler: <code>rebuildSite()<\/code>. This function will use Netlify\u2019s Deploy Hook functionality to rebuild our site from the new data every time we submit a new \u2014 successful \u2014 bookmark save.<\/p>\n<p>In your site\u2019s settings in Netlify, you can access your Build &#038; Deploy settings and create a new \u201cBuild Hook.\u201d Hooks have a name that appears in the Deploy section and an option for a non-master branch to deploy if you so wish. In our case, we\u2019ll name it \u201cnew_link\u201d and deploy our master branch.<\/p>\n<figure><a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/58bf7a4f-3248-4227-bb3c-0c2a6ed96d69\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png\"><\/p>\n<p>    <img decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/58bf7a4f-3248-4227-bb3c-0c2a6ed96d69\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/58bf7a4f-3248-4227-bb3c-0c2a6ed96d69\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/58bf7a4f-3248-4227-bb3c-0c2a6ed96d69\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/58bf7a4f-3248-4227-bb3c-0c2a6ed96d69\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png 2000w\" src=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png\" sizes=\"100vw\" alt=\"A visual reference for the Netlify Admin\u2019s build hook setup\"><\/a><figcaption>\n      A visual reference for the Netlify Admin\u2019s build hook setup (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/58bf7a4f-3248-4227-bb3c-0c2a6ed96d69\/bookmarking-application-faunadb-netlify-11ty-netlify-build-hook.png\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<p>From there, we just need to send a POST request to the URL provided.<\/p>\n<p>We need a way of making requests and since we\u2019ve already installed <code>request-promise<\/code>, we\u2019ll continue to use that package by requiring it at the top of our file.<\/p>\n<div>\n<pre><code>const rp = require('request-promise');  \n\nconst rebuildSite = async function() {  \n    var options = {  \n         method: 'POST',  \n         uri: 'https:\/\/api.netlify.com\/build_hooks\/5d7fa6175504dfd43377688c',  \n         body: {},  \n         json: true  \n    };  \n\n    const returned = await rp(options).then(function(res) {  \n         console.log('Successfully hit webhook', res);  \n     }).catch(function(err) {  \n         console.log('Error:', err);  \n     });  \n\n    return returned  \n}\n<\/code><\/pre>\n<\/div>\n<figure><figcaption>A demo of the Netlify Function setup and the iOS Shortcut setup combined<\/figcaption><\/figure>\n<h3>Setting Up An iOS Shortcut<\/h3>\n<p>So, we have a database, a way to display data and a function to add data, but we\u2019re still not very user-friendly.<\/p>\n<p>Netlify provides URLs for our Lambda functions, but they\u2019re not fun to type into a mobile device. We\u2019d also have to pass a URL as a query parameter into it. That\u2019s a LOT of effort. How can we make this as little effort as possible?<\/p>\n<figure><a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d68438f8-23dc-4253-b34c-bce3428ade47\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png\"><\/p>\n<p>    <img decoding=\"async\" srcset=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png 400w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_800\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d68438f8-23dc-4253-b34c-bce3428ade47\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png 800w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1200\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d68438f8-23dc-4253-b34c-bce3428ade47\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png 1200w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_1600\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d68438f8-23dc-4253-b34c-bce3428ade47\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png 1600w,\n\t\t\t        https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_auto\/w_2000\/https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d68438f8-23dc-4253-b34c-bce3428ade47\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png 2000w\" src=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png\" sizes=\"100vw\" alt=\"A visual reference for the setup for our Shortcut functionality\"><\/a><figcaption>\n      A visual reference for the setup for our Shortcut functionality (<a href=\"https:\/\/cloud.netlifyusercontent.com\/assets\/344dbf88-fdf9-42bb-adb4-46f01eedd629\/d68438f8-23dc-4253-b34c-bce3428ade47\/bookmarking-application-faunadb-netlify-11ty-ios-shortcuts-image.png\">Large preview<\/a>)<br \/>\n    <\/figcaption><\/figure>\n<p>Apple\u2019s Shortcuts app allows the building of custom items to go into your share sheet. Inside these shortcuts, we can send various types of requests of data collected in the share process.<\/p>\n<p>Here\u2019s the step-by-step Shortcut:<\/p>\n<ol>\n<li>Accept any items and store that item in a \u201ctext\u201d block.<\/li>\n<li>Pass that text into a \u201cScripting\u201d block to URL encode (just in case).<\/li>\n<li>Pass that string into a URL block with our Netlify Function\u2019s URL and a query parameter of <code>url<\/code>.<\/li>\n<li>From \u201cNetwork\u201d use a \u201cGet contents\u201d block to POST to JSON to our URL.<\/li>\n<li>Optional: From \u201cScripting\u201d \u201cShow\u201d the contents of the last step (to confirm the data we\u2019re sending).<\/li>\n<\/ol>\n<p>To access this from the sharing menu, we open up the settings for this Shortcut and toggle on the \u201cShow in Share Sheet\u201d option.<\/p>\n<p>As of iOS13, these share \u201cActions\u201d are able to be favorited and moved to a high position in the dialog.<\/p>\n<p>We now have a working \u201capp\u201d for sharing bookmarks across multiple platforms!<\/p>\n<h3>Go The Extra Mile!<\/h3>\n<p>If you\u2019re inspired to try this yourself, there are a lot of other possibilities to add functionality. The joy of the DIY web is that you can make these sorts of applications work for you. Here are a few ideas:<\/p>\n<ol>\n<li>Use a faux \u201cAPI key\u201d for quick authentication, so other users don\u2019t post to your site (mine uses an API key, so don\u2019t try to post to it!).<\/li>\n<li>Add tag functionality to organize bookmarks.<\/li>\n<li>Add an RSS feed for your site so that others can subscribe.<\/li>\n<li>Send out a weekly roundup email programmatically for links that you\u2019ve added.<\/li>\n<\/ol>\n<p>Really, the sky is the limit, so start experimenting!<\/p>\n<div>\n  <img decoding=\"async\" src=\"http:\/\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/logo-red-18.png\" alt=\"Smashing Editorial\"><span>(dm, yk)<\/span>\n<\/div>\n<\/article>\n<p class=\"wpematico_credit\"><small>Powered by <a href=\"http:\/\/www.wpematico.com\" target=\"_blank\">WPeMatico<\/a><\/small><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Create A Bookmarking Application With FaunaDB, Netlify And 11ty Create A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00 The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that&#8230;<a class=\"moretag\" href=\"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/\"> Read the full article&#8230;<\/a><\/p>\n","protected":false},"author":4,"featured_media":72195,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[371],"tags":[],"class_list":["post-72194","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-user-experience"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Guest Contribution\"\/>\n\t<meta name=\"keywords\" content=\"user experience\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies\" \/>\n\t\t<meta property=\"og:description\" content=\"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-25T11:19:27+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-25T11:19:27+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#article\",\"name\":\"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies\",\"headline\":\"Create A Bookmarking Application With FaunaDB, Netlify And 11ty\",\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/10\\\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg?fit=400%2C329&ssl=1\",\"width\":400,\"height\":329},\"datePublished\":\"2019-10-25T04:19:27-07:00\",\"dateModified\":\"2019-10-25T04:19:27-07:00\",\"inLanguage\":\"en-US\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#webpage\"},\"articleSection\":\"User Experience\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"position\":2,\"name\":\"User Experience\",\"item\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#listItem\",\"name\":\"Create A Bookmarking Application With FaunaDB, Netlify And 11ty\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#listItem\",\"position\":3,\"name\":\"Create A Bookmarking Application With FaunaDB, Netlify And 11ty\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/category\\\/user-experience\\\/#listItem\",\"name\":\"User Experience\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/\",\"name\":\"Guest Contribution\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Guest Contribution\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#webpage\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/\",\"name\":\"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies\",\"description\":\"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your\",\"inLanguage\":\"en-US\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/author\\\/guestcontribution\\\/#author\"},\"image\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/i0.wp.com\\\/www.taterboy.com\\\/blog\\\/wp-content\\\/uploads\\\/2019\\\/10\\\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg?fit=400%2C329&ssl=1\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#mainImage\",\"width\":400,\"height\":329},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/2019\\\/10\\\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\\\/#mainImage\"},\"datePublished\":\"2019-10-25T04:19:27-07:00\",\"dateModified\":\"2019-10-25T04:19:27-07:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/\",\"name\":\"Design for Immersive Technologies\",\"description\":\"User Experience for Games, Virtual Reality (VR) and Augmented\\\/Mixed Reality (AR\\\/MR)\",\"inLanguage\":\"en-US\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.taterboy.com\\\/blog\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies","description":"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your","canonical_url":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/","robots":"max-image-preview:large","keywords":"user experience","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#article","name":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies","headline":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty","author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg?fit=400%2C329&ssl=1","width":400,"height":329},"datePublished":"2019-10-25T04:19:27-07:00","dateModified":"2019-10-25T04:19:27-07:00","inLanguage":"en-US","mainEntityOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#webpage"},"isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#webpage"},"articleSection":"User Experience"},{"@type":"BreadcrumbList","@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","position":1,"name":"Home","item":"https:\/\/www.taterboy.com\/blog","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","position":2,"name":"User Experience","item":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/","nextItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#listItem","name":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty"},"previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#listItem","position":3,"name":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty","previousItem":{"@type":"ListItem","@id":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/#listItem","name":"User Experience"}}]},{"@type":"Organization","@id":"https:\/\/www.taterboy.com\/blog\/#organization","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","url":"https:\/\/www.taterboy.com\/blog\/"},{"@type":"Person","@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author","url":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/","name":"Guest Contribution","image":{"@type":"ImageObject","@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/d2aad73eb0f48c67b141e9fc978a0e498969791ed623346e361d02d19775b0bb?s=96&d=mm&r=g","width":96,"height":96,"caption":"Guest Contribution"}},{"@type":"WebPage","@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#webpage","url":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/","name":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies","description":"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your","inLanguage":"en-US","isPartOf":{"@id":"https:\/\/www.taterboy.com\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#breadcrumblist"},"author":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"creator":{"@id":"https:\/\/www.taterboy.com\/blog\/author\/guestcontribution\/#author"},"image":{"@type":"ImageObject","url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg?fit=400%2C329&ssl=1","@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#mainImage","width":400,"height":329},"primaryImageOfPage":{"@id":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/#mainImage"},"datePublished":"2019-10-25T04:19:27-07:00","dateModified":"2019-10-25T04:19:27-07:00"},{"@type":"WebSite","@id":"https:\/\/www.taterboy.com\/blog\/#website","url":"https:\/\/www.taterboy.com\/blog\/","name":"Design for Immersive Technologies","description":"User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","inLanguage":"en-US","publisher":{"@id":"https:\/\/www.taterboy.com\/blog\/#organization"}}]},"og:locale":"en_US","og:site_name":"Design for Immersive Technologies | User Experience for Games, Virtual Reality (VR) and Augmented\/Mixed Reality (AR\/MR)","og:type":"article","og:title":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies","og:description":"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your","og:url":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/","article:published_time":"2019-10-25T11:19:27+00:00","article:modified_time":"2019-10-25T11:19:27+00:00","twitter:card":"summary","twitter:title":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty | Design for Immersive Technologies","twitter:description":"Create A Bookmarking Application With FaunaDB, Netlify And 11tyCreate A Bookmarking Application With FaunaDB, Netlify And 11ty Bryan Robinson 2019-10-24T13:30:59+02:002019-10-25T11:07:01+00:00The JAMstack (JavaScript, APIs and Markup) revolution is in full swing. Static sites are secure, fast, reliable and fun to work on. At the heart of the JAMstack are static site generators (SSGs) that store your"},"aioseo_meta_data":{"post_id":"72194","title":null,"description":null,"keywords":null,"keyphrases":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_custom_url":null,"og_image_custom_fields":null,"og_custom_image_width":null,"og_custom_image_height":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"location":null,"local_seo":null,"created":"2021-02-07 17:05:25","updated":"2026-09-02 09:02:25","focus_keyword":null,"additional_keywords":null,"truseo_locale":null,"primary_term":null,"og_image_url":null,"og_image_width":null,"og_image_height":null,"twitter_image_url":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"limit_modified_date":false,"ai":null,"breadcrumb_settings":null,"seo_analyzer_scan_date":null},"aioseo_breadcrumb":"<div class=\"aioseo-breadcrumbs\"><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\" title=\"Home\">Home<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\t<a href=\"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/\" title=\"User Experience\">User Experience<\/a>\n\t\t<\/span><span class=\"aioseo-breadcrumb-separator\">&raquo;<\/span><span class=\"aioseo-breadcrumb\">\n\t\t\tCreate A Bookmarking Application With FaunaDB, Netlify And 11ty\n\t\t<\/span><\/div>","aioseo_breadcrumb_json":[{"label":"Home","link":"https:\/\/www.taterboy.com\/blog"},{"label":"User Experience","link":"https:\/\/www.taterboy.com\/blog\/category\/user-experience\/"},{"label":"Create A Bookmarking Application With FaunaDB, Netlify And 11ty","link":"https:\/\/www.taterboy.com\/blog\/2019\/10\/create-a-bookmarking-application-with-faunadb-netlify-and-11ty\/"}],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8wzr5-iMq","jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.taterboy.com\/blog\/wp-content\/uploads\/2019\/10\/bookmarking-application-faunadb-netlify-11ty-netlify-dev-screenshot.jpg?fit=400%2C329&ssl=1","_links":{"self":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/72194","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/comments?post=72194"}],"version-history":[{"count":0,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/posts\/72194\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media\/72195"}],"wp:attachment":[{"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/media?parent=72194"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/categories?post=72194"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.taterboy.com\/blog\/wp-json\/wp\/v2\/tags?post=72194"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}