惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
D
Darknet – Hacking Tools, Hacker News & Cyber Security
量子位
博客园 - 叶小钗
AI
AI
T
Tor Project blog
Forbes - Security
Forbes - Security
W
WeLiveSecurity
博客园_首页
爱范儿
爱范儿
J
Java Code Geeks
B
Blog
G
GRAHAM CLULEY
aimingoo的专栏
aimingoo的专栏
Cloudbric
Cloudbric
C
CXSECURITY Database RSS Feed - CXSecurity.com
TaoSecurity Blog
TaoSecurity Blog
L
LINUX DO - 热门话题
阮一峰的网络日志
阮一峰的网络日志
有赞技术团队
有赞技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
H
Help Net Security
博客园 - 三生石上(FineUI控件)
C
Cisco Blogs
C
Cybersecurity and Infrastructure Security Agency CISA
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
P
Palo Alto Networks Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 司徒正美
The Last Watchdog
The Last Watchdog
Blog — PlanetScale
Blog — PlanetScale
T
The Blog of Author Tim Ferriss
S
Secure Thoughts
Spread Privacy
Spread Privacy
F
Fortinet All Blogs
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
H
Hackread – Cybersecurity News, Data Breaches, AI and More
A
About on SuperTechFans
Security Latest
Security Latest
Webroot Blog
Webroot Blog
Scott Helme
Scott Helme
Hugging Face - Blog
Hugging Face - Blog

Buttondown's blog

Email could have been X.400 times better The physicists who convinced Fermilab to send Brazil's emails Better in-app previews Analytics 3.0 Subscriber ID variables Comments! Send latest premium action Automation filtering Free API subscribers Surveys in automations Reply to replies Labels for RSS feeds How Jeremy Singer-Vine curates curious datasets for readers 2023 (and what's next) Email vs web content Sort by engagement Better gift subscriptions How Andy Dehnart built a career reviewing television New email template Email-based automations Opt-in reply tracking Automatic alt text More social network integrations Sort by metadata Overlarge image warnings Automation tag actions Pause emails mid-flight Search tags and automations Gift via automations Subscriber-driving emails Programmatic webhooks Email page views Tag statistics Discord webhook formatting Automatic subscriber cleanup RSS subscriber count Weekly subscriber reports More list columns Customizable list views How Max Voltar turned a side gig into a trusted keyboard resource How Nick Disabato runs two newsletters from one design consultancy Made-for-you share images Automation improvements End-of-email surveys Filter by date Survey-triggered automations More automation functionality New webhooks How France Insider built a news service with paid subscribers Email as primary key How John Willshire unites two businesses in one newsletter Confirmation reminders Email churned subscribers Email-to-draft Subscriber metadata columns ChatGPT integration Faster web archives Referral program Better search results TikTok embeds Subscriber timeline Spotify embeds Improved RSS-to-email Subscribe page OG image New analytics page Google Tag Manager Even more subscriber types Integrating Duda with Buttondown Linktree integration guide Advanced and enterprise plans Framer integration guide API requests page Team collaboration In-email surveys Better CSS settings Better RSS automation fetching! Editor toolbar improvements Smart filters Faster emails page RSS automations Faster email analytics Zapier error codes Image accessibility checks Tags vs newsletters OG image picker Image editor improvements API bulk actions Improved OpenAPI spec Mastodon support Better subscriber filtering Better subscriber validation Hotkey support! Programmatic access to analytics Stronger bulk actions Faster archive page Custom canonical URLs Email slug and metadata Improved writing interface Generating a Typescript router in Django Filter emails by source
Netlify functions + Buttondown
Andrew Stiefel · 2023-03-21 · via Buttondown's blog

I enjoy the process of designing and building a custom personal website. It’s a great way to experiment with different technologies and have fun learning new tools and concepts along the way.

This time, I was eager to learn how to use serverless functions.

The Challenge

I use a static site generator called Jekyll to build and publish my website. The result is a collection of static HTML, CSS, and JS files that are lightweight, fast, and easy to maintain. It reduces my overhead, both in cost and time because I don’t need to manage a server. But it does have one drawback: I can’t easily handle small background actions, like form submissions.

After all the effort I put into building my personal website, the last thing I wanted to do was slap a pre-designed newsletter subscription form on it. Most email providers offer forms you can quickly embed on your website, but they come with a series of tradeoffs. They are difficult to customize to exactly match your website’s design, and they usually require users to load third-party scripts and styles. Often these third-party resources get blocked by browser privacy settings, and even if they don’t, they can be slow to load.

Eventually, this led me to serverless functions. A serverless function is really just a piece of code that runs like an API endpoint, independently of your website or app. Despite the name, serverless functions do, indeed, run on a server. It’s just a server you don’t have to build or maintain.

The Team: Netlify and Buttondown

I use a service called Netlify to build and deploy my website. It’s key for this project because it publishes the static HTML, CSS, and JavaScript assets from my static site generator to a global CDN and runs the serverless function for my email subscription form.

I’m using Buttondown as my email list provider. Buttondown is a simple, privacy-first newsletter platform. It offers an API with extensive documentation, including an OpenAPI Specification file. Buttondown is free for up to 100 subscribers, after which it costs $9 a month for up to 1,000. The API is available for paying customers starting at the $9/mo plan.

As a side note, Buttondown offers extensive customization options for HTML form embeds, so you can accomplish almost everything in this tutorial with less work. But this is a good starting point to learn how to build serverless functions and as an introduction to creating advanced automation with the Buttondown API.

Here is a brief overview of the steps involved:

  1. Prepare your environment
  2. Create the newsletter subscription form
  3. Write the serverless function
  4. Deploy with Netlify

Ok, let’s get started!

1. Prepare your environment

To begin, you need to configure a few settings within Netlify before you can start building serverless functions.

Configure your Netlify.toml file

First, you need to tell Netlify where it should find and process your serverless functions. If you don’t have one already, create a file in your root directory called netlify.toml. The most basic version should include information about how to build and publish your website. Refer to the Netlify documentation for additional information.

In this example, publish tells Netlify to deploy the HTML and CSS files saved in the public folder for a simple, static website. This example also includes a command Netlify should run (npm install). Netlify will install the single node module dependency you will need later.

[build]
  publish = "public"
  command = "npm install"

Now you can add the path to the functions directory. The default functions directory is YOUR_BASE_DIRECTORY/netlify/functions. You will need to use the node-fetch v3 module later. Due to a change to node-fetch, you will need to enable ESM for the site by adding node_bundler = "esbuild" under the [functions] section of your netlify.toml file:

[functions]
  directory = "netlify/functions/"
  node_bundler = "esbuild"

For this tutorial, your completed netlify.toml file should look like this:

[build]
  publish = "public"
  command = "npm install"

[functions]
  directory = "netlify/functions/"
  node_bundler = "esbuild"

Save your API Key as an environment variable

Next, you must add your Buttondown API Key as an environment variable for your Netlify site. Environment variables are useful to hold information that we don’t want to make public, like API Keys.

Find and create your API Key in Buttondown under Settings > Power Tools > API. Copy this value and then add it as an environment variable using the Netlify Admin interface under Site Settings > Environment Variables.

For this tutorial, we’ll name the key BUTTONDOWN_API_KEY, add the value from Buttondown, and save it.

2) Create the newsletter subscription form

Now that Netlify is set up, I need a way to collect email submissions. The HTML for my email subscription form is very basic to start:

<form name="newsletter" method="POST" action="/success/" data-netlify="true">
  <label for="email">Your Email Address:</label>
  <input type="email" name="email" required />
  <button type="submit">Subscribe</button>
</form>

There are a few important things to note. First, I need to tell Netlify to process the form. All I need to do is add the data-netlify="true" attribute to the form tag.

Next, I must give the form a unique name using the name attribute. In this case, that’s simply name="newsletter". The form’s name attribute determines what shows up in the Netlify interface:

I wanted to redirect users to a confirmation page, so I added action="/success/" to display a page that contains the success message.

The next input in the form is named email. That’s where we collect the email addresses. You could name it anything, but make sure you note what you call it for the next step.

I’ve specified the input type as email and indicated that is required. This means that the browser will do all my validation for me, and won’t let users submit anything besides a valid email address.

Enable bot and spam protection

I’m going to take advantage of the built-in anti-spam and bot protection that comes with Netlify Forms, so I'll tweak the form a bit. I added the netlify-honeypot="bot-field" attribute and created a hidden label and input:

<label class="hidden">Don’t fill this out:</label>
<input class="hidden" name="bot-field" />

This should catch any bots and filter them out before they ever hit my subscriber list. Here is the completed HTML for the newsletter subscription form:

<form
  name="newsletter"
  method="POST"
  action="/success/"
  netlify-honeypot="bot-field"
  data-netlify="true"
>
  <label class="hidden">Don’t fill this out:</label>
  <input class="hidden" name="bot-field" />
  <label for="email">Your Email Address:</label>
  <input type="email" name="email" required />
  <button type="submit">Subscribe</button>
</form>

3) Write the serverless function

Now that I have a working email submission form, it’s time to create the serverless function. To start, create a file in the functions folder called submission-created.js. If you used the default functions directory, you should save your function at YOUR_BASE_DIRECTORY/netlify/functions.

I’m going to use the submission-created event trigger, which will tell Netlify to run my function every time the form is submitted. To accomplish that, I have to make sure the file is named submission-created.js.

Here’s the code for the serverless function:

const { BUTTONDOWN_API_KEY } = process.env;
import fetch from "node-fetch";

exports.handler = async (event, context) => {
  const email = JSON.parse(event.body).payload.email;
  console.log(`Received a submission: ${email}`);

  const response = await fetch("https://api.buttondown.com/v1/subscribers", {
    method: "POST",
    headers: {
      Authorization: `Token ${BUTTONDOWN_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ email }),
  });

  let responseText = await response.text();
  console.log("Response:", responseText);

  return {
    statusCode: 200,
    body: JSON.stringify({}),
  };
};

Let’s look at this line-by-line to understand how it works.

Configure prerequisites

First, I import the Buttondown API key I saved earlier as an environment variable for my function:

const { BUTTONDOWN_API_KEY } = process.env;

On line 2, I import a small library called node-fetch. This allows me to use Javascript’s Fetch API, which is how we’ll format an API POST request to send data to our email service:

import fetch from "node-fetch";

NOTE: When I was writing this post, many of the tutorials available used the require method to import the Fetch API, which resulted in errors when I tried to deploy the function. Make sure you use the method I described above. If you upgrade to node-fetch v3, you'll also need to update either your netlify.toml file or package.json to use ESM.

[functions]
  node_bundler = "esbuild"

You can find more information about how to make a fetch request using node-fetch v3 in an excellent guide by Tatyana Novell on the Netlify blog.

Define the serverless function

Line 4 is where I define the function. The exports.handler value is where Netlify expects to find the function. This is the basic syntax I’ll use to create the serverless function:

exports.handler = async function (event, context) {
  // your server-side functionality
};

Parse the form submission

The first thing I need to do is retrieve the email address from the form submission. I’m going to use JSON.parse. Since I’m using an event to trigger the function, we’ll use event.body to parse the submission and payload.email to retrieve the value of the email input field.

const email = JSON.parse(event.body).payload.email;

Then log the data in the console for debugging:

console.log(`Received a submission: ${email}`);

Create the API request

After retrieving the email address from the event value using JSON.parse, I’m ready to add the email to my list of subscribers using the Buttondown API. I’ll use the node-fetch library I imported earlier to create the POST request.

I need to authorize the API request by passing the BUTTONDOWN_API_KEY environment variable in the headers, and I’ll need to add the email address to the body of the request as a string:

const response = await fetch("https://api.buttondown.com/v1/subscribers", {
  method: "POST",
  headers: {
    Authorization: `Token ${BUTTONDOWN_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email }),
});

Log the response

Then capture and log the response from the email service. We do this to diagnose any issues that happened. Netlify makes it easy to check your function’s logs, so use console.log often!

let responseText = await response.text();
console.log("Response:", responseText);

And finally, we return the response from the function with an empty body:

return {
  statusCode: 200,
  body: JSON.stringify({}),
};

4) Deploy the function

Now that I’ve written my function, configured my netlify.toml file, and added my environment variables, everything is ready to go. Deploying is painless: just set up Netlify’s GitHub integration, push your changes, and your function will be deployed.

Conclusion

It took me less than 50 lines of code to build a custom newsletter subscription form using a serverless function. I wrote it all in HTML, CSS, and JavaScript, and everything is served from my domain. Plus, my website visitors get a nice experience whether they have JavaScript enabled or not, and I’m protected from bot and spam submissions by default.

If you’d like to experiment with creating your own custom newsletter form, you can clone and deploy the GitHub Repository below to Netlify. It comes with a minimal HTML landing page, a simple form, and everything you need to deploy the Netlify Function:

Good luck, and I hope you have fun building a custom newsletter subscription form for your website!

A version of this article originally appeared at andrewstiefel.com on August 28, 2022.