<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jacob Lee</title>
    <description>The latest articles on DEV Community by Jacob Lee (@hacubu).</description>
    <link>https://dev.to/hacubu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F108131%2F41a9c0d0-aa25-446a-949f-ef3889c73248.jpg</url>
      <title>DEV Community: Jacob Lee</title>
      <link>https://dev.to/hacubu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hacubu"/>
    <language>en</language>
    <item>
      <title>From Founder to Freelancer: How I Incorporated a Business and Onboarded My First Client</title>
      <dc:creator>Jacob Lee</dc:creator>
      <pubDate>Fri, 10 Feb 2023 06:48:17 +0000</pubDate>
      <link>https://dev.to/hacubu/from-founder-to-freelancer-how-i-incorporated-a-business-and-onboarded-my-first-client-51c4</link>
      <guid>https://dev.to/hacubu/from-founder-to-freelancer-how-i-incorporated-a-business-and-onboarded-my-first-client-51c4</guid>
      <description>&lt;p&gt;&lt;em&gt;Hey readers! For those of you who don't know me, I was previously the co-founder and CTO of a startup called &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;Autocode&lt;/a&gt;. We built and scaled a fantastic product that reached over &lt;b&gt;600,000 developers&lt;/b&gt;, but after six years, I felt it was time for a change of scenery and a new adventure. I've decided to do some writing to document my post-Autocode journey - if you're interested in reading more, follow me here!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So why did I decide to become a freelance developer? After making the decision to leave Autocode and looking back at my time there, I realized I was quite interested in seeing how other organizations function - how they're structured and managed, the problems they're solving (both technical and non-technical!), and how they overcome adversity. I also wanted to explore different product areas and meet interesting, passionate people. Freelancing ticked those boxes, and had the added benefit of flexibility and control over my schedule after years of constant late-night product launches and long on-call stretches.&lt;/p&gt;

&lt;p&gt;It sounded great in theory, but I quickly found that getting started was more complex than just finding a client, writing some code, and cashing a check. &lt;/p&gt;

&lt;p&gt;My first thought was to simply Google &lt;code&gt;how to start consulting&lt;/code&gt;. Here's what I saw:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhp2yge03emd9gvqpdla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdhp2yge03emd9gvqpdla.png" alt="Google results for how to start consulting" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results included pages of clickbait-y, generic content marketing articles published by companies looking for SEO, as well as an intimidating dropdown that said I should expect getting started to cost &lt;strong&gt;$10k-50k&lt;/strong&gt; (it's much, much less!). The actual details of getting started as a freelancer - like how to incorporate, tax implications, how to set up a contract - proved scarce. And that's really too bad, since freelancing opens up opportunities for both full-time work and side-hustles.&lt;/p&gt;

&lt;p&gt;Here's how I cut through all the noise, incorporated my business, and onboarded my first paying client!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sidebar: I am not a lawyer or an accountant - don't take my experience as gospel and do your own research!&lt;/strong&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Deciding on a Corporate Structure
&lt;/h1&gt;

&lt;p&gt;Confused by all the information online, I asked my cousin, who had done some freelancing in the past, for some advice on corporate structure. She replied, "Oh, you can just start out by using your Social Security Number!" &lt;/p&gt;

&lt;p&gt;Wait, really? Sure, &lt;a href="https://www.ftb.ca.gov/file/personal/filing-situations/self-employed.html" rel="noopener noreferrer"&gt;otherwise every part-time Uber and Lyft driver would need to incorporate&lt;/a&gt;! This seemed like the easiest approach, but I decided on an LLC for a few reasons (in no specific order of importance):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The corporation is treated as a &lt;a href="https://en.wikipedia.org/wiki/Corporate_personhood" rel="noopener noreferrer"&gt;separate entity&lt;/a&gt; from me. In an unlikely scenario where a client sues, my liability is limited in most cases (as the name Limited Liability Corporation implies!). I &lt;em&gt;probably&lt;/em&gt; won't lose my house.&lt;/li&gt;
&lt;li&gt;It gives the impression of professionalism to clients.&lt;/li&gt;
&lt;li&gt;I thought it'd be cool.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are more complicated structures as well, but since the business would just be me an &lt;strong&gt;LLC seemed like a good balance between ease of management and legal protection in the worst case&lt;/strong&gt;. Single-member LLCs can also &lt;a href="https://www.irs.gov/businesses/small-businesses-self-employed/single-member-limited-liability-companies" rel="noopener noreferrer"&gt;report income on the owner's tax return&lt;/a&gt;, so I still only need to file one tax return.&lt;/p&gt;

&lt;h1&gt;
  
  
  Incorporating an LLC
&lt;/h1&gt;

&lt;p&gt;Note that your experience may vary depending on the state - I filed in California.&lt;/p&gt;

&lt;p&gt;After deciding on an LLC, my next step was legally incorporating. As a California resident, I was unsure of the tax implications if I incorporated in a different state, so I chose to incorporate in California. The downside to this is that California has &lt;a href="https://www.ftb.ca.gov/file/business/types/limited-liability-company/index.html#Annual-Tax" rel="noopener noreferrer"&gt;a very expensive Annual Tax&lt;/a&gt; on LLCs - $800 even if you make no money, and an additional tax if you make more than $250,000 a year! &lt;/p&gt;

&lt;p&gt;The good news is that the $800 component of the first year tax is being waived for businesses that incorporate before January 1st, 2024.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Side note: I probably made a mistake here - I forgot about &lt;a href="https://stripe.com/atlas" rel="noopener noreferrer"&gt;Stripe Atlas&lt;/a&gt;! They charge $500 to incorporate a business in Delaware (which has the most business-friendly laws for some reason), and $100 a year for management after. It's a bargain compared to fees in California, especially since they give Stripe credits that you can use to avoid payment processing fees when invoicing clients! There's a good chance I'll spin down my current LLC before the $800 tax kicks in and redo things in Delaware.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This process is run by the California Secretary of State's office. They have a &lt;a href="https://bizfileonline.sos.ca.gov/" rel="noopener noreferrer"&gt;surprisingly decent website&lt;/a&gt;, and all the filing can be done online. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcn2wmnljevfta8nbjuch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcn2wmnljevfta8nbjuch.png" alt="The California Secretary of State's Filing Portal" width="800" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's a section where it'll asked if I was a professional service provider, but this only applies to &lt;a href="https://www.stimmel-law.com/en/articles/professional-corporations-california-definitions-and-practicalities#:~:text=%E2%80%9CProfessional%20services%E2%80%9D%20are%20any%20type,Act%2C%20or%20the%20Osteopathic%20Act." rel="noopener noreferrer"&gt;professions that require a license&lt;/a&gt;, and writing software does not (yay?). The process required information like my Social Security Number, a business name, and a business address (like most freelancers, I don't have an office, so I used my current home address) and pay a small fee. A few days later my application was accepted, I had my Articles of Incorporation, and Remora Software, LLC was born!&lt;/p&gt;

&lt;p&gt;After incorporation, I had to file a "Statement of Information" within 90 days of incorporation. You can do this from the &lt;a href="https://bizfileonline.sos.ca.gov/" rel="noopener noreferrer"&gt;Secretary of State's website&lt;/a&gt;, and it's required every two years afterward.&lt;/p&gt;

&lt;h1&gt;
  
  
  Opening a Business Checking Account
&lt;/h1&gt;

&lt;p&gt;In software, there's a very important design concept called &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns" rel="noopener noreferrer"&gt;separation of concerns&lt;/a&gt;. it turns out, the same applies to LLCs. It becomes much easier to maintain a company's separate personhood and liability protection if all business income and expenses are separated from personal ones, so this is a must. &lt;/p&gt;

&lt;p&gt;Plus, you'll feel like a boss when the bank sends a card with your company name on it.&lt;/p&gt;

&lt;p&gt;Opening a business bank account requires an EIN (employer identification number). The IRS assigns EINs, and again, I was pleasantly surprised with how easy the process was. &lt;a href="https://www.irs.gov/businesses/small-businesses-self-employed/apply-for-an-employer-identification-number-ein-online" rel="noopener noreferrer"&gt;Everything is online&lt;/a&gt;, and I had an EIN a few minutes after applying using information from my LLC's Articles of Incorporation.&lt;/p&gt;

&lt;p&gt;You can then transfer money from your business account to your personal account via &lt;a href="https://bench.co/blog/accounting/owners-draw/" rel="noopener noreferrer"&gt;owner's draw&lt;/a&gt; as often as you'd like - your checking account will act as a record of how much revenue your business is making for tax purposes.&lt;/p&gt;

&lt;p&gt;I opened an account with &lt;a href="https://chase.com" rel="noopener noreferrer"&gt;Chase&lt;/a&gt; as I had used them in the past and they were running a $700 promotion for new accounts that meet certain requirements, but there are plenty of good options out there.&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting Up a Contract
&lt;/h1&gt;

&lt;p&gt;I was fortunate enough to be referred to a small startup interested in hiring someone with my skillset. In retrospect, I could have negotiated a much higher rate, but I was eager to get my first client and get a win on the board.&lt;/p&gt;

&lt;p&gt;The next step was putting together a contract and figuring out how to get paid. I looked around for solutions and eventually found &lt;a href="https://www.honeybook.com/" rel="noopener noreferrer"&gt;Honeybook&lt;/a&gt;, a client management platform. The week-long free trial and $1/month introductory rate drew me in, and I found it well-suited for my needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhuq3101ckq1otge3rnv1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhuq3101ckq1otge3rnv1.png" alt="Honeybook Contract Invoice" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used Honeybook's provided standard contract template, which I trusted because they've &lt;a href="https://www.crunchbase.com/organization/honeybook" rel="noopener noreferrer"&gt;raised almost half a billion dollars&lt;/a&gt;. It conveniently included an invoicing page, payment processing, and automatic reminders. They charge a 1.5% fee for ACH transfers, so I may explore using Stripe, a wire transfer, or Zelle in the future, but all in all, it was a convenient and client-friendly way to get paid, though there was some difficulty verifying my payout account because that I had only incorporated a few days before.&lt;/p&gt;

&lt;p&gt;The initial contract was for two weeks, and I structured payment so that half of the value was due up front and half at the end of the contract. The client signed and sent the money, I got to work, and it was a great feeling to have my first freelance contract in the books!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1620839324358160384-827" src="https://platform.twitter.com/embed/Tweet.html?id=1620839324358160384"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1620839324358160384-827');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1620839324358160384&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h1&gt;
  
  
  Thank You!
&lt;/h1&gt;

&lt;p&gt;If you've made it this far, thanks for reading! I'm not sure I'll remain a full-time freelancer forever, but incorporating was an extremely rewarding experience and opens up the potential to do part-time consulting on the side. &lt;strong&gt;I'd encourage more developers to give it a shot!&lt;/strong&gt; And please let me know in the comments if anything in the article looks amiss.&lt;/p&gt;

&lt;p&gt;If you're interested in hiring me, you can read more about me on my &lt;a href="https://jacobscript.dev" rel="noopener noreferrer"&gt;personal website&lt;/a&gt; - I specialize in cloud architecture/systems design, devops, and backend development, but started my career as a frontend engineer on Google Photos and can contribute to any part of a stack. &lt;/p&gt;

&lt;p&gt;To stay up to date with my journey, follow me here or on Twitter &lt;a href="https://twitter.com/hacubu" rel="noopener noreferrer"&gt;@Hacubu&lt;/a&gt;. Happy hacking!&lt;/p&gt;

</description>
      <category>freelance</category>
      <category>freelancing</category>
      <category>career</category>
      <category>sidehustle</category>
    </item>
    <item>
      <title>Build a Discord Bot in 6 Minutes With Node.js and Autocode</title>
      <dc:creator>Jacob Lee</dc:creator>
      <pubDate>Thu, 22 Apr 2021 06:58:29 +0000</pubDate>
      <link>https://dev.to/hacubu/build-a-discord-bot-in-6-minutes-with-node-js-and-autocode-4n2d</link>
      <guid>https://dev.to/hacubu/build-a-discord-bot-in-6-minutes-with-node-js-and-autocode-4n2d</guid>
      <description>&lt;p&gt;In this article, I'll show you how to set up a fully customizable &lt;a href="https://discord.com" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; bot that you can have running in your guild in 6 minutes (or less!). I'll also give you some helpful tips you can use to further customize your bot after getting the basic example working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F591tnsqh3a2dqf7t958j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F591tnsqh3a2dqf7t958j.png" alt="Your bot responding with a message when it is mentioned"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll be using &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;Autocode&lt;/a&gt; to do the heavy lifting around authenticating to the Discord API, handling incoming events, and hosting our bot's Node.js code — all for free. We've got an online editor as well, so the only other thing you'll need to get started is a Discord account!&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h1&gt;
  
  
  Quickstart
&lt;/h1&gt;

&lt;p&gt;Start by going to to the &lt;a href="https://autocode.com/app/discord/basic-discord-example/" rel="noopener noreferrer"&gt;Discord starter app page&lt;/a&gt; on Autocode. The bot's source code is completely open, so if you're curious, feel free to take a look!&lt;/p&gt;

&lt;p&gt;When you're ready, press the green &lt;strong&gt;Install Free&lt;/strong&gt; button. If you haven't already, create an Autocode account, then select a name for your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkimi2z4ph1nozvci4xcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkimi2z4ph1nozvci4xcd.png" alt="Name your Discord project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, you'll be prompted to link a Discord account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gfdhujv57zyrlp337nz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gfdhujv57zyrlp337nz.png" alt="Link a Discord account"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Link&lt;/strong&gt;, then &lt;strong&gt;Link New Resource&lt;/strong&gt; in the modal that appears and follow the instructions to link a Discord app. This involves creating an app from &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord's developer portal&lt;/a&gt; and installing it into your guild, then pasting the app's authentication credentials into Autocode when prompted. &lt;/p&gt;

&lt;p&gt;Finally, press &lt;strong&gt;Install App&lt;/strong&gt;. And that's it! If your guild has a &lt;code&gt;#general&lt;/code&gt; channel, you'll see a welcome message confirming installation. Mention your bot in a message by typing &lt;code&gt;@BotName&lt;/code&gt; and your bot will respond!&lt;/p&gt;

&lt;h1&gt;
  
  
  How Does It Work?
&lt;/h1&gt;

&lt;p&gt;Autocode listens for events from Discord using the bot credentials you supplied earlier. When it receives an event, Autocode triggers the appropriate endpoints in projects with the same bot linked.&lt;/p&gt;

&lt;p&gt;In this specific case, Autocode triggers the &lt;code&gt;functions/events/discord/bot_mention.js&lt;/code&gt; endpoint of the app you just installed. This endpoint contains a call to the messages.create method of the &lt;a href="https://autocode.com/lib/discord/channels/#messages-create" rel="noopener noreferrer"&gt;discord.channels API&lt;/a&gt; from Autocode's standard library, which sends the message. Here's what that code looks like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// authenticates you with the API standard library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;&lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STDLIB_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;messageResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@0.0.6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;channel_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channel_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s2"&gt;`Hey &amp;lt;@!&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;! I'm a bot powered by Autocode.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;`You mentioned me in a message, so here I am!`&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guild Information&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rich&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x00AA00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Green color&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You could add some information here for guild members to view!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Message Formatting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check out this link for more details on formatting message embeds:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://discord.com/developers/docs/resources/channel#embed-object-embed-structure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Setting up Slash Commands&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check out the README for this bot on Autocode for help setting up slash commands:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://autocode.com/app/discord/basic-discord-example/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;messageResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Pretty straightforward! Autocode automatically populates and defines a &lt;code&gt;context&lt;/code&gt; parameter containing details about the incoming event from Discord. We can access this data under &lt;code&gt;context.params.event&lt;/code&gt; as shown above.&lt;/p&gt;

&lt;h1&gt;
  
  
  Slash Commands
&lt;/h1&gt;

&lt;p&gt;The other endpoint in the app, &lt;code&gt;functions/events/discord/command.js&lt;/code&gt;, sends a message containing the number of users and bots currently in the guild when slash command called &lt;code&gt;/member-count&lt;/code&gt; is triggered:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" alt="Successful member count message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, before this will work, we need to register the slash command with Discord. Fortunately, this is easy with Autocode's built in Discord Slash Command Builder! &lt;/p&gt;

&lt;p&gt;Navigate to the &lt;a href="https://autocode.com/discord-command-builder/" rel="noopener noreferrer"&gt;Discord Slash Command Builder&lt;/a&gt;, and link the same bot you created during the installation flow. Select the guild you installed your bot into, and then name the command &lt;code&gt;member-count&lt;/code&gt;. Give it a description as well:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8s7cfw4ab5yhlj9gr73.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl8s7cfw4ab5yhlj9gr73.png" alt="Creating a Discord slash command via the Discord Slash Command Builder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you're ready, press &lt;strong&gt;Save All&lt;/strong&gt;. Congratulations! You've created a slash command in your guild. Try it out in your guild by typing &lt;code&gt;/member-count&lt;/code&gt;. If you've done everything correctly, you should see...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fom6eohz56ayg19gskmjn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fom6eohz56ayg19gskmjn.png" alt="Privileged intents required"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whoops. There's one more thing we need to do!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Privileged Intents
&lt;/h2&gt;

&lt;p&gt;If you look at the code for the &lt;code&gt;functions/events/discord/command.js&lt;/code&gt; endpoint, you'll notice that it calls the members.list method of the &lt;a href="https://autocode.com/lib/discord/guilds/#members-list" rel="noopener noreferrer"&gt;discord.guilds API&lt;/a&gt;. Discord treats this API as privileged, so to use it, you must manually grant access to your bot.&lt;/p&gt;

&lt;p&gt;Go back to the &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord developer portal&lt;/a&gt; and open your bot settings. Open the &lt;strong&gt;Bot&lt;/strong&gt; pane, and scroll down until you see the &lt;strong&gt;Privileged Gateway Intent&lt;/strong&gt; settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtzy5smvp86kczlb8pum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtzy5smvp86kczlb8pum.png" alt="Privileged intent settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enable the toggle labeled &lt;strong&gt;Server Members Intent&lt;/strong&gt;, then go back to your Discord server and run the command again. You should then see a proper message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" alt="Successful member count message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You've now fully set up the starter app! You can modify it's code to your heart's content, as well as add new endpoints for other Discord events, by reopening your project in Autocode.&lt;/p&gt;

&lt;h1&gt;
  
  
  Additional Tips
&lt;/h1&gt;

&lt;p&gt;As you continue to build, here are some additional tips you might find useful:&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Your Guild Id
&lt;/h2&gt;

&lt;p&gt;One way you can find your guild id (as well as ids for channels, users, and roles) is to enable &lt;strong&gt;Developer Mode&lt;/strong&gt;. Open your &lt;strong&gt;User Settings&lt;/strong&gt; by clicking the gear in the bottom left corner of your client, then click the &lt;strong&gt;Advanced&lt;/strong&gt; menu item in the left sidebar and turn on the &lt;strong&gt;Developer Mode&lt;/strong&gt; toggle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfukisooj42jrbjgvij4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfukisooj42jrbjgvij4.png" alt="Enabling developer mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once enabled, exit your settings and right click the icon of the guild containing your bot in the left sidebar. The menu that appears will contain a new &lt;code&gt;Copy ID&lt;/code&gt; item:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9hn18d4prvswekq0w30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9hn18d4prvswekq0w30.png" alt="The copy id item"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select it, and the guild id will be copied to your clipboard. Right clicking users, channels, and roles will have a similar &lt;code&gt;Copy ID&lt;/code&gt; option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting Messages
&lt;/h2&gt;

&lt;p&gt;Posting simple messages to Discord is easy with the &lt;a href="https://autocode.com/lib/discord/channels/#messages-create" rel="noopener noreferrer"&gt;discord.channels API&lt;/a&gt;, and Discord even supports markdown for easy formatting. However, as messages get more complex, here are a few tips to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mentioning a user or a bot in your message requires you to surround their id with brackets in a specific way. Here's an example: &lt;code&gt;This message is tagging a user: &amp;lt;@!000000000000&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Discord will automatically unfurl links in your messages, which you can use to your advantage to easily create useful notifications.&lt;/li&gt;
&lt;li&gt;Adding rich embeds to your message can really help it stand out in your channel. You can use the messages in the sample app endpoints as a starting point for what's possible, and check out the full list of embed object fields in &lt;a href="https://discord.com/developers/docs/resources/channel#embed-object" rel="noopener noreferrer"&gt;Discord's docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;One gotcha with embeds is the &lt;code&gt;color&lt;/code&gt; parameter — it takes a number, not a string. A convenient way to represent RGB colors as a number for this parameter is to use hexadecimal (&lt;code&gt;0x00AA00&lt;/code&gt; with no quotes for green instead of &lt;code&gt;'#00AA00'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tts&lt;/code&gt; stands for "text to speech" — setting it to true will make Discord read your message aloud to anyone with the right settings, so be careful when testing it in a guild with others!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://autocode.com/guides/how-to-build-a-discord-bot/" rel="noopener noreferrer"&gt;Official Guide to Building Discord Bots on Autocode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://autocode.com/discord-command-builder/" rel="noopener noreferrer"&gt;The Discord Slash Command Builder&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/docs/reference#message-formatting" rel="noopener noreferrer"&gt;Formatting Discord messages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/docs/resources/channel#embed-object" rel="noopener noreferrer"&gt;Discord message embed structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/docs/interactions/slash-commands" rel="noopener noreferrer"&gt;Discord slash commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord developer portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://autocode.com/lib/discord/commands/" rel="noopener noreferrer"&gt;Autocode discord/commands API page for creating slash commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-" rel="noopener noreferrer"&gt;How to find your Discord guild id&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Thank You!
&lt;/h1&gt;

&lt;p&gt;If you have any questions, feedback, or if you just want to chat, join Autocode Discord! We've got a growing community who would be happy to help. You can get an invite under &lt;strong&gt;Docs &amp;gt; Join Discord&lt;/strong&gt; in the topbar on autocode.com. &lt;/p&gt;

&lt;p&gt;You can also reach out to me directly on Twitter &lt;a href="https://twitter.com/hacubu" rel="noopener noreferrer"&gt;@Hacubu&lt;/a&gt;. And for more Autocode updates, follow us on Twitter &lt;a href="https://twitter.com/AutocodeHQ" rel="noopener noreferrer"&gt;@AutocodeHQ&lt;/a&gt;. Until next time!&lt;/p&gt;

</description>
      <category>discord</category>
      <category>node</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build a Discord Bot in 6 Minutes With Node.js and Autocode</title>
      <dc:creator>Jacob Lee</dc:creator>
      <pubDate>Thu, 22 Apr 2021 06:52:10 +0000</pubDate>
      <link>https://dev.to/hacubu/how-to-build-a-discord-bot-in-6-minutes-with-node-js-and-autocode-4if9</link>
      <guid>https://dev.to/hacubu/how-to-build-a-discord-bot-in-6-minutes-with-node-js-and-autocode-4if9</guid>
      <description>&lt;p&gt;In this article, I'll show you how to set up a fully customizable &lt;a href="https://discord.com" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; bot that you can have running in your guild in 6 minutes (or less!). I'll also give you some helpful tips you can use to further customize your bot after getting the basic example working.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F591tnsqh3a2dqf7t958j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F591tnsqh3a2dqf7t958j.png" alt="Your bot responding with a message when it is mentioned"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We'll be using &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;Autocode&lt;/a&gt; to do the heavy lifting around authenticating to the Discord API, handling incoming events, and hosting our bot's Node.js code — all for free. We've got an online editor as well, so the only other thing you'll need to get started is a Discord account!&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h1&gt;
  
  
  Quickstart
&lt;/h1&gt;

&lt;p&gt;Start by navigating to the &lt;a href="https://autocode.com/app/discord/basic-discord-example/" rel="noopener noreferrer"&gt;Discord starter app page&lt;/a&gt; on Autocode. The bot's source code is completely open, so if you're curious, feel free to take a look!&lt;/p&gt;

&lt;p&gt;When you're ready, press the green &lt;strong&gt;Install Free&lt;/strong&gt; button. If you haven't already, create an Autocode account, then select a name for your project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkimi2z4ph1nozvci4xcd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkimi2z4ph1nozvci4xcd.png" alt="Name your Discord project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the next screen, you'll be prompted to link a Discord account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gfdhujv57zyrlp337nz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gfdhujv57zyrlp337nz.png" alt="Link a Discord account"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;Link&lt;/strong&gt;, then &lt;strong&gt;Link New Resource&lt;/strong&gt; in the modal that appears and follow the instructions to link a Discord app. This involves creating an app from &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord's developer portal&lt;/a&gt; and installing it into your guild, then pasting the app's authentication credentials into Autocode when prompted. &lt;/p&gt;

&lt;p&gt;Finally, press &lt;strong&gt;Install App&lt;/strong&gt;. And that's it! If your guild has a &lt;code&gt;#general&lt;/code&gt; channel, you'll see a welcome message confirming installation. Mention your bot in a message by typing &lt;code&gt;@BotName&lt;/code&gt; and your bot will respond!&lt;/p&gt;

&lt;h1&gt;
  
  
  How Does It Work?
&lt;/h1&gt;

&lt;p&gt;Autocode listens for events from Discord using the bot credentials you supplied earlier. When it receives an event, Autocode triggers the appropriate endpoints in projects with the same bot linked.&lt;/p&gt;

&lt;p&gt;In this specific case, Autocode triggers the &lt;code&gt;functions/events/discord/bot_mention.js&lt;/code&gt; endpoint of the app you just installed. This endpoint contains a call to the messages.create method of the &lt;a href="https://autocode.com/lib/discord/channels/#messages-create" rel="noopener noreferrer"&gt;discord.channels API&lt;/a&gt; from Autocode's standard library, which sends the message. Here's what that code looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// authenticates you with the API standard library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;&lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STDLIB_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * An HTTP endpoint that acts as a webhook for Discord message.create event
 * @param {object} event
 * @returns {any} result
 */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;messageResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;discord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@0.0.2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;channel_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;channel_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;`Hey &amp;lt;@!&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;! I'm a bot powered by Autocode.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`You mentioned me in a message, so here I am!`&lt;/span&gt;
    &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guild Information&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rich&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;0x00AA00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Green color&lt;/span&gt;
      &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You could add some information here for guild members to view!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Message Formatting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check out this link for more details on formatting message embeds:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://discord.com/developers/docs/resources/channel#embed-object-embed-structure&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Setting up Slash Commands&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check out the README for this bot on Autocode for help setting up slash commands:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://autocode.com/app/discord/basic-discord-example/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;tts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;messageResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Slash Commands
&lt;/h1&gt;

&lt;p&gt;The other endpoint in the app, &lt;code&gt;functions/events/discord/command.js&lt;/code&gt;, sends a message containing the number of users and bots currently in the guild when slash command called &lt;code&gt;/member-count&lt;/code&gt; is triggered:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" alt="Successful member count message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, before this will work, we need to register the slash command with Discord. Discord doesn't currently have a built-in UI for this, but we can make the necessary API requests from Autocode's documentation pages to set it up. &lt;/p&gt;

&lt;p&gt;Navigate to the &lt;a href="https://autocode.com/lib/discord/commands/#create" rel="noopener noreferrer"&gt;create method of the discord.commands API page&lt;/a&gt; and fill out the parameters as shown below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6kgt093w9iyo806b81k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6kgt093w9iyo806b81k.png" alt="Creating a Discord slash command via API request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill out the &lt;code&gt;guild_id&lt;/code&gt; parameter with your guild's id (see the &lt;strong&gt;Tips&lt;/strong&gt; section later in the article if you need help finding it). Then, press &lt;strong&gt;Authenticate and Link Accounts&lt;/strong&gt; and choose the Discord bot you linked earlier. Finally, press &lt;code&gt;Run&lt;/code&gt;. Congratulations! You've created a slash command in your guild. Try it out in your guild by typing &lt;code&gt;/member-count&lt;/code&gt;. If you've done everything correctly, you should see...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fom6eohz56ayg19gskmjn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fom6eohz56ayg19gskmjn.png" alt="Privileged intents required"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whoops. There's one more thing we need to do!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Privileged Intents
&lt;/h2&gt;

&lt;p&gt;If you look at the code for the &lt;code&gt;functions/events/discord/command.js&lt;/code&gt; endpoint, you'll notice that it calls the members.list method of the &lt;a href="https://autocode.com/lib/discord/guilds/#members-list" rel="noopener noreferrer"&gt;discord.guilds API&lt;/a&gt;. Discord treats this API as privileged, so to use it, you must manually grant access to your bot.&lt;/p&gt;

&lt;p&gt;Go back to the &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord developer portal&lt;/a&gt; and open your bot settings. Open the &lt;strong&gt;Bot&lt;/strong&gt; pane, and scroll down until you see the &lt;strong&gt;Privileged Gateway Intent&lt;/strong&gt; settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtzy5smvp86kczlb8pum.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtzy5smvp86kczlb8pum.png" alt="Privileged intent settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enable the toggle labeled &lt;strong&gt;Server Members Intent&lt;/strong&gt;, then go back to your Discord server and run the command again. You should then see a proper message:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgkgjbijecdj062kdk9d3.png" alt="Successful member count message"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You've now fully set up the starter app! You can modify it's code to your heart's content, as well as add new endpoints for other Discord events, by reopening your project in Autocode.&lt;/p&gt;

&lt;h1&gt;
  
  
  Additional Tips
&lt;/h1&gt;

&lt;p&gt;As you continue to build, here are some additional tips you might find useful:&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding Your Guild Id
&lt;/h2&gt;

&lt;p&gt;One way you can find your guild id (as well as ids for channels, users, and roles) is to enable &lt;strong&gt;Developer Mode&lt;/strong&gt;. Open your &lt;strong&gt;User Settings&lt;/strong&gt; by clicking the gear in the bottom left corner of your client, then click the &lt;strong&gt;Advanced&lt;/strong&gt; menu item in the left sidebar and turn on the &lt;strong&gt;Developer Mode&lt;/strong&gt; toggle:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfukisooj42jrbjgvij4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfukisooj42jrbjgvij4.png" alt="Enabling developer mode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once enabled, exit your settings and right click the icon of the guild containing your bot in the left sidebar. The menu that appears will contain a new &lt;code&gt;Copy ID&lt;/code&gt; item:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9hn18d4prvswekq0w30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb9hn18d4prvswekq0w30.png" alt="The copy id item"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select it, and the guild id will be copied to your clipboard. Right clicking users, channels, and roles will have a similar &lt;code&gt;Copy ID&lt;/code&gt; option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Formatting Messages
&lt;/h2&gt;

&lt;p&gt;Posting simple messages to Discord is easy with the &lt;a href="https://autocode.com/lib/discord/channels/#messages-create" rel="noopener noreferrer"&gt;discord.channels API&lt;/a&gt;, and Discord even supports markdown for easy formatting. However, as messages get more complex, here are a few tips to keep in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mentioning a user or a bot in your message requires you to surround their id with brackets in a specific way. Here's an example: &lt;code&gt;This message is tagging a user: &amp;lt;@!000000000000&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Discord will automatically unfurl links in your messages, which you can use to your advantage to easily create useful notifications.&lt;/li&gt;
&lt;li&gt;Adding rich embeds to your message can really help it stand out in your channel. You can use the messages in the sample app endpoints as a starting point for what's possible, and check out the full list of embed object fields in &lt;a href="https://discord.com/developers/docs/resources/channel#embed-object" rel="noopener noreferrer"&gt;Discord's docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;One gotcha with embeds is the &lt;code&gt;color&lt;/code&gt; parameter — it takes a number, not a string. A convenient way to represent RGB colors as a number for this parameter is to use hexadecimal (&lt;code&gt;0x00AA00&lt;/code&gt; with no quotes for green instead of &lt;code&gt;'#00AA00'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tts&lt;/code&gt; stands for "text to speech" — setting it to true will make Discord read your message aloud to anyone with the right settings, so be careful when testing it in a guild with others!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Useful Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/docs/reference#message-formatting" rel="noopener noreferrer"&gt;Formatting Discord messages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/docs/resources/channel#embed-object" rel="noopener noreferrer"&gt;Discord message embed structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/docs/interactions/slash-commands" rel="noopener noreferrer"&gt;Discord slash commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;Discord developer portal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://autocode.com/lib/discord/commands/" rel="noopener noreferrer"&gt;Autocode discord/commands API page for creating slash commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.discord.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID-" rel="noopener noreferrer"&gt;How to find your Discord guild id&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Thank You!
&lt;/h1&gt;

&lt;p&gt;If you have any questions, feedback, or if you just want to chat, join Autocode Discord! We've got a growing community who would be happy to help. You can get an invite under &lt;strong&gt;Docs &amp;gt; Join Discord&lt;/strong&gt; in the topbar on autocode.com. &lt;/p&gt;

&lt;p&gt;You can also reach out to me directly on Twitter &lt;a href="https://twitter.com/hacubu" rel="noopener noreferrer"&gt;@Hacubu&lt;/a&gt;. And for more Autocode updates, follow us on Twitter &lt;a href="https://twitter.com/AutocodeHQ" rel="noopener noreferrer"&gt;@AutocodeHQ&lt;/a&gt;. Until next time!&lt;/p&gt;

</description>
      <category>discord</category>
      <category>node</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>An Introduction to Scraping (Almost) Anything With Puppeteer and Node.js</title>
      <dc:creator>Jacob Lee</dc:creator>
      <pubDate>Tue, 26 Jan 2021 07:46:39 +0000</pubDate>
      <link>https://dev.to/hacubu/an-introduction-to-scraping-almost-anything-with-puppeteer-and-node-js-e9g</link>
      <guid>https://dev.to/hacubu/an-introduction-to-scraping-almost-anything-with-puppeteer-and-node-js-e9g</guid>
      <description>&lt;p&gt;Despite the macabre name, headless browsers aren't scary (at least, to most people). They're similar to standard web browsers, but are controlled through code instead of with a mouse and keyboard. You can do almost anything with a headless browser on a page that you can do with a normal web browser, including submit forms, wait for asynchronous JavaScript, and set cookies. When used in combination with modern cloud platforms, it's easier than ever to create automated scrapers.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk you through a few web scraping tricks and examples using &lt;a href="https://pptr.dev" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt;, a headless browser based on Chromium that has become an industry standard, and Node.js. We'll also use &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;Autocode&lt;/a&gt; to easily run and iterate on our scraper code.&lt;/p&gt;

&lt;p&gt;All you need to get started is a free Autocode account. Let's dive in!&lt;/p&gt;

&lt;h1&gt;
  
  
  TL;DR (30s)
&lt;/h1&gt;

&lt;p&gt;Getting a basic scraper working is simple. Start by going to &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;https://autocode.com&lt;/a&gt; and creating or logging into your account. Create a new project, and paste the following code into the editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// authenticates you with the API standard library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;&lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STDLIB_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autocode-puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Any URL you'd like&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Autocode will automatically add required dependencies, so all you need to do now is, press the &lt;strong&gt;Save&lt;/strong&gt; or &lt;strong&gt;Run&lt;/strong&gt; button to push your code live. And that's it! You're now scraping the title of the page (what you'd see in the tab bar when you open the site in Chrome) with Puppeteer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzb15ntjzp0t6pjl5sxz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzb15ntjzp0t6pjl5sxz5.png" alt="Scraping the title from youtube.com"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Breaking It Down
&lt;/h1&gt;

&lt;p&gt;Here's what happens in the above code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We require a &lt;a href="https://www.npmjs.com/package/autocode-puppeteer" rel="noopener noreferrer"&gt;variant of Puppeteer configured to work in the Autocode environment&lt;/a&gt;. Note that the default Puppeteer package will not work due to dependency size constraints.&lt;/li&gt;
&lt;li&gt;We prepare Puppeteer by launching it and opening a new page.&lt;/li&gt;
&lt;li&gt;We navigate to the desired page with &lt;code&gt;await page.goto()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Once on the right page, we use the &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pagetitle" rel="noopener noreferrer"&gt;&lt;code&gt;page.title()&lt;/code&gt;&lt;/a&gt; method to scrape the page title.&lt;/li&gt;
&lt;li&gt;We close the browser instance when we're finished.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This flow is analogous to opening Chrome on your computer, typing &lt;code&gt;https://youtube.com/&lt;/code&gt; in the navbar, checking the title of the page, and finally closing the browser. We'll follow this general pattern when web scraping with Puppeteer. &lt;/p&gt;

&lt;p&gt;We're just scratching the surface of what's possible, but there's something important to emphasize first.&lt;/p&gt;

&lt;h1&gt;
  
  
  With Great Power...
&lt;/h1&gt;

&lt;p&gt;Many websites disallow scraping, and use tools like reCAPTCHA or contain a &lt;a href="https://en.wikipedia.org/wiki/Robots_exclusion_standard" rel="noopener noreferrer"&gt;robots.txt&lt;/a&gt; file containing guidelines for scrapers and other automated tools. You should always check and respect site rules before scraping.&lt;/p&gt;

&lt;h1&gt;
  
  
  Scraping Data From Page Content
&lt;/h1&gt;

&lt;p&gt;Now that you've got the basics down, let's explore how to scrape more useful data from a page. One key piece of functionality Puppeteer provides is the ability to query a page for HTML elements with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors" rel="noopener noreferrer"&gt;CSS selectors&lt;/a&gt;. For example, Puppeteer's &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pageevalselector-pagefunction-args" rel="noopener noreferrer"&gt;&lt;code&gt;page.$$eval()&lt;/code&gt;&lt;/a&gt; method takes a selector and allows you to run code in the context of the browser on all elements matching the selector.&lt;/p&gt;

&lt;p&gt;Here's what it looks like in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// authenticates you with the API standard library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;&lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STDLIB_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autocode-puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;videoData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="nf"&gt;$eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a#video-title-link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;titleLinkEls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;titleLinkEls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;titleLinkEl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;titleLinkEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://youtube.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;titleLinkEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;videoData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh6rgv349ppgd3gae26a1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh6rgv349ppgd3gae26a1.png" alt="The results of scraping video links and titles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once we've loaded YouTube in a page, we can use the &lt;code&gt;page.$$eval()&lt;/code&gt; function to query for each video link on the front page and return the name of the video and a link to it. We've essentially created a custom trending video API!&lt;/p&gt;

&lt;h1&gt;
  
  
  Crafting Selectors With Chrome Devtools
&lt;/h1&gt;

&lt;p&gt;One tricky part about creating scrapers is figuring out what elements of the page contain relevant data - after all, it's not immediately obvious that &lt;code&gt;a#video-title-link&lt;/code&gt; matches all the video links on YouTube. One convenient tool for this is the inspector in Chrome's devtools.&lt;/p&gt;

&lt;p&gt;You can open the inspector under &lt;strong&gt;View &amp;gt; Developer &amp;gt; Inspect Elements&lt;/strong&gt; in the topbar, or by using the keyboard shortcut &lt;strong&gt;CMD + Option + C&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjpx3qixio92pe8s4h90l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjpx3qixio92pe8s4h90l.png" alt="Accessing the Chrome inspector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you have the inspector open, you can mouse over elements on the page and see them highlighted. Clicking one will show the element in the devtools window, including its attributes and position in the DOM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8byhz5nwg6d36mz7sg4p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8byhz5nwg6d36mz7sg4p.png" alt="Highlighting elements in the Chrome inspector"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the inspector, you should be able to figure out a way to reference the correct elements and scrape the data you want.&lt;/p&gt;

&lt;h1&gt;
  
  
  Debugging With Screenshots
&lt;/h1&gt;

&lt;p&gt;Puppeteer allows you to take screenshots of pages using the &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pagescreenshotoptions" rel="noopener noreferrer"&gt;&lt;code&gt;page.screenshot()&lt;/code&gt;&lt;/a&gt; method. This can be especially useful to see the current page state when composing flows that involve interaction with &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pageclickselector-options" rel="noopener noreferrer"&gt;&lt;code&gt;page.click()&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pageselectselector-values" rel="noopener noreferrer"&gt;&lt;code&gt;page.select()&lt;/code&gt;&lt;/a&gt;, much like a &lt;code&gt;console.log&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;For example, let's say you want to build a flow that involves clicking on the first video on the front page of YouTube to scrape its like and dislike count. You might try something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// authenticates you with the API standard library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;&lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STDLIB_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autocode-puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
* An HTTP endpoint that acts as a webhook for HTTP(S) request event
* @returns {object.http} result
*/&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a#video-title-link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;screenshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To make the screenshot return as an image rather than just binary data, we must pass back the proper &lt;code&gt;Content-Type&lt;/code&gt; header. The above code is an example of how to do this in Autocode with the &lt;code&gt;object.http&lt;/code&gt; return type. To return other types of data, you'll need to remove the return type or change the return type to match the type of data you're returning. For more information, you can read about the &lt;a href="https://github.com/functionscript/functionscript#types" rel="noopener noreferrer"&gt;FunctionScript specification&lt;/a&gt; Autocode uses for parameters and return types.&lt;/p&gt;

&lt;p&gt;If you were to try running the above code a few times, you would notice that the screenshot is either not changing from the main page, or that you'd see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi0rkb9ecaa3i1r9832ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi0rkb9ecaa3i1r9832ag.png" alt="Loading state"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This would clue you in to the fact that you need to wait for parts of the page to load using something like &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pagewaitforselectorselector-options" rel="noopener noreferrer"&gt;&lt;code&gt;page.waitForSelector()&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// authenticates you with the API standard library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)({&lt;/span&gt;&lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STDLIB_SECRET_TOKEN&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;autocode-puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/**
* An HTTP endpoint that acts as a webhook for HTTP(S) request event
* @returns {object.http} result
*/&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.youtube.com/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a#video-title-link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#top-level-buttons&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;screenshot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;screenshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other use-cases include combining screenshots with the &lt;a href="https://docs.autocode.com/getting-started/creating-your-first-project/the-autocode-scheduler/" rel="noopener noreferrer"&gt;Autocode scheduler&lt;/a&gt;, a file hosting platform like &lt;a href="https://box.com" rel="noopener noreferrer"&gt;Box&lt;/a&gt;, and a package like &lt;a href="https://github.com/mapbox/pixelmatch" rel="noopener noreferrer"&gt;pixelmatch&lt;/a&gt; to create daily screenshot diffs for regression testing.&lt;/p&gt;

&lt;h1&gt;
  
  
  Other Tips
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Tuning Your Scraper
&lt;/h2&gt;

&lt;p&gt;The web is a wild place, and new standards are appearing all the time. If a technique doesn't work the way you expect on a first attempt, you may need to try a different method, like waiting for a selector or even just waiting for a set amount of time rather than a &lt;code&gt;DOMContentLoaded&lt;/code&gt; event, or using a different selector. Luckily, Puppeteer has plenty of options!&lt;/p&gt;

&lt;h2&gt;
  
  
  Increasing Your Autocode Timeout
&lt;/h2&gt;

&lt;p&gt;Complicated flows that involve visiting multiple pages or flows that visit slow pages may require more than the default 10 second timeout on Autocode. If your scraper is consistently timing out, you can try increasing the timeout under &lt;strong&gt;Advanced Settings&lt;/strong&gt; in the bottom left corner of the Autocode editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0aliamwshsebmvzb58xt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0aliamwshsebmvzb58xt.png" alt="Increasing your Autocode timeout"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Free accounts can increase their timeouts to up to 30 seconds, while &lt;a href="https://autocode.com/pricing" rel="noopener noreferrer"&gt;Professional Tier accounts&lt;/a&gt; can go up to 2 minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simulating Logged-In States (Advanced)
&lt;/h2&gt;

&lt;p&gt;Many websites show different data based on whether or not the current viewer is logged in. To simulate this state in Puppeteer, you might be able to use the &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pagesetcookiecookies" rel="noopener noreferrer"&gt;&lt;code&gt;page.setCookie()&lt;/code&gt;&lt;/a&gt; method using cookies obtained from the Chrome devtools. You can also attempt to use and submit a site's login form directly with your username and password, but many sites use CAPTCHAs to prevent this.&lt;/p&gt;

&lt;p&gt;Be careful not to set cookies under the wrong domain if you try this!&lt;/p&gt;

&lt;h1&gt;
  
  
  Limitations
&lt;/h1&gt;

&lt;p&gt;Because web scrapers usually rely on the structure of the DOM for pages they visit, frontend updates can break scrapers. For this reason, it's often better to use a supported API if it is available to you. &lt;/p&gt;

&lt;p&gt;If your desired data follows a specific format and the page you're scraping obfuscates their CSS attributes or changes them frequently, Puppeteer does have a &lt;a href="https://pptr.dev/#?product=Puppeteer&amp;amp;version=v5.5.0&amp;amp;show=api-pagecontent" rel="noopener noreferrer"&gt;&lt;code&gt;page.content()&lt;/code&gt;&lt;/a&gt; method that returns the current DOM structure of the page as a string. You can then use a regex or some other method to extract your data.&lt;/p&gt;

&lt;h1&gt;
  
  
  Thank You!
&lt;/h1&gt;

&lt;p&gt;Puppeteer is an extremely powerful tool, and if you're interested in a deeper dive, I would encourage you to check out &lt;a href="https://pptr.dev" rel="noopener noreferrer"&gt;the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, a good place to get in touch with me is the Autocode community Slack channel. You can get an invite under &lt;strong&gt;Docs &amp;gt; Ask for Help in Slack&lt;/strong&gt; in the topbar on autocode.com. You can also reach out to me on Twitter &lt;a href="https://twitter.com/Hacubu" rel="noopener noreferrer"&gt;@Hacubu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to stay up to date on the latest from Autocode, I would also encourage you to follow &lt;a href="https://twitter.com/AutocodeHQ" rel="noopener noreferrer"&gt;@AutocodeHQ&lt;/a&gt;. Happy hacking!&lt;/p&gt;

</description>
      <category>webscraping</category>
      <category>node</category>
      <category>puppeteer</category>
      <category>autocode</category>
    </item>
    <item>
      <title>How to Use Airtable as a Production Database (Analyzing Airtable Performance)</title>
      <dc:creator>Jacob Lee</dc:creator>
      <pubDate>Fri, 11 Dec 2020 07:56:03 +0000</pubDate>
      <link>https://dev.to/hacubu/how-to-use-airtable-as-a-production-database-analyzing-airtable-performance-41e9</link>
      <guid>https://dev.to/hacubu/how-to-use-airtable-as-a-production-database-analyzing-airtable-performance-41e9</guid>
      <description>&lt;p&gt;I've been an &lt;a href="https://airtable.com" rel="noopener noreferrer"&gt;Airtable&lt;/a&gt; user for around three years now, and over that span I've recommended it to more people than any other SaaS product. It "Just Works™" — while it has its limitations and doesn't provide all the flexibility of a relational database, the sheer quality of the UX often makes up for any shortcomings. Like &lt;a href="https://dev.to/hacubu/how-to-use-google-sheets-as-a-database-responsibly-3ohk"&gt;Google Sheets&lt;/a&gt;, Airtable particularly shines as a backend for apps where non-technical team members might need to examine or edit data, and the structure it provides through features like types, access control, views, and computed fields make working with it pleasant.&lt;/p&gt;

&lt;p&gt;In this article, we'll go over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to use Airtable as a database with some pseudo-join capabilities&lt;/li&gt;
&lt;li&gt;How to set up an HTTP-accessible API for your Airtable base that you can send requests to from a frontend&lt;/li&gt;
&lt;li&gt;Some trade-offs to consider when selecting Airtable for your project (like how the Airtable API scales)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz2cyjcrjk6frlsltanx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz2cyjcrjk6frlsltanx6.png" alt="The example Airtable base"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Returns all records from the "My Books" table with genre "Fantasy", case-sensitive&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/airtable-db-examples@dev/select/my_books/genre/is/?query=Fantasy'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Rhythm of War"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Genre"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fantasy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Published On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2020-11-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Author Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"rec00000000000000"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Brandon Sanderson"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The Doors of Stone"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Genre"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Fantasy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Published On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Author Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"rec00000000000000"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Patrick Rothfuss"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To host the sample app and handle authentication to the Airtable API, we'll use &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;Autocode&lt;/a&gt;, a Node.js app platform and editor with built-in autocomplete. Both &lt;a href="https://airtable.com/pricing/" rel="noopener noreferrer"&gt;Airtable&lt;/a&gt; and &lt;a href="https://autocode.com/pricing/" rel="noopener noreferrer"&gt;Autocode&lt;/a&gt; are free to start, so let's dive in!&lt;/p&gt;

&lt;h1&gt;
  
  
  TL;DR (30s)
&lt;/h1&gt;

&lt;p&gt;First, create your own copy of the &lt;a href="https://airtable.com/addBaseFromShare/shrkGIiwUFcIz4Te4" rel="noopener noreferrer"&gt;demo Airtable base by clicking here&lt;/a&gt; and following the prompt.&lt;/p&gt;

&lt;p&gt;Once you've done that, &lt;a href="https://autocode.com/app/airtable/airtable-db-examples/" rel="noopener noreferrer"&gt;click here to open the Airtable cookbook in Autocode&lt;/a&gt;, then press the green button to install the cookbook to your Autocode account. When asked to link an Airtable base, supply your API key and select the base you just cloned.&lt;/p&gt;

&lt;p&gt;After that, you're live! You can try hitting some of the endpoints from your browser or cURL right away (the URLs will look something like &lt;code&gt;https://YOUR_USERNAME.api.stdlib.com/airtable-db-examples@dev/select/my_books/genre/is/?query=Fantasy&lt;/code&gt;. You can also check out the &lt;a href="https://autocode.com/app/airtable/airtable-db-examples/" rel="noopener noreferrer"&gt;README for the Airtable cookbook&lt;/a&gt; for more concrete examples.&lt;/p&gt;

&lt;h1&gt;
  
  
  Limitations
&lt;/h1&gt;

&lt;p&gt;Now that we've got a basic example set up, let's talk about some things you should consider when choosing between Airtable and other solutions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;To gauge performance, I ran three experiments: &lt;strong&gt;single record retrieval&lt;/strong&gt;, &lt;strong&gt;full table retrieval&lt;/strong&gt;, and a &lt;strong&gt;50,000 record table query test&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Record Retrieval
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Methodology
&lt;/h4&gt;

&lt;p&gt;For this experiment, I created tables containing various numbers of records with a with an autonumber field called &lt;code&gt;Key&lt;/code&gt; and a text field called &lt;code&gt;Value&lt;/code&gt;. I populated each &lt;code&gt;Value&lt;/code&gt; with a single character. I then changed an arbitrary record's &lt;code&gt;Value&lt;/code&gt; field to the string &lt;code&gt;"testing"&lt;/code&gt;, then queried for records matching that &lt;code&gt;Value&lt;/code&gt; using the &lt;a href="https://autocode.com/lib/airtable/query" rel="noopener noreferrer"&gt;airtable.query&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Results
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fapiwaq9iyazghds99cfz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fapiwaq9iyazghds99cfz.png" alt="Single record retrieval test results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Airtable API performed spectacularly on this test, retrieving a single record in about the same time for table sizes from 10 to 50,000 records. There was a small increase in query time as the number of records in the table grew, but overall stayed fairly consistent. This result suggests that the Airtable API is well optimized for queries that return a small number of results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full Table Retrieval
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Methodology
&lt;/h4&gt;

&lt;p&gt;For this experiment, I used the same tables as the previous experiment, but left the &lt;code&gt;where&lt;/code&gt; parameter blank in my &lt;a href="https://autocode.com/lib/airtable/query" rel="noopener noreferrer"&gt;airtable.query&lt;/a&gt; API call to select for all records in the table.&lt;/p&gt;

&lt;h4&gt;
  
  
  Results
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh4eb42q10lovd16dpkus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fh4eb42q10lovd16dpkus.png" alt="Full table retrieval test results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Airtable API performed worse on this test, crossing the 2s mark after retrieving 500 records and quickly becoming impractically slow after ~1,000 records.&lt;/p&gt;

&lt;h3&gt;
  
  
  50,000 Record Table Queries
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Methodology
&lt;/h4&gt;

&lt;p&gt;For this experiment, I wanted to see if querying with parameters would perform better than retrieving an entire table. Starting with the same table structure, I added a third field that I populated with the following (hacky) formula to simulate an even distribution of numbers (Airtable does not have a built-in random function):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MOD((MOD(VALUE(RECORD_ID()), 50000) + 1) * {Key}, 50000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;{Key}&lt;/code&gt; is the autonumbered field we defined earlier. &lt;code&gt;RECORD_ID()&lt;/code&gt;, a function returning a random looking id that Airtable internally assigns each record, did not contain enough entropy on its own to evenly distribute numbers, and often evaluated to zero when evaluated as a number using the &lt;code&gt;VALUE()&lt;/code&gt; Airtable formula operator, hence the hack of adding 1.&lt;/p&gt;

&lt;p&gt;In practice, this formula returned values spread out sufficiently to ensure a roughly even distribution from 0 to 50,000 for each record, though using the generated values as random numbers for any important application is not recommended. I then queried the base for records where this third computed field was below a certain threshold to simulate a query matching a given number of "randomly selected" records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sidenote:&lt;/strong&gt; For more on Airtable formula operators, &lt;a href="https://support.airtable.com/hc/en-us/articles/203255215-Formula-field-reference" rel="noopener noreferrer"&gt;check out their official reference here&lt;/a&gt;. The endpoints in the sample app all use &lt;a href="https://github.com/FunctionScript/KeyQL" rel="noopener noreferrer"&gt;KeyQL parameters&lt;/a&gt; to make queries, but if you'd prefer to query Airtable using formulas, you can use &lt;a href="https://autocode.com/lib/airtable/query/#records-find-formula" rel="noopener noreferrer"&gt;this endpoint on Autocode&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Results
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl3mqzfz87yzr417oj71t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl3mqzfz87yzr417oj71t.png" alt="50,000 record table query test results"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The results for this test were similar to the full table retrieval test, showing that performance correlated mostly with the number of returned records.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;The Airtable API's performance scales, surprisingly, with the number of records returned in a given query rather than the size of the table. Even at table sizes at plan limits, the Airtable API performs well for queries that return small numbers of records. Unfortunately, Airtable formulas don't have a built-in concept of pagination, and the &lt;a href="https://github.com/FunctionScript/KeyQL" rel="noopener noreferrer"&gt;KeyQL parameters translate to formulas&lt;/a&gt; under the hood, so the KeyQL limit parameter does not get around this issue.&lt;/p&gt;

&lt;p&gt;Adding extra fields to the records in the table seemed to have a negligible impact on performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Capacity and Throughput
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://support.airtable.com/hc/en-us/articles/115010928147-Airtable-plans#pro" rel="noopener noreferrer"&gt;Airtable's top non-enterprise plan&lt;/a&gt; is $24 a month and has an upper limit of 50,000 records per base. For free plan users, this limit is 1,200 records per base. &lt;/p&gt;

&lt;p&gt;Even for projects where you expect to exceed these limits, it may be better to avoid premature optimization and choose Airtable for ease-of-use, migrating if and when scale becomes a problem.&lt;/p&gt;

&lt;p&gt;Airtable sets a &lt;a href="https://community.airtable.com/t/api-request-daily-limit/34175" rel="noopener noreferrer"&gt;rate limit of 5 requests per second per base&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relationships Between Tables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Linked Record Fields
&lt;/h3&gt;

&lt;p&gt;Airtable allows you to associate records in one table with records in another through a special type of field called a &lt;a href="https://support.airtable.com/hc/en-us/articles/360021502354#h_0c3d1a65-1f35-4e67-9b14-6213d4d2bca9" rel="noopener noreferrer"&gt;linked record field&lt;/a&gt;. In the &lt;a href="https://autocode.com/app/airtable/airtable-db-examples/" rel="noopener noreferrer"&gt;example base&lt;/a&gt; from the TL;DR above, the &lt;code&gt;Author Id&lt;/code&gt; field on the &lt;code&gt;My Books&lt;/code&gt; table is an example of a linked record field, associating records from the &lt;code&gt;My Books&lt;/code&gt; table with those from the &lt;code&gt;Authors&lt;/code&gt; table.&lt;/p&gt;

&lt;p&gt;If you're familiar with frameworks like &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Ruby on Rails&lt;/a&gt; or &lt;a href="https://www.djangoproject.com/" rel="noopener noreferrer"&gt;Django&lt;/a&gt;, you'll immediately think of a linked record as creating a relationship between the two tables. And it does, with the following caveats:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can't select the field you're joining on. It must be the primary field for the table you're joining to.&lt;/li&gt;
&lt;li&gt;When querying via API, all relationships are represented as many-to-many and are returned as arrays.&lt;/li&gt;
&lt;li&gt;You can't make queries that use joins.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;#3&lt;/strong&gt; is a particular bummer if you were hoping to use Airtable as a drop-in replacement for something like &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;Postgres&lt;/a&gt;. However, there are some neat tricks we can use!&lt;/p&gt;

&lt;h3&gt;
  
  
  Lookup Fields
&lt;/h3&gt;

&lt;p&gt;You may have also noticed in the example base that there is a field called &lt;code&gt;Author&lt;/code&gt; in the &lt;code&gt;My Books&lt;/code&gt; table. &lt;code&gt;Author&lt;/code&gt; is another special type of field called a &lt;a href="https://support.airtable.com/hc/en-us/articles/360042312194-Lookup-field-overview" rel="noopener noreferrer"&gt;lookup field&lt;/a&gt; which, as the name implies, allows a record in one table to lookup data from another field. In this case, the &lt;code&gt;Author&lt;/code&gt; field on records in &lt;code&gt;My Books&lt;/code&gt; contains the value of the &lt;code&gt;Name&lt;/code&gt; field for the proper linked record in the &lt;code&gt;Authors&lt;/code&gt; table. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz2cyjcrjk6frlsltanx6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fz2cyjcrjk6frlsltanx6.png" alt="The example Airtable base"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Those familiar with database theory will red-flag this as &lt;a href="https://en.wikipedia.org/wiki/Database_normalization" rel="noopener noreferrer"&gt;denormalization of data&lt;/a&gt; — essentially caching copies of a field in two places. This is usually difficult to deal with, but Airtable populates and updates lookup fields automatically, handling the complexity for you.&lt;/p&gt;

&lt;p&gt;Airtable returns the value of lookup fields along with other types of fields on a record. You can use the &lt;a href="https://autocode.com/lib/airtable/query" rel="noopener noreferrer"&gt;airtable.query&lt;/a&gt; API to query records by the value of a lookup field like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bookQueryResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;airtable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Books&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Author__contains&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As previously mentioned, Airtable treats lookup fields as arrays, even if they only contain one value. Therefore, you should prefer &lt;code&gt;contains&lt;/code&gt; or &lt;code&gt;icontains&lt;/code&gt; when querying them with &lt;a href="https://github.com/FunctionScript/KeyQL" rel="noopener noreferrer"&gt;KeyQL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Through smart use of lookup fields, you can achieve results similar to basic joins in a traditional relational database. Of course, the more lookup fields you add, the more complex your base will become. If you find yourself needing to do this too often, it might make sense to make multiple queries for data from different tables or to choose a different database solution.&lt;/p&gt;

&lt;h1&gt;
  
  
  How It Works
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://docs.autocode.com/introduction-to-autocode/components-of-autocode/api-authentication-management/" rel="noopener noreferrer"&gt;Autocode automatically handles authentication&lt;/a&gt; between your app and your Airtable account. When you link an Airtable base, Autocode associates your provided Airtable key and the linked base to your app's token (see the &lt;code&gt;const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN})&lt;/code&gt; line at the top of all the endpoints in the sample cookbook). When you call an Airtable API with &lt;code&gt;await lib.airtable&lt;/code&gt;, Autocode makes the appropriate credentials available to the API, allowing it act as a proxy and use your credentials to call Airtable on your behalf.&lt;/p&gt;

&lt;p&gt;You'll notice each endpoint contains Node.js code that calls a method from the &lt;a href="https://autocode.com/lib/airtable/query/" rel="noopener noreferrer"&gt;airtable.query API&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bookQueryResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;airtable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My Books&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Genre__is&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Though there is an Airtable endpoint on Autocode that allows you to use &lt;a href="https://autocode.com/lib/airtable/query/#records-find-formula" rel="noopener noreferrer"&gt;Airtable formulas directly&lt;/a&gt;, the API calls in the sample project use the &lt;a href="https://github.com/FunctionScript/KeyQL" rel="noopener noreferrer"&gt;KeyQL query language&lt;/a&gt;. Check it out if you're interested in seeing more ways you can use KeyQL, including other available operators, and how it converts parameters to Airtable formulas.&lt;/p&gt;

&lt;h1&gt;
  
  
  Calling Endpoints
&lt;/h1&gt;

&lt;p&gt;Because these API endpoints are accessible via HTTP, so you can make calls to them via fetch, cURL, or whatever other HTTP client you prefer. You can use your web browser directly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1xfinq574v7aic88rit1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1xfinq574v7aic88rit1.png" alt="Airtable records returned from calling API endpoint from the web browser"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can even use the same &lt;a href="https://github.com/stdlib/lib-node" rel="noopener noreferrer"&gt;lib-node&lt;/a&gt; package that the endpoints use to call the Airtable APIs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F95ehwwp75lezndy58kdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F95ehwwp75lezndy58kdk.png" alt="Calling API endpoints with the lib package"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your endpoints will respond to either GET or POST requests. Parameters are parsed from the querystring for GET requests and the request body for POST requests. Each endpoint has default parameters set for the sake of clarity.&lt;/p&gt;

&lt;h1&gt;
  
  
  Endpoints
&lt;/h1&gt;

&lt;p&gt;For a full explanation of each endpoint in the example cookbook, along with usage examples, check out the &lt;a href="https://autocode.com/app/airtable/airtable-db-examples/" rel="noopener noreferrer"&gt;Autocode Airtable cookbook page&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Thank You!
&lt;/h1&gt;

&lt;p&gt;If you have any questions or feedback, the best thing to do is to join the Autocode community Slack channel. You can get an invite from the question mark tab in the top bar on &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;autocode.com&lt;/a&gt;. You can also reach out to me directly on Twitter &lt;a href="https://twitter.com/hacubu" rel="noopener noreferrer"&gt;@Hacubu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to stay up to date on the latest from Autocode, you can follow &lt;a href="https://twitter.com/AutocodeHQ" rel="noopener noreferrer"&gt;@AutocodeHQ&lt;/a&gt;. Happy hacking!&lt;/p&gt;

</description>
      <category>airtable</category>
      <category>api</category>
      <category>database</category>
      <category>autocode</category>
    </item>
    <item>
      <title>How to Use Google Sheets as a Database (Responsibly)</title>
      <dc:creator>Jacob Lee</dc:creator>
      <pubDate>Wed, 07 Oct 2020 07:00:16 +0000</pubDate>
      <link>https://dev.to/hacubu/how-to-use-google-sheets-as-a-database-responsibly-3ohk</link>
      <guid>https://dev.to/hacubu/how-to-use-google-sheets-as-a-database-responsibly-3ohk</guid>
      <description>&lt;p&gt;While database technology and other tools have come a long way, it's still tough to beat the humble spreadsheet's versatility and intuitiveness. While using them for sensitive, critical applications like storing COVID-19 patient data is ill-advised, the fact that everyone knows how to use a spreadsheet means they're great for smaller cross-functional projects where a non-developer might need to examine or edit data.&lt;/p&gt;

&lt;p&gt;In this guide, I'll show you how to use Google Sheets as a database, complete with an API interface accessible over HTTP. We'll use &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;Autocode&lt;/a&gt;, a Node.js API development platform and editor with built-in autocomplete, to deploy a simple app and handle Google's authentication process. I'll also explore the limitations of Google Sheets, including scalability, and where it makes sense to start looking at more complex alternatives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdu2ha4sdmc25q.cloudfront.net%2Fgooglesheets%2Fgsheets-database-example%2Fsrc_ZRXDowU7gZg4aUcmXfdXD8DByy7K8RkFdPGc%2Freadme%2Fgallery%2F1-spreadsheet-template.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdu2ha4sdmc25q.cloudfront.net%2Fgooglesheets%2Fgsheets-database-example%2Fsrc_ZRXDowU7gZg4aUcmXfdXD8DByy7K8RkFdPGc%2Freadme%2Fgallery%2F1-spreadsheet-template.png"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Returns all people in the database whose names start &lt;/span&gt;
&lt;span class="c"&gt;# with "bil", case-insensitive&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/gsheets-database-example/select/name/istartswith/?query=bil'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bilbo Baggins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Burglar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TRUE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9/21/1937"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bill Nye"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Scientist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FALSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"11/27/1955"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billie eilish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Artist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FALSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12/18/2001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need anything other than a Google account and a free Autocode account to get started. Let's go!&lt;/p&gt;

&lt;h1&gt;
  
  
  TL;DR (30s)
&lt;/h1&gt;

&lt;p&gt;First, you'll need to clone your own copy of the template Google Sheet by &lt;a href="https://docs.google.com/spreadsheets/u/1/d/1Lhj0s2X9YD_4WIQ3DpubXLKevok4PqFP3EwDzVLDqyQ/template/preview" rel="noopener noreferrer"&gt;clicking here, then pressing &lt;strong&gt;Use Template&lt;/strong&gt; in the top right&lt;/a&gt;. This will add the sample spreadsheet to your personal Google account. &lt;/p&gt;

&lt;p&gt;Once you've done that, &lt;a href="https://autocode.com/app/googlesheets/gsheets-database-example/" rel="noopener noreferrer"&gt;click here to open the starter app in Autocode&lt;/a&gt;. Poke around the source code if you'd like, then install the app to your Autocode account by pressing the green button. When prompted to link a Google Sheet, follow the instructions in the flow to link your Google account, then select the spreadsheet you just cloned.&lt;/p&gt;

&lt;p&gt;After that, your app should be ready to go! Try accessing a few of the endpoints via their URLs and see what is returned/what happens to your new spreadsheet database. You can check out the &lt;code&gt;Endpoints&lt;/code&gt; section below for example calls.&lt;/p&gt;

&lt;h1&gt;
  
  
  Limitations
&lt;/h1&gt;

&lt;p&gt;That tl;dr was easy, right? So why isn't everything built on Google Sheets? &lt;/p&gt;

&lt;p&gt;While a backend that takes 30 seconds to set up and that everyone can interact with is extremely appealing, there are some obvious limitations. A spreadsheet as a database as described above doesn't naturally support multiple tables or relationships between rows. There's also no concept of enforcing types for a given column, transactions, built-in backups, or encryption, so sensitive/critical data (like COVID-19 patient data) is probably best stored elsewhere.&lt;/p&gt;

&lt;p&gt;In terms of scalability, Google Sheets has a &lt;a href="https://support.google.com/drive/answer/37603" rel="noopener noreferrer"&gt;hard limit of 5,000,000 cells&lt;/a&gt; (including blank cells). When I tried to verify this by creating a spreadsheet with that many values, however, I encountered significant issues in performance before that threshold:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstdlib-static.s3.us-west-2.amazonaws.com%2Ffiles%2Fautocode%2Fgoogle-sheets-unresponsive.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstdlib-static.s3.us-west-2.amazonaws.com%2Ffiles%2Fautocode%2Fgoogle-sheets-unresponsive.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mass-operations like pasting a large number of cells slowed, then began to fail at around 1m cells. Navigation was generally sluggish. &lt;/p&gt;

&lt;p&gt;My experiments around making API calls yielded similar results. Query speed seemed to scale linearly with the number of cells: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstdlib-static.s3.us-west-2.amazonaws.com%2Ffiles%2Fautocode%2Fgoogle-sheets-api-benchmarks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstdlib-static.s3.us-west-2.amazonaws.com%2Ffiles%2Fautocode%2Fgoogle-sheets-api-benchmarks.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Queries became impractically slow around the 500,000 cell mark, but were still below 2 seconds for a 100,000 cell query. Therefore, if you anticipate a dataset larger than a few hundred thousand cells, it would probably be smart to choose a more scalable option. &lt;/p&gt;

&lt;h1&gt;
  
  
  How It Works
&lt;/h1&gt;

&lt;p&gt;When you link your cloned Google Sheet to your app and install it to your account, Autocode automatically handles authentication between your app and your Google account using your app's token (see the &lt;code&gt;const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN})&lt;/code&gt; line at the top of all the endpoints).&lt;/p&gt;

&lt;p&gt;For the actual queries, each endpoint contains Node.js code that calls a method from the &lt;a href="https://autocode.com/lib/googlesheets/query" rel="noopener noreferrer"&gt;googlesheets.query API&lt;/a&gt;. These APIs take a parameter called &lt;code&gt;range&lt;/code&gt; formatted in &lt;a href="https://developers.google.com/sheets/api/guides/concepts" rel="noopener noreferrer"&gt;A1 notation&lt;/a&gt; that corresponds to the part of the spreadsheet the API call should consider as part of the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;queryResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;googlesheets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@0.3.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;range&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`A:E`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;FULL_RANGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Name__istartswith&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;range&lt;/code&gt; value of &lt;code&gt;A:E&lt;/code&gt; is essentially shorthand for "use all rows in columns &lt;strong&gt;A&lt;/strong&gt; through &lt;strong&gt;E&lt;/strong&gt; in the spreadsheet as my database". The query interprets the first row of each column in that range as the field name of the values in that column. Given the template you cloned, the above query will check all values in the &lt;strong&gt;A&lt;/strong&gt; column (named &lt;code&gt;Name&lt;/code&gt;) for rows with a value matching the query.&lt;/p&gt;

&lt;p&gt;These API calls use the &lt;a href="https://github.com/FunctionScript/KeyQL" rel="noopener noreferrer"&gt;KeyQL query language&lt;/a&gt;. If you're interested in a deep dive, you can check it out for more examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Calling Endpoints
&lt;/h2&gt;

&lt;p&gt;As previously mentioned, these endpoints are accessible via HTTP, so you can make calls to them via &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;cURL&lt;/code&gt;, or whatever other HTTP client you prefer. You can use your web browser directly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdu2ha4sdmc25q.cloudfront.net%2Fgooglesheets%2Fgsheets-database-example%2Fsrc_ZRXDowU7gZg4aUcmXfdXD8DByy7K8RkFdPGc%2Freadme%2Fgallery%2F2-browser-access.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdu2ha4sdmc25q.cloudfront.net%2Fgooglesheets%2Fgsheets-database-example%2Fsrc_ZRXDowU7gZg4aUcmXfdXD8DByy7K8RkFdPGc%2Freadme%2Fgallery%2F2-browser-access.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can even use the same &lt;a href="https://github.com/stdlib/lib-node/" rel="noopener noreferrer"&gt;lib-node&lt;/a&gt; Node package that the endpoints use to call the Google Sheets APIs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdu2ha4sdmc25q.cloudfront.net%2Fgooglesheets%2Fgsheets-database-example%2Fsrc_ZRXDowU7gZg4aUcmXfdXD8DByy7K8RkFdPGc%2Freadme%2Fgallery%2F3-lib-access.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdu2ha4sdmc25q.cloudfront.net%2Fgooglesheets%2Fgsheets-database-example%2Fsrc_ZRXDowU7gZg4aUcmXfdXD8DByy7K8RkFdPGc%2Freadme%2Fgallery%2F3-lib-access.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your endpoints will respond to either GET or POST requests. Parameters are parsed from the querystring for GET requests and the request body for POST requests. Each endpoint has default parameters set for the sake of clarity. You can find examples for each endpoint below.&lt;/p&gt;

&lt;h1&gt;
  
  
  Endpoints
&lt;/h1&gt;

&lt;h3&gt;
  
  
  functions/select/job/contains.js
&lt;/h3&gt;

&lt;p&gt;This endpoint is an example of a &lt;code&gt;contains&lt;/code&gt; KeyQL query. It looks for rows in the linked Google Sheet where the &lt;code&gt;Job&lt;/code&gt; field contains a substring (case-sensitive) matching the parameter &lt;code&gt;query&lt;/code&gt;. From the sample sheet, it returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/gsheets-database-example/select/job/contains/?query=ist'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mistborn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2006-07-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TRUE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vin Venture"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Scientist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1955-11-27"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bill Nye"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FALSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Artist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2001-12-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billie eilish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FALSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  functions/select/born_on/date_gt.js
&lt;/h3&gt;

&lt;p&gt;This endpoint is an example of a &lt;code&gt;date_gt&lt;/code&gt; KeyQL query. It looks for rows in the linked Google Sheet where the &lt;code&gt;Born On&lt;/code&gt; field is after the &lt;code&gt;query&lt;/code&gt; parameter, formatted as &lt;code&gt;2000/01/01&lt;/code&gt;. From the sample sheet, it returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/gsheets-database-example/select/born_on/date_gt/?query=2000/01/01'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mistborn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2006/07/17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TRUE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vin Venture"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Artist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2001/12/18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billie eilish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FALSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  functions/select/name/istartswith.js
&lt;/h3&gt;

&lt;p&gt;This endpoint is an example of a &lt;code&gt;istartswith&lt;/code&gt; KeyQL query. It looks for rows in the linked Google Sheet where the &lt;code&gt;Name&lt;/code&gt; field starts with the &lt;code&gt;query&lt;/code&gt; parameter (case-insensitive). From the sample sheet, it returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/gsheets-database-example/select/name/istartswith/?query=bil'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Scientist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1955-11-27"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bill Nye"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FALSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Artist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2001-12-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"billie eilish"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FALSE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Job"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Burglar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Born On"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1937-09-21"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Fictional"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TRUE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bilbo Baggins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Updated At"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  functions/insert.js
&lt;/h3&gt;

&lt;p&gt;This endpoint is an example of an insert query. It passes the input parameters into the &lt;code&gt;fieldsets&lt;/code&gt; parameter of the &lt;a href="https://autocode.com/lib/googlesheets/query/#insert" rel="noopener noreferrer"&gt;googlesheets.query.insert&lt;/a&gt;&lt;br&gt;
API. For example, to add &lt;code&gt;Bill Gates&lt;/code&gt; to your spreadsheet, you could make the following request (all parameters are lower-case):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"Bill Gates","job":"CEO","fictional":false,"bornOn":"10/28/1955"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/gsheets-database-example/insert/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Autocode APIs do not respond exclusively one HTTP method over another, and instead rely on descriptive naming to avoid confusion over functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  functions/update.js
&lt;/h3&gt;

&lt;p&gt;This endpoint is an example of an update query. It sets the &lt;code&gt;Updated At&lt;/code&gt; field of people whose names exactly match the &lt;code&gt;name&lt;/code&gt; parameter, and updates other fields based on input parameters. It uses the &lt;a href="https://autocode.com/lib/googlesheets/query/#update" rel="noopener noreferrer"&gt;googlesheets.query.update API&lt;/a&gt;.&lt;br&gt;
For example, to update the &lt;code&gt;Job&lt;/code&gt; field of &lt;code&gt;Bilbo Baggins&lt;/code&gt; to &lt;code&gt;Ring Bearer&lt;/code&gt; in your spreadsheet, you could make the following request (all parameters are lower-case):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--header&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'{"name":"Bilbo Baggins","job":"Ring Bearer"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/gsheets-database-example/update/'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This can affect multiple rows if more than one row matches the query conditions.&lt;/p&gt;

&lt;h3&gt;
  
  
  functions/delete.js
&lt;/h3&gt;

&lt;p&gt;This endpoint is an example of a delete query. It removes rows &lt;br&gt;
of people whose names exactly match the &lt;code&gt;name&lt;/code&gt; parameter. It uses the &lt;a href="https://autocode.com/lib/googlesheets/query/#delete" rel="noopener noreferrer"&gt;googlesheets.query.delete API&lt;/a&gt;. For example, to remove &lt;code&gt;Bilbo Baggins&lt;/code&gt; from your spreadsheet, you could make the following request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--request&lt;/span&gt; GET &lt;span class="nt"&gt;--url&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'https://YOUR_USERNAME.api.stdlib.com/gsheets-database-example/delete/?name=Bilbo%20Baggins'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This can affect multiple rows if more than one row matches the query conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank You!
&lt;/h2&gt;

&lt;p&gt;If you have any questions or feedback, the best thing to do is to join the Autocode community Slack channel. You can get an invite from the &lt;strong&gt;Community&lt;/strong&gt; tab in the top bar on &lt;a href="https://autocode.com" rel="noopener noreferrer"&gt;the website&lt;/a&gt;. You can also reach out to me directly on Twitter &lt;a href="https://twitter.com/hacubu" rel="noopener noreferrer"&gt;@Hacubu&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to stay up to date on the latest from Autocode, you can follow &lt;a href="https://twitter.com/AutocodeHQ" rel="noopener noreferrer"&gt;@AutocodeHQ&lt;/a&gt;. Happy hacking!&lt;/p&gt;

</description>
      <category>googlesheets</category>
      <category>database</category>
      <category>api</category>
      <category>autocode</category>
    </item>
  </channel>
</rss>
