<?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: Avi Goldman</title>
    <description>The latest articles on DEV Community by Avi Goldman (@avigoldman).</description>
    <link>https://dev.to/avigoldman</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%2F19823%2F255b3d68-dcb2-4c06-b359-0a71bec52984.jpg</url>
      <title>DEV Community: Avi Goldman</title>
      <link>https://dev.to/avigoldman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/avigoldman"/>
    <language>en</language>
    <item>
      <title>JavaScript is hard: here are some of my go-to packages</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Mon, 06 Jun 2022 00:37:27 +0000</pubDate>
      <link>https://dev.to/avigoldman/javascript-is-hard-here-are-some-of-my-go-to-packages-56cg</link>
      <guid>https://dev.to/avigoldman/javascript-is-hard-here-are-some-of-my-go-to-packages-56cg</guid>
      <description>&lt;p&gt;I've been programming for a little while now. I'm not the best, I don't have much formal training, but my code runs and it does the job :)&lt;/p&gt;

&lt;p&gt;I started out programming in Java. Then dabbled in python. Then did PHP for a couple of years. And finally committed to the JavaScript game back in 2015.&lt;/p&gt;

&lt;p&gt;JavaScript is so versatile and so overloaded that it can be hard to find your way. And once you do get your bearings you can build pretty much anything. We've all read (or at least seen the titles) of articles bashing JavaScript or praising it. This isn't one of those. This is more for documenting which packages I always end up going back to, partly for myself since I forget, and partly for everyone else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isomorphic utilities&lt;/strong&gt; – fancy words for "it works everywhere"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Servers&lt;/strong&gt; – someone else's computer that usually does database work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Databases&lt;/strong&gt; - the thing that stores your data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt; – your user's computer that displays data and talks to servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scraping&lt;/strong&gt; – grabbing data from a website when they don't give you an API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email&lt;/strong&gt; – most people won't care about this but I spent a lot of my time in here&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Isomorphic utilities
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Generic utilties&lt;/td&gt;
&lt;td&gt;&lt;a href="https://lodash.com/"&gt;lodash&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/jdalton"&gt;John-David Dalton&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A utility library with a ton of useful functions for manipulating strings, objects, arrays, and basically anything else. I don't think I've ever made a project without it. Oh, and it's the most depended upon NPM package.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/debug"&gt;debug&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/debug-js"&gt;Debug.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A debugging library. Add logs. Filter them out. Colors for easy visual parsing. Another top 10 package.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data fetching&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/axios"&gt;axios&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/axios"&gt;Axios&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A super comprehensive library for making HTTP requests. Works in the browser and node.js. Another one that is in almost all of my projects.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data validation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/yup"&gt;yup&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/monasticpanic"&gt;Jason Quense&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;My favorite way to validate an object. It can be as simple or complex as you need. Super portable. Casts values. I use it to validate API requests and form values in formik.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ID generation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/uuid"&gt;uuid&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/uuidjs"&gt;UUID&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Generates universally unique identifiers (UUIDs). I love UUIDs for IDs of things that aren't user facing. Better than sequential IDs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ID generation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/nanoid"&gt;nanoid&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sitnikcode"&gt;Andrey Sitnik&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Generates unique identifiers. I like it for when UUIDs are too long. Think YouTube video IDs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ID generation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/slugify"&gt;slugify&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/_simov"&gt;Simeon Velichkov&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Takes a string and returns a "slugified" version. i.e. "Hello World" becomes "hello-world". Great for IDs that don't need to be globally unique or need to be memorable to the user.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dates&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/date-fns"&gt;date-fns&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/date-fns"&gt;date-fns&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;You can't go without this one. If you need to deal with dates, this is the tool for you. Formatting, math, comparing, sorting, filtering – it does it all.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/delay"&gt;delay&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Sleep for some time. Great for testing delays or for writing flaky code ;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-map"&gt;p-map&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Maps an array into promises.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-timeout"&gt;p-timeout&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Timeout a promise after some time.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-retry"&gt;p-retry&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Retries an async function on failure.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-cancelable"&gt;p-cancelable&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;For creating promises you might want to cancel before they complete.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-progress"&gt;p-progress&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;For creating promises you want to report progress as the work is done.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-any"&gt;p-any&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Resolve after any of the promises finish.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-some"&gt;p-some&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Resolve when a specific number of promises finish.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promises&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/p-all"&gt;p-all&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Resolve when all the promises finish.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URLs&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/is-absolute-url"&gt;is-absolute-url&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Checks if a string is an absolute URL.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;URLs&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/is-relative-url"&gt;is-relative-url&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Checks if a string is a relative URL.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Servers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;&lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://expressjs.com/"&gt;Express.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The industry standard for running a node server. If you don't have a strong opinion about a tool, use this.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Middleware&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/body-parser"&gt;body-parser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://expressjs.com/"&gt;Express.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A must have middleware for parsing the body.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Middleware&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/cookie-parser"&gt;cookie-parser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://expressjs.com/"&gt;Express.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A must have middleware for parsing cookies.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Middleware&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/helmet"&gt;helmet&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://expressjs.com/"&gt;Express.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A must have middleware for securing your server.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Middleware&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/cors"&gt;cors&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://expressjs.com/"&gt;Express.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A must have middleware for setting good cors headers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Middleware&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/express-ws"&gt;express-ws&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://expressjs.com/"&gt;Express.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A middleware for setting up web socket things. Not a must have ;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Databases
&lt;/h2&gt;

&lt;p&gt;I almost always reach for PostgreSQL. I've been using it for years, and it's been a great tool. If I need something real-time I use Firebase. If I need something for caching or queuing I use Redis.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database connector&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/pg-promise"&gt;pg-promise&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/vitaly-t"&gt;Vitaly Tomilov&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An promise library for PostgreSQL. Low level and lovely.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database connector&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/ioredis"&gt;ioredis&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/luinlee"&gt;Zihua Li&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A client for Redis. No real opinions. Which is a good sign in-itself.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ORM&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/prisma"&gt;prisma&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.prisma.io/"&gt;Prisma&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;An ORM with migrations and type-safety. My go-to for any new projects.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Queues&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/bullmq"&gt;bullmq&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://taskforce.sh"&gt;TaskForce&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;My favorite way to build queues.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Framework&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reactjs.org"&gt;React&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A JS library for building user interfaces.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metaframework&lt;/td&gt;
&lt;td&gt;&lt;a href="https://nextjs.org"&gt;Next.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A React Framework - I wouldn't use React without something like this.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;&lt;a href="https://styled-components.com/"&gt;styled-components&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/styled-components"&gt;styled-components&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A CSS library for React. Only for when I want 100% control.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Styling&lt;/td&gt;
&lt;td&gt;&lt;a href="https://tailwindcss.com/"&gt;tailwindcss&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/tailwindcss"&gt;Tailwind Labs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The utility-first CSS framework. When I don't care about 100% control, I use Tailwind.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data fetching&lt;/td&gt;
&lt;td&gt;&lt;a href="https://swr.vercel.app/"&gt;SWR&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://vercel.com/"&gt;Vercel&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The best way to fetch data in React (that I know of)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forms&lt;/td&gt;
&lt;td&gt;&lt;a href="https://formik.org/"&gt;Formik&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/jaredpalmer"&gt;Jared Palmer&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The best way to build forms in React (that I know of)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/clipboard-copy"&gt;clipboard-copy&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/feross"&gt;Feross Aboukhadijeh&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Copies something to the user's clipboard. That's it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/file-saver"&gt;file-saver&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sephr"&gt;Eli Grey&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Makes it super easy to save files to the user's computer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/react-outside-click-handler"&gt;react-outside-click-handler&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://airbnb.io/"&gt;AirBnb&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;"When the user clicks outside this element, do XYZ"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reach.tech/auto-id"&gt;@reach/auto-id&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reach.tech/"&gt;Reach - the accessible foundation for React&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Auto generate IDs. Useful for connecting labels and inputs without needing to manually set the id.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reach.tech/rect/"&gt;@reach/rect&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reach.tech/"&gt;Reach - the accessible foundation for React&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;React hook and higher order component to get the exact size of the DOM element.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/react-intersection-observer"&gt;react-intersection-observer&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/teh_builder"&gt;Daniel Schmidt&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Track when a DOM element enters the viewport. "When the user scrolls to this element, do XYZ".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/react-focus-lock"&gt;react-focus-lock&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/theKashey"&gt;Anton Korzunov&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;"Don't let the user leave this element"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Utilities&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/react-error-boundary"&gt;react-error-boundary&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/brian_d_vaughn"&gt;Brian Vaughn&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;"When something breaks, display XYZ"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reach.tech/"&gt;Reach UI&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://reach.tech/"&gt;Reach - the accessible foundation for React&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A ton of accessible React components I turn to all of the team&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dndkit.com/"&gt;dnd kit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/clauderic_d"&gt;Claudéric Demers&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Wanna make sortable lists, drag and drop builders, or anything else that the user can move? This is the tool for you.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/downshift-js/downshift"&gt;downshift&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/kentcdodds"&gt;Kent C. Dodds&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Accessible comboboxes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://fkhadra.github.io/react-toastify"&gt;react-toastify&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/fadi_khadra"&gt;Fadi Khadra&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Easy way to show toast notifications (Those notifications that pop up in the corner.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://fkhadra.github.io/react-contexify/"&gt;react-contexify&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/fadi_khadra"&gt;Fadi Khadra&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Easy scalable way to add context menus. (What you get when you right click.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://split.js.org/"&gt;Split.js&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/nathancahill"&gt;Nathan Cahill&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Super flexible utilities for resizable panes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://kbar.vercel.app/docs"&gt;kbar&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/timcchang"&gt;Tim Chang&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A customizable command palette.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/react-monaco-editor/react-monaco-editor"&gt;react-monaco-editor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/react-monaco-editor"&gt;react-monaco-editor&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A nice React wrapper around &lt;a href="https://microsoft.github.io/monaco-editor/"&gt;Monaco Editor&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Components&lt;/td&gt;
&lt;td&gt;&lt;a href="https://react-chartjs-2.js.org/"&gt;react-chartjs-2&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/reactchartjs"&gt;reactchartjs&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A nice React wrapper around chart.js&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Scraping
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Headless browser&lt;/td&gt;
&lt;td&gt;&lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/Microsoft"&gt;Microsoft&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A headless browser for testing and prototyping. Supports all major browsers and has great utils for waiting for the DOM to be ready.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extraction&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/metascraper"&gt;Metascraper&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://microlink.io/"&gt;microlink.io&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for extracting metadata from web pages.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extraction&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/html-get"&gt;html-get&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://microlink.io/"&gt;microlink.io&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for getting the HTML from any webpage, even if it uses JavaScript for rendering.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOM navigation / manipulation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/cheerio"&gt;cheerio&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/cheeriojs"&gt;Cheerio&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A jQuery-like library for navigating and manipulating the DOM. Couldn't live without it.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Email
&lt;/h2&gt;

&lt;p&gt;Ok so I work in email. I spent a lot of time thinking about email creation, validation, testing, etc. This section probably won't be interesting to most folks but I figured I'd add it anyways.&lt;/p&gt;

&lt;h3&gt;
  
  
  Email address validation
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Domains&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/disposable-email-domains"&gt;disposable-email-domains&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/ivolo"&gt;Ilya Volodarsky&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A list of domains associated with disposable email services. You'll never catch em all but this is pretty damn good.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domains&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/free-email-domains"&gt;free-email-domains&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/Kikobeats"&gt;Kiko Beats&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A list of domains associated with free email providers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Domains&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/email-providers"&gt;email-providers&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/derhuerst"&gt;Jannis R&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A list of domains associated with email providers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Localparts&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/role-based-email-addresses"&gt;role-based-email-addresses&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.mixmax.com/"&gt;Mixmax&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A list of localparts (the part before the &lt;code&gt;@&lt;/code&gt; sign) that refer to a role and not a person (i.e. "sales" or "admin")&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email address&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/email-misspelled"&gt;email-misspelled&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/sl-julienamblard"&gt;Julien Amblard&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library that will suggest an correction if the given email address probably has a typo.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email address&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/email-addresses"&gt;email-addresses&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/jackbearheart"&gt;Jack Bearheart&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for validating email addresses.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  String / HTML / CSS manipulation
&lt;/h3&gt;

&lt;p&gt;I a email creation platform and it requires a fair bit of DOM and string manipulation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Creator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Email parser&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/mailparser"&gt;mailparser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://nodemailer.com/about/"&gt;Node Mailer&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for parsing emails. I'm a fan but it's a bit heavy compared with letterparser and is node.js only.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email parser&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/letterparser"&gt;letterparser&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/matsz_dev"&gt;Mat Sz&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A full isomorphic library for parsing emails. Oh and it has AMP support which is a big plus. My go-to now-a-days.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS inliner&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/juice"&gt;juice&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://automattic.com/"&gt;Automattic&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The industry standard CSS inliner. It's hard to beat.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CSS navigation / manipulation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/postcss"&gt;postcss&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://postcss.org/"&gt;PostCSS&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for transforming CSS. Super powerful but a bit too dependent on perfect code for my liking.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML navigation / manipulation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/cheerio"&gt;Cheerio&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/cheeriojs"&gt;Cheerio&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A jQuery-like library for navigating and manipulating the DOM. Couldn't live without it.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML navigation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/simple-html-tokenizer"&gt;simple-html-tokenizer&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.tilde.io/"&gt;Tilde&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for tokenizing HTML. I use it for finding the source information for HTML attributes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML manipulation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/sanitize-html"&gt;sanitize-html&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://apostrophecms.com/"&gt;Apostrophe CMS&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for sanitizing HTML.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML manipulation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/escape-html"&gt;escape-html&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/component"&gt;Component&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for escaping HTML.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML manipulation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/html-to-text"&gt;html-to-text&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/html-to-text"&gt;html-to-text&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for converting HTML to text. Not perfect but a great base to work off of.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML information&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/is-self-closing"&gt;is-self-closing&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/jonschlinkert"&gt;Jon Schlinkert&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for checking if a tag is self-closing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML information&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/html-tags"&gt;html-tags&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://twitter.com/sindresorhus"&gt;Sindre Sorhus&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A list of all HTML tags.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML information&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/caniemail"&gt;caniemail&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://parcel.io"&gt;parcel.io&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;The data powering caniemail.com - the best source of support data for email.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String manipulation&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/ranges-apply"&gt;ranges-apply&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.codsen.com/"&gt;Codsen&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A library for manipulating strings. When you need to do non-parsing HTML/CSS manipulation there is literally no better way than this.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Open a new tab when your browser extension is installed</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Fri, 07 Feb 2020 13:56:07 +0000</pubDate>
      <link>https://dev.to/avigoldman/open-a-new-tab-when-your-browser-extension-is-installed-7h7</link>
      <guid>https://dev.to/avigoldman/open-a-new-tab-when-your-browser-extension-is-installed-7h7</guid>
      <description>&lt;p&gt;I recently published my first Browser extension to &lt;a href="https://chrome.google.com/webstore/detail/glitch-extension/pdiedadcnaailaknceakckfffhfjiagd"&gt;Chrome&lt;/a&gt; and &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/glitchextension/"&gt;Firefox&lt;/a&gt;, bringing &lt;a href="http://glitch.com/"&gt;Glitch&lt;/a&gt; superpowers to your browser. After the user installed the extension, I wanted to show the user a new tab with some information about myself as well as ask them if they wanted to receive updates about the extension and other projects I’m working on.&lt;/p&gt;

&lt;p&gt;I was inspired by some of my favorite Chrome extensions that do this, including &lt;a href="https://chrome.google.com/webstore/detail/wappalyzer/gppongmhjkpfnbhagpmjfkannfbllamg"&gt;Wappalyzer&lt;/a&gt;, &lt;a href="https://chrome.google.com/webstore/detail/colorzilla/bhlhnicpbhignbdhedgjhgdocnmhomnp"&gt;ColorZilla&lt;/a&gt;, and &lt;a href="https://chrome.google.com/webstore/detail/honey/bmnlcjabgnpnenekpadlanbbkooimhnj"&gt;Honey&lt;/a&gt;. Here’s a quick demo of Honey’s install page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2BypXnw7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aim1p1w59sqvi1dcttrg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2BypXnw7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/aim1p1w59sqvi1dcttrg.gif" alt="Honey install" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thankfully it was a fairly short amount of code to get this working! In case you're interested in doing something similar, here's how I did it. &lt;/p&gt;

&lt;h3&gt;
  
  
  manifest.json
&lt;/h3&gt;

&lt;p&gt;Normally this is the section where you need to update your &lt;a href="https://developer.chrome.com/extensions/manifest"&gt;manifest.json&lt;/a&gt; so you can implement the functionality. However, here we will use the &lt;code&gt;tabs&lt;/code&gt; API, most of which can be used without any extra permissions 🙌&lt;/p&gt;

&lt;p&gt;We do need to have a background script declared in our manifest.json. A background script listens for an event, like installs, messages from the &lt;a href="https://developer.chrome.com/extensions/content_scripts"&gt;content scripts&lt;/a&gt;, &lt;a href="https://developer.chrome.com/extensions/background_pages"&gt;and more&lt;/a&gt;.&lt;br&gt;
&lt;/p&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="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;"Glitch extension"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.0.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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Glitch superpowers for your browser 👾"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&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;"scripts"&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="s2"&gt;"background.js"&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;"persistent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;"browser_action"&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;"default_icon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"browser-icon.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"default_popup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"popup.html"&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;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&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;
  
  
  background.js
&lt;/h3&gt;

&lt;p&gt;We will listen to an event when the extension is installed. The event contains a &lt;code&gt;reason&lt;/code&gt; property which could be any of the following: "install", "update", "chrome_update", or "shared_module_update"&lt;br&gt;
Since we only want to open the new tab on install, we will make sure that the &lt;code&gt;reason&lt;/code&gt; equals “install”.&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="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onInstalled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;details&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;install&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;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://avigoldman.com/glitch&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;h3&gt;
  
  
  Try it out
&lt;/h3&gt;

&lt;p&gt;That’s it! Now every time your chrome extension is installed, a new tab will be opened. And here’s a video of it working 👾&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S8g0jCP_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q17mhy6wey5kiwldnurb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S8g0jCP_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/q17mhy6wey5kiwldnurb.gif" alt="Installing the glitch extension" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;br&gt;
Avi&lt;/p&gt;

</description>
      <category>ux</category>
      <category>glitch</category>
      <category>extension</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Give Glitch superpowers with this browser extension 🎏💖</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Tue, 14 Jan 2020 18:01:26 +0000</pubDate>
      <link>https://dev.to/avigoldman/give-glitch-superpowers-with-this-browser-extension-296m</link>
      <guid>https://dev.to/avigoldman/give-glitch-superpowers-with-this-browser-extension-296m</guid>
      <description>&lt;p&gt;I'm super excited to share my latest project: a browser extension to make it easier to remix glitch apps, import code from git, or start a fresh project.&lt;/p&gt;

&lt;p&gt;You can install it on &lt;a href="https://chrome.google.com/webstore/detail/glitch-extension/pdiedadcnaailaknceakckfffhfjiagd" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt; or &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/glitchextension/" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt; right now! &lt;/p&gt;

&lt;p&gt;With it, you can easily:&lt;br&gt;
👀 View glitch app details&lt;br&gt;
🎤 Remix glitch apps&lt;br&gt;
🍱 Generate embed code&lt;br&gt;
💻 Import code from GitHub and GitLab&lt;br&gt;
✨ Start new Glitch projects from any page on the web&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8pey0Kujwjg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  All about Glitch
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://glitch.com/" rel="noopener noreferrer"&gt;Glitch&lt;/a&gt; is an awesome tool where you can find cool projects, copy them and make them your own, and create things you can share with people immediately – all in your browser. You can read more about what Glitch is this “&lt;a href="https://medium.com/glitch/what-is-glitch-90cd75e40277" rel="noopener noreferrer"&gt;What is Glitch?&lt;/a&gt;” post.&lt;/p&gt;

&lt;p&gt;Seriously – there is some really &lt;a href="https://glitch.com/@glitch/glitch-team-faves" rel="noopener noreferrer"&gt;cool stuff&lt;/a&gt; to check out. &lt;a href="https://doodle-place.glitch.me/" rel="noopener noreferrer"&gt;Here is a project&lt;/a&gt; that is filled with animated doodles where you can create your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I made this
&lt;/h2&gt;

&lt;p&gt;If you can’t tell, I’m a bit of a super fan of Glitch. In fact, I can show you the exact date and time Glitch won my heart. 😅&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;I found that when I was in my own project it was super easy, but checking out random projects, moving between the source and the app, and pulling in code from GitHub was definitely harder.&lt;/p&gt;

&lt;p&gt;So I decided to scratch my own itch!&lt;/p&gt;

&lt;h2&gt;
  
  
  Give it a try
&lt;/h2&gt;

&lt;p&gt;If you are a Glitch fan like me, then give this extension a try! It’s available for &lt;a href="https://chrome.google.com/webstore/detail/glitch-extension/pdiedadcnaailaknceakckfffhfjiagd" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt; and &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/glitchextension/" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt;. And if it works for you, upvote it on &lt;a href="https://www.producthunt.com/posts/glitch-extension/" rel="noopener noreferrer"&gt;Product Hunt&lt;/a&gt;! 😻&lt;br&gt;
Interested in creating your own browser extension? All the code is available in this &lt;a href="https://glitch.com/edit/#!/extension" rel="noopener noreferrer"&gt;Glitch project&lt;/a&gt; 👾 for you to remix into something that works for you.&lt;/p&gt;

&lt;p&gt;Happy coding,&lt;br&gt;
Avi&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>glitch</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>How To Send Faxes Via Email Using SparkPost, Twilio &amp; Cloudinary</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Fri, 12 Jan 2018 14:08:50 +0000</pubDate>
      <link>https://dev.to/sparkpost/how-to-send-faxes-via-email-using-sparkpost-twilio--cloudinary-5e5i</link>
      <guid>https://dev.to/sparkpost/how-to-send-faxes-via-email-using-sparkpost-twilio--cloudinary-5e5i</guid>
      <description>&lt;h3&gt;
  
  
  No Fax Machine? No Problem! SparkPost, Twilio, and Cloudinary Are Here To Save The Day!
&lt;/h3&gt;

&lt;p&gt;I don’t know about you, but I don’t have a fax machine. Once in a blue moon, I have to send or receive a fax, so I was excited when Twilio shared an awesome way to receive faxes straight to your email using SparkPost. But that is only half the battle. I decided to build the ability to send faxes from an email so I would never need a fax machine again!&lt;/p&gt;

&lt;p&gt;We’ll build a function so we can send a PDF attachment to FAX_NUMBER@YOUR_DOMAIN, which will automatically fax the PDF to the phone number before the at sign (a.k.a. the local part, for all you &lt;a href="https://www.meetup.com/emailgeeksSF/" rel="noopener noreferrer"&gt;email geeks&lt;/a&gt; out there). To do this, we’ll use SparkPost’s inbound functionality, Twilio’s Fax API, and Cloudinary to glue them together. We’ll receive an email to a Twilio function, pull off the attached PDF, save it to Cloudinary, and send it as a fax.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sign Up and Configure Your SparkPost Account
&lt;/h3&gt;

&lt;p&gt;The first thing you’ll need is a &lt;a href="https://app.sparkpost.com/join" rel="noopener noreferrer"&gt;SparkPost account&lt;/a&gt; and a domain you want to use to receive mail (a.k.a. an &lt;a href="https://developers.sparkpost.com/api/inbound-domains.html" rel="noopener noreferrer"&gt;inbound domain&lt;/a&gt;). You’ll also need to create an API key with permissions to read and write&lt;code&gt;inbound domains&lt;/code&gt; and &lt;code&gt;relay webhooks&lt;/code&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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.27.28-PM.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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.27.28-PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, add the &lt;a href="https://www.sparkpost.com/docs/tech-resources/inbound-email-relay-webhook/#add-mx-records" rel="noopener noreferrer"&gt;SparkPost MX&lt;/a&gt; records for your inbound domain. Once you&lt;a href="https://mxtoolbox.com/" rel="noopener noreferrer"&gt; verify&lt;/a&gt; that they are set up correctly, run the following cURL to add your inbound domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://api.sparkpost.com/api/v1/inbound-domains &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&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;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "domain": "YOUR_DOMAIN" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Sign Up For Cloudinary
&lt;/h3&gt;

&lt;p&gt;Next, we’ll need a &lt;a href="https://cloudinary.com/users/register/free" rel="noopener noreferrer"&gt;Cloudinary account&lt;/a&gt; to store the PDF we’ll fax. If you don’t know, Cloudinary is a powerful solution for storing, manipulating, and delivering all your media. Grab your cloud name, API key, and API secret and put them somewhere safe for later.&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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.41.58-PM.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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.41.58-PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create &amp;amp; Configure Twilio
&lt;/h3&gt;

&lt;p&gt;We’ll use Twilio to consume the incoming emails and send the faxes. To get started, &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; for an account, and &lt;a href="https://www.twilio.com/login?g=%2Fconsole%2Fphone-numbers%2Fsearch%2Fbuy%2Fresults&amp;amp;t=3a4d26ea80f7766974e80da9f2835d8e4df28f63a511efd5ae0093c5cb47e0d9" rel="noopener noreferrer"&gt;buy a phone number&lt;/a&gt; that can send and receive faxes.&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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.43.40-PM.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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.43.40-PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Twilio’s serverless functions are perfect for this project. This isn’t a stateful application and it doesn’t need to be running all the time. Using one, we can quickly get set up and run our application.&lt;/p&gt;

&lt;p&gt;We’ll need the following NPM modules for our function:&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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.45.46-PM.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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.45.46-PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s also add in our environment variables for Cloudinary and enable Twilio’s option to pass through our &lt;code&gt;ACCOUNT_SID&lt;/code&gt; and &lt;code&gt;AUTH_TOKEN&lt;/code&gt; so we can use their client library without worrying about our credentials:&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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.48.59-PM.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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-3.48.59-PM.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code
&lt;/h3&gt;

&lt;p&gt;First things first, let’s write our basic Twilio function and pull in our dependencies:&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;const&lt;/span&gt; &lt;span class="nx"&gt;cloudinary&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;cloudinary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MailParser&lt;/span&gt; &lt;span class="p"&gt;}&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;mailparser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;toArray&lt;/span&gt; &lt;span class="p"&gt;}&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;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&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;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&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;twilio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTwilioClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;cloud_name&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;CLOUDINARY_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;api_key&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;CLOUDINARY_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;api_secret&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;CLOUDINARY_SECRET&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;The &lt;code&gt;messages&lt;/code&gt; variable is an object full of the messages SparkPost handed off to us. We’ll need to loop through the messages, pull off the attachment, save it to Cloudinary, and send off the fax.&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;// after the cloudinary config&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;toArray&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;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;message&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;const&lt;/span&gt; &lt;span class="nx"&gt;toNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;pickPhoneNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;pickPdfAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;attachment&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&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="nf"&gt;uploadAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attachment&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;mediaUrl&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="nf"&gt;sendFax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;twilio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mediaUrl&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&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="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parsing The Email
&lt;/h3&gt;

&lt;p&gt;You can see an example the &lt;a href="https://developers.sparkpost.com/api/relay-webhooks.html#header-example-payloads" rel="noopener noreferrer"&gt;payload SparkPost sends&lt;/a&gt; in the API documentation. The important parts for us are the &lt;code&gt;rcpt_to&lt;/code&gt;and the&lt;br&gt;
&lt;code&gt;content&lt;/code&gt; which will contain the raw email. These live inside &lt;code&gt;message.msys.relay_message&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can pull out the phone number by splitting off the local part from the &lt;code&gt;rcpt_to&lt;/code&gt; value – everything before the &lt;code&gt;@&lt;/code&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;function&lt;/span&gt; &lt;span class="nf"&gt;pickPhoneNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;msys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relay_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rcpt_to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&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;We’ll also need to pull the PDF off of the message. To do this, we’ll parse the RFC 822 value using the &lt;code&gt;mailparser&lt;/code&gt; library and return the content of the first PDF attached.&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;function&lt;/span&gt; &lt;span class="nf"&gt;pickPdfAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;msys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;relay_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isBase64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email_rfc822_is_base64&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;isBase64&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email_rfc822&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&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;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email_rfc822&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MailParser&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;streamAttachments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;attachment&lt;/span&gt;
    &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&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;data&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;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;attachment&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attachment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
          &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/pdf&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;attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attachment&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;h3&gt;
  
  
  Saving The Attachment
&lt;/h3&gt;

&lt;p&gt;Assuming we get an attachment, we’ll need to put it in an accessible spot for Twilio to pull it from. Enter Cloudinary. Using their Node library we can easily pipe the PDF and get the publicly accessible URL.&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;function&lt;/span&gt; &lt;span class="nf"&gt;uploadAttachment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reject&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;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload_stream&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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="nx"&gt;attachment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&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;h3&gt;
  
  
  Sending The Fax
&lt;/h3&gt;

&lt;p&gt;The last step for our code is to send the fax! Twilio makes this really easy. We need three values: the number we are sending to, the number we are sending from, and the media url. We’ll use the number we bought earlier to send for the “from” number. And we should have a phone number from the local part of the email and the media url from Cloudinary!&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;function&lt;/span&gt; &lt;span class="nf"&gt;sendFax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;twilio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mediaUrl&lt;/span&gt;&lt;span class="p"&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;twilio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;faxes&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;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;YOUR_PHONE_NUMBER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mediaUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mediaUrl&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mediaUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;to&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toNumber&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;h3&gt;
  
  
  Create A Relay Webhook
&lt;/h3&gt;

&lt;p&gt;The last piece of the puzzle it to tie our inbound domain from SparkPost to our Twilio function. Let’s create a relay webhook to pass all mail sent to our inbound domain onto our Twilio function. Copy your function path and pass it into this cURL request to create the relay webhook. Make sure that “Check for valid Twilio signature” is unchecked so that SparkPost can access it!&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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-4.13.11-PM.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%2Fmedia.sparkpost.com%2Fuploads%2F2018%2F01%2FScreen-Shot-2018-01-10-at-4.13.11-PM.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;curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://api.sparkpost.com/api/v1/relay-webhooks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: YOUR_API_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&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;-d&lt;/span&gt; &lt;span class="s1"&gt;'{ "target": "YOUR_TWILIO_PATH", "match": { "domain": "YOUR_INBOUND_DOMAIN", "protocol": "SMTP" }, "name": "Email-to-Fax" }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Send The Fax!! 🎉
&lt;/h3&gt;

&lt;p&gt;Send an email over to FAX_NUMBER@YOUR_DOMAIN with your fax attached as a PDF and it should go through!&lt;/p&gt;

&lt;p&gt;You can set up receiving faxes for a fully working email fax machine with some &lt;a href="https://www.twilio.com/blog/2017/12/fax-to-email-twilio-functions-sparkpost.html" rel="noopener noreferrer"&gt;guidance&lt;/a&gt; from our friend &lt;a href="https://twitter.com/kolencherry" rel="noopener noreferrer"&gt;Patrick&lt;/a&gt; at &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt;! Feel free to &lt;a href="https://twitter.com/theavigoldman" rel="noopener noreferrer"&gt;reach&lt;/a&gt; out if you’ve got any questions and have fun faxing!&lt;/p&gt;

&lt;p&gt;-Avi&lt;/p&gt;

&lt;p&gt;P.S. Before you launch this to the world, you’ll probably want to validate the phone numbers you’re sending to and add some security to who can send. I’d suggest a token in the email that you verify in the Twilio function.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The post &lt;a href="https://www.sparkpost.com/blog/send-faxes-via-email/" rel="noopener noreferrer"&gt;How To Send Faxes Via Email Using SparkPost, Twilio &amp;amp; Cloudinary&lt;/a&gt; appeared first on &lt;a href="https://www.sparkpost.com" rel="noopener noreferrer"&gt;SparkPost&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>twilio</category>
      <category>cloundinary</category>
      <category>serverless</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Announcing HEML: An Open Source Framework for Email</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Fri, 27 Oct 2017 13:05:21 +0000</pubDate>
      <link>https://dev.to/sparkpost/announcing-heml-an-open-source-framework-for-email-cek</link>
      <guid>https://dev.to/sparkpost/announcing-heml-an-open-source-framework-for-email-cek</guid>
      <description>&lt;p&gt;Today I am excited to announce the first release of &lt;a href="https://heml.io/" rel="noopener noreferrer"&gt;HEML,&lt;/a&gt; an open source markup language for crafting clean, responsive emails.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why
&lt;/h3&gt;

&lt;p&gt;If you’re not familiar with writing HTML emails, &lt;a href="https://www.sparkpost.com/blog/topol-html-email-templates/" rel="noopener noreferrer"&gt;it can be a painful process.&lt;/a&gt; A few months ago I started to play with some exciting interactive developments on the front end. I found the bottleneck wasn’t sending or testing the email, but simply building the email itself. At our next company hackathon, I took the opportunity to solve this problem, and thus HEML was born.&lt;/p&gt;

&lt;p&gt;Each element in HEML renders into email-ready HTML so that you can send without worry. HEML also works to iron out CSS bugs and limitations of different email clients. An excellent example of one such bug is an obscure issue in Lotus Notes where if you use RGB decimal value, the entire style declaration will be ignored. HEML will handle that bug for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Our Goals
&lt;/h3&gt;

&lt;p&gt;We wanted HEML to help make email more accessible to developers. The idea is for developers to jump in quickly and build their emails without wrestling with Outlook (or any other email inbox). To do this, we focused on three things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Native feel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We wanted HEML to look and feel like HTML. As a result, it mirrors HTML as closely as possible and uses plain ol’ CSS for styling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forward Thinking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;HEML doesn’t limit you from taking advantage of all that HTML and CSS can do. It encourages progressive enhancements. &lt;a href="https://emails.hteumeuleu.com/do-emails-need-to-look-exactly-the-same-in-every-client-5c0ec5ca541d" rel="noopener noreferrer"&gt;Email doesn’t have to look the same everywhere.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extendable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;HEML lets you create your custom elements, share them, and pull in other elements made by the community.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using HEML
&lt;/h3&gt;

&lt;p&gt;There are a couple of different ways to use HEML.&lt;/p&gt;

&lt;p&gt;Get started quickly using our editor at &lt;a href="https://heml.io/editor" rel="noopener noreferrer"&gt;heml.io/editor&lt;/a&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%2Fmedia.sparkpost.com%2Fuploads%2F2017%2F10%2Fezgif-5-df93edf292.gif" 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%2Fmedia.sparkpost.com%2Fuploads%2F2017%2F10%2Fezgif-5-df93edf292.gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use it locally, install it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g heml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create your HEML email in &lt;code&gt;email.heml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;heml&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;subject&amp;gt;&lt;/span&gt;Email Rox!&lt;span class="nt"&gt;&amp;lt;/subject&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SkyBlue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DarkViolet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;container&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;marquee&amp;gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world &lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&amp;lt;/marquee&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/container&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/heml&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;heml develop email.heml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That starts a development server that will auto-reload your browser whenever you make a change.&lt;/p&gt;

&lt;p&gt;Once you’re ready to send your email into the wild, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;heml build email.heml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This generates an &lt;code&gt;email.html&lt;/code&gt; file that is ready to be sent.&lt;/p&gt;

&lt;p&gt;Give it a spin!&lt;/p&gt;

&lt;p&gt;This is our take on a difficult problem. It doesn’t solve every problem presented by email, but it can help you create solutions for your unique email challenges. There has been amazing work done to simplify this challenge by &lt;a href="https://mjml.io/" rel="noopener noreferrer"&gt;MJML&lt;/a&gt;,&lt;a href="https://foundation.zurb.com/emails.html" rel="noopener noreferrer"&gt;Foundation for Email&lt;/a&gt;, and many others. We hope you find this equally as helpful!&lt;/p&gt;

&lt;p&gt;So give it a try! Hopefully, it makes your life easier. If you have any feedback, suggestions, or bugs, &lt;a href="https://github.com/SparkPost/heml/issues" rel="noopener noreferrer"&gt;let us know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Coding!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The post &lt;a href="https://www.sparkpost.com/blog/heml-open-source/" rel="noopener noreferrer"&gt;Announcing HEML: An Open Source Framework for Email&lt;/a&gt; appeared first on &lt;a href="https://www.sparkpost.com" rel="noopener noreferrer"&gt;SparkPost&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>heml</category>
      <category>opensource</category>
      <category>email</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Meet Our New West Coast Developer Advocate</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Fri, 18 Aug 2017 13:05:54 +0000</pubDate>
      <link>https://dev.to/sparkpost/meet-our-new-west-coast-developer-advocate</link>
      <guid>https://dev.to/sparkpost/meet-our-new-west-coast-developer-advocate</guid>
      <description>&lt;p&gt;Hello everyone! ðŸ‘‹ Avi here. I’ve got exciting news about my role at SparkPost!&lt;/p&gt;

&lt;h3&gt;
  
  
  TL;DR:
&lt;/h3&gt;

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

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



&lt;/p&gt;

&lt;p&gt;Last summer, I joined SparkPost as an &lt;a href="https://www.sparkpost.com/blog/engineering-interns-intro-2016/" rel="noopener noreferrer"&gt;intern&lt;/a&gt; on the Growth Engineering team. I was lucky enough drop the &lt;em&gt;“intern”&lt;/em&gt; part of my title in September of last year, and I’ve gotten to work on some pretty &lt;a href="https://www.sparkpost.com/blog/author/agoldman/" rel="noopener noreferrer"&gt;interesting stuff&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, one year later (drumroll please) I’m psyched to say that I’m joining the Developer Relations team as the west coast Developer Advocate in San Francisco. ðŸŽ‰ I’ve gotten to work with &lt;a href="https://twitter.com/mary_grace" rel="noopener noreferrer"&gt;Mary&lt;/a&gt; and &lt;a href="https://twitter.com/aydrianh" rel="noopener noreferrer"&gt;Aydrian&lt;/a&gt; during my time here and I’m thrilled to join them full time.&lt;/p&gt;

&lt;p&gt;I visited SF for the first time after &lt;a href="http://angelhack.com/angelhack-global-hackathon-series-silicon-valley/" rel="noopener noreferrer"&gt;AngelHack Silicon Valley&lt;/a&gt; a couple of weeks ago and I’m already a little in love with the city.&lt;/p&gt;

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

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



&lt;/p&gt;

&lt;p&gt;I’ve got a ton of ideas that I hope to share about email and making it a little more fun. I’ve got some tooling for front-end work that I’m playing with because let’s face it – HTML emails are hard. (If you want to help, let me know ðŸ’Œ)&lt;/p&gt;

&lt;p&gt;After that, who knows! I want to work on email testing and validation tools, integrations with some awesome projects, and some fun demos and guides. Right now, I’m toying with the idea of checkers built with interactive email and SparkPost.&lt;/p&gt;

&lt;p&gt;I’m going to be traveling to more events now as well (though I’ve managed to go a good number as an engineer) so if you see me, say hi! I absolutely love going to hackathons, so if you have any suggestions there let me know!&lt;/p&gt;

&lt;p&gt;In any case, I’m new to the Developer Advocacy side of things, but I’m super excited for this adventure. If you have any tips or tricks for living in SF or traveling, tweet em at me ðŸ˜¬&lt;/p&gt;

&lt;p&gt;If you want to talk, you can find me on &lt;a href="https://twitter.com/theavigoldman" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://github.com/avigoldman" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, or &lt;a href="//mailto:avi@sparkpost.com"&gt;email&lt;/a&gt; â¤, and I’m always up for a cup of coffee.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://www.sparkpost.com/blog/west-coast-developer-advocate/" rel="noopener noreferrer"&gt;sparkpost.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>developeradvocate</category>
      <category>devrel</category>
      <category>career</category>
    </item>
    <item>
      <title>PHP Frameworks In The Wild: A Closer Look at the PHP Ecosystem</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Fri, 04 Aug 2017 13:05:35 +0000</pubDate>
      <link>https://dev.to/sparkpost/php-frameworks-in-the-wild-a-closer-look-at-the-php-ecosystem</link>
      <guid>https://dev.to/sparkpost/php-frameworks-in-the-wild-a-closer-look-at-the-php-ecosystem</guid>
      <description>&lt;h3&gt;
  
  
  SparkPost and PHP Frameworks: Scratching the Surface
&lt;/h3&gt;

&lt;p&gt;PHP is one of the older languages used on the Web, created in the mid-nineties. Some environments are still stuck running PHP 3 while others are running the latest version. There’s been a lot of development around amazing PHP frameworks and tools in the last few years, and most of them come with easy ways to send mail. We recently broke down how we &lt;a href="https://www.sparkpost.com/blog/php-client-library-2-0/"&gt;simplified&lt;/a&gt; our PHP library, but going with a native solution can still be easier sometimes. Here’s a quick breakdown of how to use SparkPost to send mail with some of the more popular PHP frameworks in the wild.&lt;/p&gt;

&lt;h3&gt;
  
  
  Plain ol’ PHP
&lt;/h3&gt;

&lt;p&gt;Sometimes you just need to fire off a quick API call. All you need is a little cURL magic to do what you need. Check out &lt;a href="https://github.com/SparkPost/php-sparkpost/issues/164#issuecomment-289888237"&gt;this snippet&lt;/a&gt; for shooting off SparkPost requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt; 

&lt;span class="cd"&gt;/**
 * Use this method to access the SparkPost API
 */&lt;/span&gt;
&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sparkpost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nv"&gt;$headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$defaultHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: application/json'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="nv"&gt;$curl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;strtoupper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$finalHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;array_merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$defaultHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://api.sparkpost.com:443/api/v1/'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$uri&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CURLOPT_CUSTOMREQUEST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CURLOPT_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CURLOPT_RETURNTRANSFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CURLOPT_POSTFIELDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$payload&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nb"&gt;curl_setopt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CURLOPT_HTTPHEADER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$finalHeaders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;curl_exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nb"&gt;curl_close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$curl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sending email...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$email_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sparkpost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'transmissions'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'from'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SparkPost Team'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'php@yourdomain.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'subject'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'First Mailing From PHP+cURL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'text'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Congratulations!! You just sent an email with PHP+cURL!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'recipients'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'address'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'john.doe@example.com'&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="s1"&gt;'Authorization: YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  ðŸ˜ + ðŸŽ¶ = â¤ï¸
&lt;/h3&gt;

&lt;p&gt;Here at SparkPost we created a &lt;a href="https://packagist.org/packages/sparkpost/sparkpost"&gt;composer package&lt;/a&gt; to simplify using our API in all modern PHP environments. It’s quick to install and provides some “sugar to smooth out some of the more confusing parts of the API. To keep our library as flexible as possible, we use &lt;a href="http://docs.php-http.org/en/latest/"&gt;PHP HTTP&lt;/a&gt; to let you bring your own request library. To use it we can require &lt;code&gt;guzzlehttp/guzzle&lt;/code&gt;, the &lt;code&gt;php-http/guzzle6-adapter&lt;/code&gt;, and the &lt;code&gt;sparkpost/sparkpost&lt;/code&gt; library. Then we are good to go with the code below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;SparkPost\SparkPost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;GuzzleHttp\Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Http\Adapter\Guzzle6\Client&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;GuzzleAdapter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$httpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;GuzzleAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nv"&gt;$sparky&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;SparkPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$httpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'async'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sparky&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;transmissions&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'from'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SparkPost Team &amp;lt;phplibrary@yourdomain.com&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'subject'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'First Mailing From PHP Library'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'text'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Congratulations!! You just sent an email with the PHP library!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'recipients'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt; &lt;span class="s1"&gt;'address'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'John Doe &amp;lt;john.doe@example.com&amp;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;h3&gt;
  
  
  World of WordPress
&lt;/h3&gt;

&lt;p&gt;Our very own &lt;a href="https://thehungrycoder.com/"&gt;Raju&lt;/a&gt; wrote a &lt;a href="https://github.com/SparkPost/wordpress-sparkpost/"&gt;SparkPost plugin&lt;/a&gt; to use SparkPost as your email provider. It exposes key features like using templates, sending via SMTP or the API, and allows you to &lt;a href="https://github.com/SparkPost/wordpress-sparkpost/blob/master/docs/hooks.md"&gt;hook&lt;/a&gt; in at a ton of points to modify the email and settings on the fly. Plus, it’s easy to &lt;a href="https://www.sparkpost.com/blog/sparkpost-wordpress-plugin/"&gt;get started&lt;/a&gt; ðŸ˜Ž&lt;/p&gt;

&lt;h3&gt;
  
  
  PHPMailer
&lt;/h3&gt;

&lt;p&gt;PHPMailer is probably the most popular way to send mail in PHP! It’s got a great API that makes it easy to plug any SMTP provider. Check out this &lt;a href="https://github.com/SparkPost/code-snippets/blob/master/snippets/SMTP/smtp_example.php"&gt;simple example&lt;/a&gt; to get started with SparkPost quickly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// This example uses the PHPMailer library:&lt;/span&gt;
&lt;span class="c1"&gt;// https://github.com/PHPMailer/PHPMailer&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$mail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;PHPMailer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// configure for sparkpost&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;isSMTP&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'smtp.sparkpostmail.com'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;587&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;SMTPSecure&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'tls'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;SMTPAuth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'SMTP_Injection'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;setFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'phpmailer@yourdomain.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;addAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'john.doe@example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'First Mailing From PHPMailer'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;Body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Congratulations!! You just sent an email from PHPMailer!'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Message could not be sent&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Mailer Error: "&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$mail&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;ErrorInfo&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Message has been sent&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;h3&gt;
  
  
  Laravel
&lt;/h3&gt;

&lt;p&gt;Laravel is a whole beast unto itself so I’ll just hit the main points. You can follow the official &lt;a href="https://laravel.com/docs/5.4/mail#driver-prerequisites"&gt;Laravel docs&lt;/a&gt; to get set up with the SparkPost mail driver. Once you’ve done that you can &lt;a href="https://laravel.com/docs/5.4/mail#generating-mailables"&gt;generate&lt;/a&gt; and &lt;a href="https://laravel.com/docs/5.4/mail#sending-mail"&gt;send&lt;/a&gt; your mailables. &lt;a href="https://mattstauffer.co/blog/using-sparkpost-for-transactional-emails-with-laravel"&gt;Matt Stauffer&lt;/a&gt; did a solid write up on using SparkPost with Laravel which is worth checking out.&lt;/p&gt;

&lt;h3&gt;
  
  
  CodeIgniter
&lt;/h3&gt;

&lt;p&gt;CodeIgniter provides a simple API to send mail. You can add the SMTP configuration in code or through &lt;a href="https://www.codeigniter.com/user_guide/libraries/email.html#setting-email-preferences-in-a-config-file"&gt;config/email.php&lt;/a&gt;. Check out the example below to fire off a quick email.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="c1"&gt;// inside a controller&lt;/span&gt;

&lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'protocol'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'smtp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'smtp_host'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'smtp.sparkpostmail.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'smtp_user'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SMTP_Injection'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'smtp_pass'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'smtp_crypto'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'tls'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'smtp_port'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'587'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'newline'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;library&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'codeigniter@yourdomain.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Your Name'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'john.doe@example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Testing SparkPost SMTP from CodeIgniter'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Congratulations!! You just sent an email from CodeIgniter!'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Be sure to comment below or &lt;a href="http://twitter.com/sparkpost"&gt;tweet us&lt;/a&gt; if you want us to go into more depth on any particular tool or if we missed your favorite framework ðŸ‘‹&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://www.sparkpost.com/blog/php-frameworks/"&gt;sparkpost.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>phpframeworks</category>
      <category>sdk</category>
    </item>
    <item>
      <title>How And Why We Simplified The SparkPost PHP Client Library</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Mon, 31 Jul 2017 21:16:22 +0000</pubDate>
      <link>https://dev.to/sparkpost/how-and-why-we-simplified-the-sparkpost-php-client-library</link>
      <guid>https://dev.to/sparkpost/how-and-why-we-simplified-the-sparkpost-php-client-library</guid>
      <description>&lt;p&gt;You might remember that last year we released the &lt;a href="https://github.com/SparkPost/php-sparkpost/releases/tag/2.0.0"&gt;2.0 version&lt;/a&gt; of the SparkPost PHP client library. We’ve talked about the evolution of our approach &lt;a href="https://www.sparkpost.com/blog/client-libraries/"&gt;across all our client libraries&lt;/a&gt;, but I wanted to dive deeper into the PHP changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;After 18 months of having our client libraries in the wild we’ve learned some important lessons. The biggest one was that abstraction is hard when you’re wrapping a living, growing API. With 16 API endpoints and &lt;a href="https://developers.sparkpost.com/api/labs-introduction.html?_ga=2.195912812.1986239921.1501527668-424867781.1485888065"&gt;more coming&lt;/a&gt; we were limiting our users and ourselves as our client libraries fell behind our API additions. We changed our mentality to focus on providing a thin layer of abstraction with some syntactic “sugar” to simplify the more complex areas of our API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the PHP client library
&lt;/h2&gt;

&lt;p&gt;In the new version we decided to let a single method drive all SparkPost requests. This lets you hit any API endpoint quickly and with a ton of flexibility.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// in 1.x&lt;/span&gt;
&lt;span class="nv"&gt;$sparky&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setupUnwrapped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'metrics'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sparky&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'deliverability'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'from'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'2016-03-07T17:00'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'to'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'2016-03-08T17:30'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'metrics'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'count_targeted,count_injected,count_rejected'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'timezone'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'America/New_York'&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;


&lt;span class="c1"&gt;// in 2.x&lt;/span&gt;
&lt;span class="nv"&gt;$promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sparky&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'metrics/deliverability'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'from'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'2016-03-07T17:00'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'to'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'2016-03-08T17:30'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'metrics'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'count_targeted,count_injected,count_rejected'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'timezone'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'America/New_York'&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the 1.x version we did a good amount of property mapping. For example, to enable inline css you would set &lt;code&gt;inlineCss =&amp;gt; true&lt;/code&gt; when you called &lt;code&gt;$sparky-&amp;gt;transmission-&amp;gt;send&lt;/code&gt;. Now in the 2.x release, we simply pass along the data you provide, so to inline css you would set &lt;code&gt;'options' =&amp;gt; [ 'inline_css' =&amp;gt; true ]&lt;/code&gt;. While this might look more complex at first it actually makes it easier to use since you can now depend on our main &lt;a href="https://developers.sparkpost.com/api/transmissions.html?_ga=2.35456993.1986239921.1501527668-424867781.1485888065#transmissions-retrieve-and-delete-get"&gt;API docs&lt;/a&gt; and quickly translate JSON into an associative array.&lt;/p&gt;

&lt;p&gt;We also moved off of the deprecated &lt;code&gt;egeloen/ivory-http-adapter&lt;/code&gt; to use &lt;code&gt;php-http/httplug&lt;/code&gt;. This means you can continue to bring your own HTTP request library and adds the new ability to use &lt;a href="http://docs.php-http.org/en/latest/components/promise.html"&gt;promises&lt;/a&gt; in PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Use It
&lt;/h2&gt;

&lt;p&gt;We have a bunch of examples on how to use our new 2.x library in the &lt;a href="https://github.com/SparkPost/php-sparkpost/tree/master/examples"&gt;github repo&lt;/a&gt;. Here’s a quick walk through on how to get going.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing &amp;amp; setup
&lt;/h3&gt;

&lt;p&gt;First we need to pull the library from composer. If you don’t have that setup, check out their &lt;a href="https://getcomposer.org/doc/00-intro.md"&gt;getting started page&lt;/a&gt;. (If you’re using a PHP version earlier than 5.6 or you can’t use composer you can still use our API with some &lt;a href="https://github.com/SparkPost/php-sparkpost/issues/164#issuecomment-289888237"&gt;simple cURL magic&lt;/a&gt;!)&lt;/p&gt;

&lt;p&gt;We are going to use &lt;a href="http://docs.guzzlephp.org/en/stable/"&gt;guzzle&lt;/a&gt; as our request library. You can use any of the &lt;a href="https://packagist.org/providers/php-http/client-implementation"&gt;httplug-supported clients&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;composer require guzzlehttp/guzzle
composer require php-http/guzzle6-adapter
composer require sparkpost/sparkpost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have our dependencies we can set up the library. To run our code synchronously we’ll set &lt;code&gt;async&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;SparkPost\SparkPost&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;GuzzleHttp\Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Http\Adapter\Guzzle6\Client&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;GuzzleAdapter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$httpClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GuzzleAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nv"&gt;$sparky&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SparkPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$httpClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'key'&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="s1"&gt;'YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'async'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building the transmission
&lt;/h3&gt;

&lt;p&gt;Looking at the following transmission, we can see it’s almost identical to a regular SparkPost transmission. The difference is that we added a &lt;code&gt;cc&lt;/code&gt; and &lt;code&gt;bcc&lt;/code&gt;. This is that “sugar” I mentioned. The library will go through and format your &lt;code&gt;cc&lt;/code&gt; and &lt;code&gt;bcc&lt;/code&gt; recipients to fit the SparkPost API’s requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="nv"&gt;$transmissionData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'content'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'from'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SparkPost Team'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'from@sparkpostbox.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'subject'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Mailing via PHP library 2.x'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'text'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Woo! We did the thing!'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'recipients'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'address'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_EMAIL'&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="s1"&gt;'cc'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'address'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ANOTHER_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ANOTHER_EMAIL'&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="s1"&gt;'bcc'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'address'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'AND_ANOTHER_NAME'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'AND_ANOTHER_EMAIL'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$sparky&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;transmissions&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$transmissionData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStatusCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;print_r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBody&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&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;And there you go! Your email is off!&lt;/p&gt;

&lt;p&gt;To dig into the library, checkout the repo on &lt;a href="https://github.com/SparkPost/php-sparkpost"&gt;GitHub&lt;/a&gt;, or come talk to our team in &lt;a href="http://slack.sparkpost.com/?_ga=2.103678784.1986239921.1501527668-424867781.1485888065"&gt;Slack&lt;/a&gt;. And if you find a bug (ðŸ˜±) submit an &lt;a href="https://github.com/SparkPost/php-sparkpost/issues"&gt;issue&lt;/a&gt; or a &lt;a href="https://github.com/SparkPost/php-sparkpost/pulls"&gt;PR&lt;/a&gt;. Check back soon for an upcoming post on using SparkPost with some popular PHP frameworks.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was originally published on &lt;a href="https://www.sparkpost.com/blog/php-client-library-2-0/"&gt;sparkpost.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>php</category>
      <category>sdk</category>
      <category>api</category>
      <category>refactoring</category>
    </item>
    <item>
      <title>Using Slack Emojis Almost Everywhere</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Fri, 30 Jun 2017 13:35:17 +0000</pubDate>
      <link>https://dev.to/sparkpost/using-slack-emojis-almost-everywhere</link>
      <guid>https://dev.to/sparkpost/using-slack-emojis-almost-everywhere</guid>
      <description>&lt;p&gt;Anyone in the modern workplace is probably familiar with tools like &lt;a href="https://slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt;, a team-oriented chat platform for getting things done. One of my favorite features is the emoji shortcut, which allows you to simply type &lt;code&gt;:emoji-name:&lt;/code&gt; to insert the corresponding emoji. It makes it really easy to &lt;a href="https://thenextweb.com/insider/2015/06/23/the-psychology-of-emojis/#.tnw_A0H29uOg" rel="noopener noreferrer"&gt;add emotion&lt;/a&gt; without hunting for the particular emoji you’re thinking of. After living in Slack for hours every day I found myself trying this shortcut in other apps, which disappointingly didn’t work. I’ve kept an ear to the ground for a solution to this very first-world problem… and finally found one!&lt;/p&gt;

&lt;h2&gt;
  
  
  But how? ðŸ¤”
&lt;/h2&gt;

&lt;p&gt;At our last company holiday party, &lt;a href="https://twitter.com/aydrianh" rel="noopener noreferrer"&gt;Aydrian&lt;/a&gt; mentioned that he had recently added an auto-correct match to his iPhone which replaced &lt;code&gt;shrug&lt;/code&gt; with &lt;code&gt;Â¯\_(ãƒ„)_/Â¯&lt;/code&gt;. I loved the idea, so when I got home, I added all my favorite Slack emoji shortcuts to my Mac auto-correct.&lt;/p&gt;

&lt;p&gt;To do this on a Mac, go to &lt;code&gt;System Preferences &amp;gt; Keyboard &amp;gt; Text&lt;/code&gt; and then add emoji shortcuts. For an iPhone its the same process, found in &lt;code&gt;Settings &amp;gt; General &amp;gt; Keyboard &amp;gt; Text Replacement&lt;/code&gt;. It’s that easy! Here is a table of my most used emojis.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;shortcut&lt;/th&gt;
&lt;th&gt;emoji&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;:_1:&lt;/td&gt;
&lt;td&gt;ðŸ‘Ž&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👍&lt;/td&gt;
&lt;td&gt;ðŸ‘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;😊&lt;/td&gt;
&lt;td&gt;ðŸ˜Š&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:cry_:_&lt;/td&gt;
&lt;td&gt;ðŸ˜¢&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:eyeroll:&lt;/td&gt;
&lt;td&gt;ðŸ™„&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:grimace:&lt;/td&gt;
&lt;td&gt;ðŸ˜¬&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;😯&lt;/td&gt;
&lt;td&gt;ðŸ˜¯&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;😂&lt;/td&gt;
&lt;td&gt;ðŸ˜‚&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💋&lt;/td&gt;
&lt;td&gt;ðŸ˜˜&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;😷&lt;/td&gt;
&lt;td&gt;ðŸ˜·&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:money:&lt;/td&gt;
&lt;td&gt;ðŸ¤‘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🐒&lt;/td&gt;
&lt;td&gt;ðŸ™ˆ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:nerd:&lt;/td&gt;
&lt;td&gt;ðŸ¤“&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:party:&lt;/td&gt;
&lt;td&gt;ðŸŽ‰&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:peace:&lt;/td&gt;
&lt;td&gt;âœŒï¸&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:plane_down:&lt;/td&gt;
&lt;td&gt;ðŸ›¬&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:plane_up:&lt;/td&gt;
&lt;td&gt;ðŸ›«&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:plane:&lt;/td&gt;
&lt;td&gt;âœˆï¸&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;💩&lt;/td&gt;
&lt;td&gt;ðŸ’©&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🤷&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Â¯\_(ãƒ„)_/Â¯&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:sick:&lt;/td&gt;
&lt;td&gt;ðŸ˜·&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;:sleep:&lt;/td&gt;
&lt;td&gt;ðŸ˜´&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🤔&lt;/td&gt;
&lt;td&gt;ðŸ¤”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;👋&lt;/td&gt;
&lt;td&gt;ðŸ‘‹&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  But does it work â‰ï¸
&lt;/h2&gt;

&lt;p&gt;I’m pretty happy with how it works. Check it out in action. ðŸŽ‰&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%2Fmedia.sparkpost.com%2Fuploads%2F2017%2F06%2Femoji.gif" 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%2Fmedia.sparkpost.com%2Fuploads%2F2017%2F06%2Femoji.gif" alt="slack emojis gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully this trick will help you maintain your emoji skills everywhere. Sometimes you’ll want to want to use an emoji you haven’t added a shortcut for though ðŸ˜Ÿ You can keep your emoji-ing in high gear with the keyboard shortcut &lt;code&gt;ctrl + cmd + space&lt;/code&gt; ðŸŽ‰&lt;/p&gt;

&lt;h2&gt;
  
  
  More love for emojis â¤ï¸
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Emojis in your code ðŸ–¥
&lt;/h3&gt;

&lt;p&gt;While it might not be the best idea, it is possible to use emoji in your code and comments. More practically, if you’re like me and prefer writing in your code editor, being able to add emojis easily is a nice feature (I’m actually using it to write this blogpost – gettin’ meta). There are a ton of plugins that help with that. Here are a few that I’ve heard good things about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atom: &lt;a href="https://github.com/atom/autocomplete-emojis" rel="noopener noreferrer"&gt;autocomplete+emojis suggestions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sublime Text 3: &lt;a href="https://github.com/akatopo/GithubEmoji" rel="noopener noreferrer"&gt;GithubEmoji&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;JetBrains: &lt;a href="https://plugins.jetbrains.com/plugin/9174-emoji-support-plugin" rel="noopener noreferrer"&gt;Emoji Support Plugin&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Emojis as your code ðŸ˜¯
&lt;/h3&gt;

&lt;p&gt;On a slightly more whimsical front, there is an open source project named &lt;a href="http://www.emojicode.org/" rel="noopener noreferrer"&gt;Emojicode&lt;/a&gt; which is an entirely emoji-based language. You should definitely &lt;a href="http://www.emojicode.org/docs/guides/install.html" rel="noopener noreferrer"&gt;give it a try&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Be sure to comment or &lt;a href="http://twitter.com/sparkpost" rel="noopener noreferrer"&gt;tweet&lt;/a&gt; us your favorite hacks for slack emojis. I’m always on the lookout for new tricks!&lt;/p&gt;

&lt;p&gt;p.s. At SparkPost we love all things Slack – check out &lt;a href="https://twitter.com/colestrode" rel="noopener noreferrer"&gt;Cole’s&lt;/a&gt; posts if you’re interested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.sparkpost.com/blog/slack-bots-productivity/" rel="noopener noreferrer"&gt;Our Obsession with Slack Bots and Productivity&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sparkpost.com/blog/using-slack-bots-sparkpost/" rel="noopener noreferrer"&gt;How We Use Bots in Slack at SparkPost&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The post &lt;a href="https://www.sparkpost.com/blog/slack-emojis/" rel="noopener noreferrer"&gt;Using Slack Emojis Almost Everywhere&lt;/a&gt; appeared first on &lt;a href="https://www.sparkpost.com" rel="noopener noreferrer"&gt;SparkPost&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>emoji</category>
      <category>lifehack</category>
    </item>
    <item>
      <title>Updated Support Docs: A Better Way To Find The Resources You Need</title>
      <dc:creator>Avi Goldman</dc:creator>
      <pubDate>Wed, 14 Jun 2017 16:40:52 +0000</pubDate>
      <link>https://dev.to/sparkpost/updated-support-docs-a-better-way-to-find-the-resources-you-need</link>
      <guid>https://dev.to/sparkpost/updated-support-docs-a-better-way-to-find-the-resources-you-need</guid>
      <description>&lt;h2&gt;
  
  
  Supporting Support
&lt;/h2&gt;

&lt;p&gt;Maybe you’re new to SparkPost and you want a guide to &lt;a href="https://www.sparkpost.com/docs/getting-started/getting-started-sparkpost/"&gt;get started&lt;/a&gt;. Or maybe you’re a long time pro and you want to test your &lt;a href="https://www.sparkpost.com/docs/faq/using-sink-server/"&gt;production list with a sink server&lt;/a&gt;. For these things and everything in between you’ll find the answer in our &lt;a href="https://www.sparkpost.com/docs/"&gt;support docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Sounds pretty standard for most companies, right? Unfortunately we’d gotten a lot of feedback from our users that our support website was difficult to use. From their perspective, the search didn’t always return the best results, the navigation could be confusing at times, the content styling was inconsistent, and the design hid information cues about the the page below the fold. We were using Desk.com behind the scenes to build out the site, which was a fine solution, but didn’t allow people outside of our support team, including our fine community members, to improve our documentation.&lt;/p&gt;

&lt;p&gt;Our new setup allows our users to not only find what they need quickly, but allows them to submit changes or even add new articles if they’re so inclined.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed?
&lt;/h2&gt;

&lt;p&gt;The tl;dr is we moved the support docs into GitHub, open sourced it, shoved the articles into Algolia for awesome search, and wrote a pipeline right into our sparkpost.com WordPress website. Whew! We also redesigned the landing page to help call out resources for getting started, migrating from other providers, and getting help. Let’s roll up our sleeves and and dig into some of the changes that we made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Into the weeds
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prison break: hacker style
&lt;/h3&gt;

&lt;p&gt;The first step was to to get our articles out of Desk.com. They have an API, so with a little work we wrote a &lt;a href="https://github.com/SparkPost/support-docs/tree/master/export"&gt;script&lt;/a&gt; to export the articles from desk and convert them from HTML to Markdown. This was relatively easy, albeit tedious -- since the Markdown converter had some trouble with the messy HTML there was a bit of manual cleanup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OAJpJwfd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media2.giphy.com/media/3KQ4VNwCrOThC/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OAJpJwfd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://media2.giphy.com/media/3KQ4VNwCrOThC/giphy.gif" alt="oh no"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each category of articles was stored in a folder, which had an index.md file to store any metadata. Within each category folder was a media folder for any related images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;articles/
├── category_name/
│   ├── media/
│   │    └──my_article/
│   │       └──some_picture.png
│   ├── index.md
│   ├── my_article.md
│   └── another_article.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  WordPressing
&lt;/h3&gt;

&lt;p&gt;Next up was pushing the articles into WordPress. To start, we first needed to set up a custom post type and taxonomy in WordPress for the support articles and categories. We used a &lt;a href="https://github.com/jasonrhodes/WP-CustomObjects"&gt;library&lt;/a&gt; written by our very own &lt;a href="https://twitter.com/rhodesjason"&gt;Jason Rhodes&lt;/a&gt; to simplify the management of  our custom post types. With a couple of custom templates, we were good to go on this front.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="c1"&gt;// Support Article CPT&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'support_article'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'singular_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Support Article'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'plural_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Support Articles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'description'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Support Articles'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'supports'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'editor'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'author'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'excerpt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'custom-fields'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'revisions'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="s1"&gt;'public'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'has_archive'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'show_ui'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'taxonomies'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'support_category'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="s1"&gt;'rewrite'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'slug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'docs/%support_category%'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'with_front'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&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="c1"&gt;// Support Category CT&lt;/span&gt;
&lt;span class="nf"&gt;register_taxonomy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'support_category'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'support_article'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'labels'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$support_category_labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'rewrite'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'slug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'docs'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'with_front'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'hierarchical'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;true&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;With regard to search, we decided to use &lt;a href="https://www.algolia.com/"&gt;Algolia&lt;/a&gt; which has worked really well for us with our &lt;a href="https://developers.sparkpost.com/api/?_ga=2.165630079.1914068973.1496187918-424867781.1485888065"&gt;API documentation&lt;/a&gt;. They built a really great WordPress plugin that handles indexing, multiple environments, and the search that you see live. Needless to say, we’re big fans.&lt;/p&gt;

&lt;h3&gt;
  
  
  Laying the pipeline
&lt;/h3&gt;

&lt;p&gt;This final piece was the most complicated. We needed to push any changes to the categories, articles, and media in a way that worked across our local, staging, and production environments.&lt;/p&gt;

&lt;p&gt;To build out this we used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bash + &lt;a href="http://wp-cli.org/"&gt;WP CLI&lt;/a&gt;, a super useful tool for interacting with WordPress from the command line.&lt;/li&gt;
&lt;li&gt;Some JS for converting the Markdown to HTML.&lt;/li&gt;
&lt;li&gt;Travis CI for automated build and deployment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We wrote a wrapper around the WP CLI to handle the environment variables that we stored in Travis. During each deploy we first &lt;a href="https://github.com/SparkPost/support-docs/blob/master/bin/deploy-categories.sh"&gt;updated the categories&lt;/a&gt;, followed by &lt;a href="https://github.com/SparkPost/support-docs/blob/master/bin/deploy-articles.sh"&gt;each article&lt;/a&gt;. The categories are fairly straight forward: find the changed index.md files, and update the corresponding categories in WordPress.&lt;/p&gt;

&lt;p&gt;Handling the articles is where it gets interesting. There are a bunch of states to account for. You could replace and move an image in an article, move the article to a different category, or even delete the entire article and rerun the deploy. We took the safest route of rebuilding the entire world around the article on every update, including the metadata, images, and actual content.&lt;/p&gt;

&lt;p&gt;Don’t let this fool you -- while it sounds relatively straightforward, each of the above steps could be a whole blog post on its own. Maybe I’ll dig into that someday, but for now, feel free to dig around the &lt;a href="https://github.com/SparkPost/support-docs/tree/master/bin"&gt;deployment scripts&lt;/a&gt; and reach out if you have any questions. And if you find a typo in our docs or want to write an article about using SparkPost with your favorite email client/tool/language, simply submit a PR on the &lt;a href="https://github.com/SparkPost/support-docs"&gt;docs repo&lt;/a&gt; and we’ll take a look!&lt;/p&gt;

&lt;p&gt;This post was originally posted on the &lt;a href="https://www.sparkpost.com/blog/updated-support-docs/"&gt;SparkPost blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>support</category>
      <category>github</category>
      <category>documentation</category>
    </item>
  </channel>
</rss>
