<?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>Forem: annielcook</title>
    <description>The latest articles on Forem by annielcook (@annielcook).</description>
    <link>https://forem.com/annielcook</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%2F57766%2F6d4c22d5-8b62-4518-83c4-97f031ff96a4.jpeg</url>
      <title>Forem: annielcook</title>
      <link>https://forem.com/annielcook</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/annielcook"/>
    <language>en</language>
    <item>
      <title>Build a Scheduler Slackbot in 30 Minutes!</title>
      <dc:creator>annielcook</dc:creator>
      <pubDate>Thu, 16 Aug 2018 21:03:02 +0000</pubDate>
      <link>https://forem.com/nylas/build-a-scheduler-slackbotin-30-minutes-1i6c</link>
      <guid>https://forem.com/nylas/build-a-scheduler-slackbotin-30-minutes-1i6c</guid>
      <description>&lt;p&gt;The days of leaving Slack to create an event on your calendar are over! &lt;/p&gt;

&lt;p&gt;In this tutorial, you are going to learn how to create a scheduler bot that adds events to your personal calendar with a simple Slack slash command using the Nylas Calendar API. &lt;/p&gt;

&lt;h3&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2FSlack%2520Bot2%2520%281%29.png" alt="Slack Bot2 (1)"&gt;&lt;/h3&gt;

&lt;h2&gt;&lt;strong&gt;What You’ll Need Beforehand&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;There are a few systems that you’ll need to have in place before embarking on this DIY Slack bot journey!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://help.github.com/articles/set-up-git/" rel="noopener noreferrer"&gt;Git&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/" rel="noopener noreferrer"&gt;Github&lt;/a&gt; account&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://signup.heroku.com/" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; account&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;a href="https://slack.com/" rel="noopener noreferrer"&gt;Slack&lt;/a&gt; workspace&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;span&gt;An&lt;/span&gt;&lt;span&gt; email address associated with a Google or Microsoft&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;(Office&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;65&lt;/span&gt;&lt;span&gt; or Outlook) calendar&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;A text editor of your choosing — PyCharm, Atom, Vim, Sublime, etc.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;&lt;strong&gt;Overview&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Here are the high-level steps involved in building the scheduling bot:&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Part 0: Basic app setup &lt;span&gt;&lt;br&gt;TLDR; Fork the starter code, run the setup script and run the app locally&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Part 1: Hook your repo up to Heroku &amp;amp; deploy it &lt;span&gt;&lt;br&gt;TLDR; Create a Heroku app, hook it up to your Github repo, deploy it&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Part 2: Set up Heroku config variables&lt;br&gt;TLDR; Auth an account with Nylas to get the access token, use this to get the ID of the calendar, add these plus the calendar timezone as config variables in Heroku.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Part 3: Connect your app to your Slack workspace&lt;span&gt;&lt;br&gt;TLDR; Create a Slack App and configure a slash command to communicate with your app, add a route in your app that listens for the slash command &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Part 4: Add text parsing functionality    &lt;span&gt;&lt;br&gt;TLDR; Parse the text sent from Slack into meaningful components for an event&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Part 5: Build scheduling functionality  &lt;span&gt;&lt;br&gt;TLDR; Send the parsed arguments to the /events endpoint of the Nylas API to create an event on your calendar.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h3&gt;Ready? Let’s dive in!&lt;/h3&gt;

&lt;h2 id="part-0"&gt;&lt;strong&gt;Part 0: Basic app setup&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 0: Fork the Slackbot starter code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The code for this project lives in a repository on the Nylas Github account at &lt;a href="https://github.com/nylas/slackbot" rel="noopener noreferrer"&gt;https://github.com/nylas/slackbot&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;You are going to fork and then clone this repository instead of just cloning it so that you have your own repository that you can connect to Heroku. Follow &lt;a href="https://help.github.com/articles/fork-a-repo/" rel="noopener noreferrer"&gt;these steps&lt;/a&gt; from Github to fork the repository. Follow the steps up until “Find another repository to fork”.&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531330588485_image.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531330588485_image.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After, you will have a local copy of a directory called &lt;code&gt;slackbot&lt;/code&gt; . This directory has five files:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;span&gt;Procfile - tells heroku how to run the app&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;app.py - the main app file with initial Flask app setup, you will do all of your development in this file&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;requirements.txt - the list of dependencies you need to install&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;runtime.txt - tells Heroku that your app is written in Python3&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;setup.sh - a script to setup your development environment&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the rest of the tutorial, you will be executing all of your commands in the &lt;code&gt;/slackbot&lt;/code&gt; directory&lt;/p&gt;

&lt;p&gt;In addition to the master branch, which you will be on by default, this repo also contains five other branches corresponding to each part of the tutorial. You can stay on the master branch as you build your Slack bot, but if you get stuck at any step then you can see the difference between your code and the example application code by using &lt;code&gt;git diff &amp;lt;part-#&amp;gt;.&lt;/code&gt; So if you get stuck during Part 4, &lt;code&gt;git diff part-4&lt;/code&gt; will show the difference between your code and the example code after part 4. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Run the Script &lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The setup script sets up your development environment and installs necessary dependencies. Run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source setup.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After this script completes it will print "Set up complete!" in your terminal&lt;/p&gt;

&lt;p&gt;This script will have activated a virtual environment which you will use to keep the development of your project isolated. If your terminal prompt is now prepended with &lt;code&gt;(.venv)&lt;/code&gt; your virtual environment has successfully been activated!&lt;/p&gt;

&lt;p&gt;If you are interested in learning more about virtual environments and python3 check out &lt;a href="https://help.dreamhost.com/hc/en-us/articles/115000695551-Installing-and-using-virtualenv-with-Python-3" rel="noopener noreferrer"&gt;https://help.dreamhost.com/hc/en-us/articles/115000695551-Installing-and-using-virtualenv-with-Python-3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Run the app locally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;export FLASK_ENV=development&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;python3 app.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531180157293_Screen%2BShot%2B2018-07-09%2Bat%2B4.46.21%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531180157293_Screen%2BShot%2B2018-07-09%2Bat%2B4.46.21%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;If you visit the URL on the 4th line that the app is running on, you will see &lt;code&gt;Howdy Hacker!&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Wohoo! You made it through the app set up. Pat yourself on the back and crack open a La Croix. &lt;/p&gt;

&lt;h2 id="part-1"&gt;&lt;strong&gt;Part 1: Hook your repo up to Heroku&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 0: Create a new Heroku app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You could use DigitalOcean or DIY if you’d prefer but these instructions will be for Heroku.&lt;/p&gt;

&lt;p&gt;Log in to heroku: &lt;a href="https://id.heroku.com/login" rel="noopener noreferrer"&gt;https://id.heroku.com/login&lt;/a&gt;, and create a new application by clicking &lt;code&gt;new&lt;/code&gt; in the top right corner and completing the new app form&lt;/p&gt;

&lt;p&gt;&lt;span&gt;You can enter your own app name or leave it blank and let Heroku generate one for you! Make note of this url as you will need it in Part 3 when you connect your application to Slack.  &lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Hook up your Github repo to Heroku&lt;span&gt; &lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Heroku has an integration with Github which enables automatic deploys. This means that whenever you push code to a branch on Github, Heroku will deploy this code. How handy is that!&lt;/p&gt;

&lt;p&gt;&lt;br&gt;On the deploy page that you get redirected to after creating an application, in the &lt;strong&gt;Deployment method &lt;/strong&gt;section, click &lt;code&gt;Connect to GitHub&lt;/code&gt; and then in the &lt;strong&gt;Connect to Github&lt;/strong&gt; section, click the &lt;code&gt;Connect to GitHub&lt;/code&gt; button.&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531183138050_Screen%2BShot%2B2018-07-09%2Bat%2B5.25.56%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531183138050_Screen%2BShot%2B2018-07-09%2Bat%2B5.25.56%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;span&gt; &lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Next, you will be taken to a page where you can select which repository to connect. If you have successfully created a repository under your github account, then it should come up when you start to type the repository name into the input. &lt;/p&gt;

&lt;p&gt;Once you have connected up the repository, click &lt;code&gt;Enable Automatic Deploys&lt;/code&gt; for the master branch. This will simplify the deploy process so that any time you push to master, heroku will deploy the latest changes. &lt;/p&gt;

&lt;p&gt;It’s important to note that if you start working on a branch other than master, then you will not have automatic deploys. You can manually deploy code from another branch if you push your code to the other branch select that branch from the manual deploy section and click &lt;code&gt;Deploy Branch&lt;/code&gt;.&lt;/p&gt;

&lt;p&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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-1.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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-1.png" alt="image_preview-1"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Keep Heroku open because you will need it in Part 4 - Step 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Push code to master to trigger a deploy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Quit your locally running app with &lt;code&gt;CTRL+C&lt;/code&gt;. &lt;span&gt;Lets make a change to commit and add another exclamation point to your return statement.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The route should now look like&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@app.route('/')&lt;br&gt;
def homepage():&lt;br&gt;
   return "Howdy hacker!!"&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In your terminal, run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git add .&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git commit -m 'Part 1: Hook up repo to Heroku'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git push origin master&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;After this deploy is complete — it may take a few seconds — visit the url of your app. You should be greeted with the message &lt;code&gt;Howdy Hacker!!&lt;/code&gt;. Your website is live and ready to welcome any hacker who visits the url, all thanks to the automatic deploy you configured. &lt;span&gt; &lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;&lt;strong&gt;Part 2: Set up Heroku config variables&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;Okay, so to make this work you need 2 major things. First, you need an access token that indicates that you have authorized your account to be accessed through the Nylas API. This is an important step because the API cannot add an event to any old calendar unless it has been authenticated and the owner has given permission. Second, you need the ID of the calendar you want to add events to. Most accounts have a few calendars — some autogenerated and some created by the user. Without this, the API would not know which calendar to send the request for.&lt;/p&gt;

&lt;p&gt;Next, you’ll set these as config variables in Heroku so that you’ll avoid adding and committing them to code in your app. &lt;/p&gt;

&lt;p&gt;In Step 0, you will get an access token and in Step 1, you will use this access token to find the ID for the calendar that you wish to add events to. Then, in Step 3 you will add these as config variables to your Heroku app. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 0: Get an access token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Head over to &lt;a href="https://dashboard.nylas.com/register" rel="noopener noreferrer"&gt;https://dashboard.nylas.com/register&lt;/a&gt; to register for a free Nylas account or to &lt;a href="https://dashboard.nylas.com/sign-in" rel="noopener noreferrer"&gt;https://dashboard.nylas.com/sign-in&lt;/a&gt; to sign in to an existing account. Once you’ve signed into the Nylas dashboard, click Accounts in the left side bar.&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531260178164_Screen%2BShot%2B2018-07-10%2Bat%2B2.56.06%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531260178164_Screen%2BShot%2B2018-07-10%2Bat%2B2.56.06%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;On the Accounts page, click &lt;code&gt;Auth Account&lt;/code&gt; in either the top right or if you have no previously authed accounts, in the center of the page.&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531260180284_Screen%2BShot%2B2018-07-10%2Bat%2B2.55.47%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531260180284_Screen%2BShot%2B2018-07-10%2Bat%2B2.55.47%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;In the input window that pops up, type the email address associated with the calendar that you want to add events to and follow the authentication steps. &lt;/p&gt;

&lt;p&gt;When you complete authentication, you will be redirected back to the Accounts page of the dashboard. At the top of the page will be a blue notification that looks like:&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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-2.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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-2.png" alt="image_preview-2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;From the blue notification, copy the value of the access token to a temporary place — you will need this token in the following two steps. In this example, the access token is &lt;code&gt;xyz123xyz123xyz123xyz123xyz123.&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;If you dismiss the notification or reload the page, the access token will disappear. For security reasons, there is no way to retrieve that specific token so if you lose your access token and need to get a new one, click &lt;code&gt;Auth Account&lt;/code&gt; and follow the authorization steps. You can re-auth a previously authenticated account no problem! This will create a new access token for the account. &lt;/p&gt;

&lt;p&gt;You will use this token first in the Nylas docs to get the ID of the calendar and second when you add it to Heroku config variables.&lt;/p&gt;

&lt;p&gt;You can keep the dashboard page open and as long as you don’t navigate away or reload it, the blue notification with the access token will stay at the top of the page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Get the calendar ID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can’t create an event without telling it what calendar it needs to be added to so you need to get the ID of the calendar you want to add events to.&lt;/p&gt;

&lt;p&gt;Head over to &lt;a href="https://docs.nylas.com/reference#calendars-1" rel="noopener noreferrer"&gt;https://docs.nylas.com/reference#calendars-1&lt;/a&gt;. This is the calendar section of the Nylas docs and specifically the &lt;code&gt;GET /calendars endpoint&lt;/code&gt;. You could hit the Nylas API by making a request to &lt;code&gt;https://api.nylas.com/calendars&lt;/code&gt; in your terminal, but the docs are interactive and make it possible to do in two steps within the site. &lt;/p&gt;

&lt;p&gt;Paste your access token into the &lt;code&gt;Authorization&lt;/code&gt; input field at the bottom of the inputs for this endpoint section. &lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531262232159_Screen%2BShot%2B2018-07-10%2Bat%2B3.32.36%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531262232159_Screen%2BShot%2B2018-07-10%2Bat%2B3.32.36%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;If you then scroll back up to the code in the dark box above, the &lt;code&gt;authorization:&lt;/code&gt;  will have been automatically filled out.&lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531262370948_Screen%2BShot%2B2018-07-10%2Bat%2B3.35.36%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531262370948_Screen%2BShot%2B2018-07-10%2Bat%2B3.35.36%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Next, click &lt;code&gt;Try It&lt;/code&gt; in the top right corner. This will run the &lt;code&gt;cURL&lt;/code&gt; request on the left and hit the Nylas API with your access token and display the response on the right. This will be a list of your calendars. &lt;/p&gt;

&lt;p&gt;From the JSON list of calendars, select which calendar you want to add events to. Some of the calendars you may have never seen before. That is because some providers have default calendars that they automatically generate. One common example is “Holidays”. Often, these automatic calendars will have &lt;code&gt;"read_only": True&lt;/code&gt;. This means that you don’t have permissions to create events on those calendars. It is important to select a calendar that has &lt;code&gt;"read_only": False.&lt;/code&gt; Copy the &lt;code&gt;id&lt;/code&gt; from your selected calendar. &lt;/p&gt;

&lt;p&gt;In the example screenshot above, the &lt;code&gt;id&lt;/code&gt; of the top calendar is &lt;code&gt;"67qmz3fuk9wfljwj1w8ngshkc".&lt;/code&gt; You will have a different value after you run Try It because the ID will be a unique identifier to your calendar.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Add config variables to Heroku&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to Heroku and navigate to the settings page for your app. Click &lt;code&gt;Reveal Config Vars&lt;/code&gt;. This will pop open a pair of inputs where the key, or how you want to refer to the variable in your code, is on the left and the value is on the right. &lt;/p&gt;

&lt;p&gt;Add your calendar ID as a config variable with a key of &lt;code&gt;CALENDAR_ID&lt;/code&gt; and a value of the ID that you copied in Step 1. Make sure to remove the double quotes from the ID. &lt;/p&gt;

&lt;p&gt;Next, you are going to add the access token from Step 0 as a config variable with a key of &lt;code&gt;ACCESS_TOKEN&lt;/code&gt; and the value from Step 0. &lt;/p&gt;

&lt;p&gt;Finally, you need to add a config variable with your calendar timezone so that your calendar can properly schedule events. If you don’t pass the timezone, then your calendar will assume UTC (Universal Time Coordinated) also known as GMT (Greenwich Mean Time). If your calendar is in any other timezone, then the evens will show up shifted by the timezone UTC offset. Setting the timezone config variable will avoid this! &lt;/p&gt;

&lt;p&gt;Since I am writing this in California and it is currently daylight savings, the value will be &lt;code&gt;PDT&lt;/code&gt;. You can also set the value to the UTC offset, which for me would be &lt;code&gt;-0700&lt;/code&gt;. You can find your timezone information &lt;a href="https://en.wikipedia.org/wiki/List_of_time_zone_abbreviations" rel="noopener noreferrer"&gt;here&lt;/a&gt;. If you want to use the UTC offset then it needs to be in the proper format. A timezone of UTC+10 would be &lt;code&gt;+1000&lt;/code&gt; and a timezone of UTC-3 would be &lt;code&gt;-0300&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is how it looked for me:&lt;/p&gt;

&lt;p&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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-3.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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-3.png" alt="image_preview-3"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Or with UTC offset:&lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531264746932_Screen%2BShot%2B2018-07-10%2Bat%2B4.12.02%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531264746932_Screen%2BShot%2B2018-07-10%2Bat%2B4.12.02%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Sweet now that everything in Heroku is configured, we can dive into the exciting next steps. &lt;/p&gt;

&lt;h2 id="part-3"&gt;&lt;strong&gt;Part 3: Connect your app to Slack&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a Slack app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://api.slack.com/apps?new_app=1" rel="noopener noreferrer"&gt;https://api.slack.com/apps?new_app=1&lt;/a&gt;. This is the landing page for building a new Slack app.&lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_23A4CD528EBB9FE5912B9C3F2BF4F0B1B5BDBBA2881E8512A634DDE0D3EF3B57_1530226678465_Screen%2BShot%2B2018-06-28%2Bat%2B3.56.58%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_23A4CD528EBB9FE5912B9C3F2BF4F0B1B5BDBBA2881E8512A634DDE0D3EF3B57_1530226678465_Screen%2BShot%2B2018-06-28%2Bat%2B3.56.58%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;If the pop up above does not appear, click &lt;code&gt;Create New App&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Fill in the &lt;code&gt;App Name&lt;/code&gt; with “Scheduler” or whatever you would like to name this app. The name you select here is not too important.&lt;/p&gt;

&lt;p&gt;Next select the development workspace in which you want to use this app. If you are not signed into your Slack account, when you click the &lt;code&gt;Development Slack Workspace&lt;/code&gt; drop down the only option that will appear is &lt;code&gt;Sign in to another workspace&lt;/code&gt;. Select this and follow these instructions. This will get you to sign into your workspace on desktop. Once this is successful, head back to &lt;a href="https://api.slack.com/apps?new_app=1" rel="noopener noreferrer"&gt;https://api.slack.com/apps?new_app=1&lt;/a&gt; and fill out the pop up again. This time, there should be workspace options in the dropdown. Select which workspace you want to add this scheduler app to and hit &lt;code&gt;Create App&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Configure Slack app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You will be taken to an app dashboard. Here, there are lots of options for what type of application and functionality you want to build. You will be making a slash command so select the &lt;code&gt;Slash Commands&lt;/code&gt; option. &lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_23A4CD528EBB9FE5912B9C3F2BF4F0B1B5BDBBA2881E8512A634DDE0D3EF3B57_1530227635007_Screen%2BShot%2B2018-06-28%2Bat%2B4.06.17%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_23A4CD528EBB9FE5912B9C3F2BF4F0B1B5BDBBA2881E8512A634DDE0D3EF3B57_1530227635007_Screen%2BShot%2B2018-06-28%2Bat%2B4.06.17%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Then select &lt;code&gt;Create New Command&lt;/code&gt;. This will take you to a slash command configuration form. &lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531184636689_Screen%2BShot%2B2018-07-09%2Bat%2B5.58.05%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531184636689_Screen%2BShot%2B2018-07-09%2Bat%2B5.58.05%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Command&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can name the command however you want to invoke it when you use it. I choose &lt;code&gt;/scheduleme&lt;/code&gt; because I think it looks cute when invoked in full: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;/scheduleme "Dinner with James" "Tomorrow at 7pm" "Tomorrow at 9pm"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;but you can select whatever slash command tickles your fancies. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request URL&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;Request URL&lt;/code&gt; input, copy the url that your heroku app was deployed to on step 1 and paste it in the input. Then at the end of the url, add a &lt;code&gt;/scheduleme&lt;/code&gt; so the url will look something like &lt;code&gt;https://my-scheduler-bot.herokuapp.com/scheduleme&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is setting up where Slack should send the request from the slash command. In otherwords, when you invoke a slash command, Slack needs to know where to send the data. This is specifying a url and a specific route on that url, &lt;code&gt;/scheduleme&lt;/code&gt;.  You will define this route in your python file in Part 5! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short description&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add a short description to help you remember what this command does. This will pop up in Slack when you are typing commands that match. &lt;/p&gt;

&lt;p&gt;For mine, I put &lt;code&gt;Adds an event to my calendar!&lt;/code&gt;&lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531185146535_Screen%2BShot%2B2018-07-09%2Bat%2B6.12.13%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531185146535_Screen%2BShot%2B2018-07-09%2Bat%2B6.12.13%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage Hint&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is a hint for how to properly invoke the slash command. As you can see in the screen shot above, this will also pop up when you start typing the command. &lt;/p&gt;

&lt;p&gt;For mine, I put &lt;code&gt;“title” “start date &amp;amp; time” “end date &amp;amp; time”&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The scheduler will be expecting three arguments, each in double quotes.  The first argument should be the title of the event, the second should be the start date and time and the third should be the end date and time. You will see how these get parsed in when you build out the text parsing functionality in Part 4.&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Save&lt;/code&gt; in the bottom right corner and you will be redirect back to your app homepage. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Install the app in your workspace&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On the sidebar, click &lt;code&gt;Install App&lt;/code&gt;&lt;/p&gt;

&lt;p&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531859978889_image.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531859978889_image.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Follow the steps to install the app in your workspace. Without this step, your workspace won’t know about your app and its slash command. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Add the /scheduleme route to your application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the previous step, you connected your Slack slash command up to the url of your heroku app + &lt;code&gt;/scheduleme&lt;/code&gt;. But what is &lt;code&gt;/scheduleme&lt;/code&gt; referring to? Nothing at the moment!&lt;/p&gt;

&lt;p&gt;Let’s fix that. In your file &lt;code&gt;app.py&lt;/code&gt; lets add another route. You can add a route with the url &lt;code&gt;scheduleme&lt;/code&gt; to match what you appended to the end of your heroku app url in the &lt;code&gt;Create New Command&lt;/code&gt; form on Slack. You will be doing data processing in this route with the information from the slash command. To indicate that data is sent with the request, you need to add &lt;code&gt;methods=['POST']&lt;/code&gt; as the second &lt;code&gt;@app.route&lt;/code&gt; parameter. The function name on line 14 doesn’t matter in this case, but for the sake of consistency, you can call it &lt;code&gt;scheduleme&lt;/code&gt; as well. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from flask import Flask
app = Flask(__name__)
PORT = 4390

@app.route('/')
def homepage():
    return "Howdy hacker!!"

@app.route('/scheduleme', methods=['POST'])
def scheduleme():
    return 'I would like to schedule that, but I haven\'t quite figured out how...'

if __name__ == '__main__':
    app.run(debug=True, port=PORT)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This function doesn’t do much — it just returns some text. But lets deploy it to make sure you properly connected your app up to Slack. &lt;/p&gt;

&lt;p&gt;Add, commit and push this code to master to make it live!&lt;/p&gt;

&lt;p&gt;Then, go to your Slack workspace and type in &lt;code&gt;/scheduleme&lt;/code&gt; and hit enter. Your bot should respond with:&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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531256268768_Screen%2BShot%2B2018-07-10%2Bat%2B1.57.06%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531256268768_Screen%2BShot%2B2018-07-10%2Bat%2B1.57.06%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;You have officially made a Slack bot! Now lets add some functionality!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your bot responded properly, skip this section and continue onto Part 4!&lt;/p&gt;

&lt;p&gt;If you get a &lt;code&gt;404&lt;/code&gt; here, there are a few strategies to identify whats going wrong.&lt;/p&gt;

&lt;p&gt;To see if the issue is with your application code, user a cURL request to hit that endpoint.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -X POST -F 'text="Post tutorial celebration dance" "Now" "Today at 11pm"' https://&amp;lt;your-app-url&amp;gt;/scheduleme&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Replace &lt;code&gt;&amp;lt;your-app-url&amp;gt;&lt;/code&gt;&lt;span&gt;with your Heroku app url. This command is bypassing Slack, and hitting your application code directly. If the bot does not respond properly, then the problem must be with your application. If your bot does respond properly, then the issue is likely from the connection with your Slack slash command.  &lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Does your code matches the code above?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Has this new code been deployed — either automatically if you have that set up or otherwise manually via the Heroku UI or Heroku command line interface?&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Run the app locally and cURL it from the command line&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;curl -X POST -F 'text="Post tutorial celebration dance" "Now" "Today at 11pm"' https://127.0.0.1:4390/scheduleme&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you have the Heroku command line interface installed, you can see the logs with &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;heroku logs --tail -a &amp;lt;your-app-name&amp;gt;&lt;/code&gt;;&lt;/p&gt;

&lt;ul&gt;
&lt;ul&gt;
&lt;li&gt;You have to be logged in via heroku login for this to work. &lt;/li&gt;
&lt;/ul&gt;




&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Connection Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In Slack, when you type in &lt;code&gt;/scheduleme&lt;/code&gt; does the command help pop up? If not, your app likely has not be installed to your workspace. Make sure Step 3 was completed properly. &lt;/li&gt;
&lt;li&gt;On the edit command page that you filled out in Step 2, does your request url end in &lt;code&gt;/scheduleme&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id="part-4"&gt;&lt;strong&gt;Part 4: Add text parsing functionality&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;When you use the slash command, you are going to send some text with it. In this step, you’re going to parse that text and make sure you have the correct number of arguments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 0: Add new dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For parsing the request sent by Slack, you are going to use two new dependencies. First, you are going to import &lt;code&gt;request&lt;/code&gt; from Flask. You will use this to access the information in the request. Next you are going to unpack the text of the request using a regular expression. For this you need to add  &lt;code&gt;import re&lt;/code&gt; to your import section.&lt;/p&gt;

&lt;p&gt;The top of the file will now look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from flask import Flask, request
import re
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Get the text from the request&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;request&lt;/code&gt; library, you will need to get the text sent with the slash command. You can do this by adding &lt;code&gt;raw_text = request.form.get('text')&lt;/code&gt; at the top of your &lt;code&gt;scheduleme&lt;/code&gt; route. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Unwrap the text&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The text that is sent from the slash command is the string following the command name. For example, if you type &lt;code&gt;/scheduleme "Workout" "Tomorrow at 7:30am" "Tomorrow at 8:30am"&lt;/code&gt; into Slack, the text that you will get in the previous step will &lt;code&gt;"Workout" "Tomorrow at 7:30am" "Tomorrow at 8:30am"&lt;/code&gt;. It is important to note that this text is wrapped in single quotes with each of the three components wrapped in double quotes. You can use a regular expression to unwrap the text from single quotes and put all the components into an array.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;text_array = re.findall(r'"(.*?)"', raw_text)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Error handling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should make sure that you have the proper number of arguments and warn if not.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if len(text_array) != 3: 
    return 'The format is /scheduleme "[title]" "[start date &amp;amp; time]" "[end date &amp;amp; time]"'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Pull out event components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Finally, lets pull out the different components from the array and give them better names. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;title, start, end = text_array&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then to make sure this is all working swimmingly, you can pass them all to a return statement like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;return 'Sweet I parsed the title: {}, start: {} and end: {}'.format(title, start, end)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At the end of this section, your app.py file will look like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from flask import Flask, request
import re
import os
import dateparser
import requests
 
app = Flask(__name__)
 
PORT = 4390
 
@app.route('/')
def homepage():
    return "Howdy hacker!!"
 
@app.route('/scheduleme', methods=['POST'])
def scheduleme():
    raw_text = request.form.get('text')

    text_array = re.findall(r'["“”‘’\'](.*?)["“”‘’\']', raw_text)
    if len(text_array) != 3:
        return 'The format is /scheduleme "[title]" "[start date &amp;amp; time]" "[end date &amp;amp; time]"'
    title, start, end = text_array
    
    return f'Sweet I parsed the title: {title}, start: {start} and end: {end}'
    
if __name__ == '__main__': 
    app.run(debug=True, port=PORT)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add, commit and push this code! To test it, type in a full &lt;code&gt;/scheduleme&lt;/code&gt; command to Slack like:&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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-5.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%2Fcdn2.hubspot.net%2Fhubfs%2F3314308%2Fimage_preview-5.png" alt="image_preview-5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and you should see:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531258937486_Screen%2BShot%2B2018-07-10%2Bat%2B2.41.35%2BPM.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%2Fd2mxuefqeaa7sj.cloudfront.net%2Fs_9E966E4D153A92A6C1EE7A51F0576B6B726D6CE9A933DDB5AB33E608A1C0317A_1531258937486_Screen%2BShot%2B2018-07-10%2Bat%2B2.41.35%2BPM.png" alt="null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="part-5"&gt;&lt;strong&gt;Part 5: Build scheduling functionality&lt;/strong&gt;&lt;/h2&gt;

&lt;p&gt;This is your last part — hooray you are almost finished! In this last step, you’re going to sent the request to the Nylas API that will create an event on your calendar. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 0: Access the config variables in your code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To access config variables in your code, you will need to add another dependency, &lt;code&gt;os&lt;/code&gt;. Add the line &lt;code&gt;import os&lt;/code&gt; to the bottom of your import statements. &lt;/p&gt;

&lt;p&gt;Next, lets use &lt;code&gt;os&lt;/code&gt; to pull the values of your config variables from Heroku into your code. Replace the return statement with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;calendar_id = os.environ.get('CALENDAR_ID')
assert calendar_id is not None, 'Missing `CALENDAR_ID` config variable'
access_token = os.environ.get('ACCESS_TOKEN')
assert access_token is not None, 'Missing `ACCESS_TOKEN` config variable'
timezone = os.environ.get('TIMEZONE')
assert timezone is not None, 'Missing `TIMEZONE` config variable'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Build request components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You are going to use the &lt;code&gt;requests&lt;/code&gt; library to send the request to the Nylas API. Add &lt;code&gt;import requests&lt;/code&gt; to your import statements. You are also going to use &lt;code&gt;dateparser&lt;/code&gt; which takes a variety of inputs for example, “Today at 3pm”, “12/31/2018 at 11:30pm”, “Friday at 9am” and turns them into datetime objects. Using this library gives you more flexibility for what you can type in as the start and end components of the slash command. Add &lt;code&gt;import dateparser&lt;/code&gt; to your import statements. &lt;/p&gt;

&lt;p&gt;The request is comprised of a few components — a URL, headers and json. First, you will create a dictionary of the &lt;code&gt;timezone_settings&lt;/code&gt;. You will pass this into &lt;code&gt;dateparser.parse()&lt;/code&gt; to indicate that you want the date to be parsed into a timezone aware datetime object. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;timezone_settings = {'TIMEZONE': timezone, 'RETURN_AS_TIMEZONE_AWARE': True}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;span&gt;Now you have all the information to create the meat of the request, the &lt;code&gt;json&lt;/code&gt; argument. For this, you will create a dictionary with &lt;code&gt;calendar_id&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;when&lt;/code&gt; keys. The when dictionary has two keys &lt;code&gt;start_time&lt;/code&gt; and &lt;code&gt;end_time&lt;/code&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;json = {
        'calendar_id': calendar_id, 
        'title': title, 
        'when': {
                'start_time': int(dateparser.parse(start, settings=timezone_settings).timestamp()), 
                'end_time': int(dateparser.parse(end, settings=timezone_settings).timestamp())
            }
        }&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;span&gt;There’s a lot going on in the values of &lt;code&gt;start_time&lt;/code&gt; and &lt;code&gt;end_time&lt;/code&gt;! The first step, &lt;code&gt;dateparser.parse(start, settings=timezone_settings)&lt;/code&gt;, is parsing the string text into a timezone aware datetime. Then, &lt;code&gt;.timestamp()&lt;/code&gt; is turning it into a &lt;/span&gt;&lt;a href="https://en.wikipedia.org/wiki/Unix_time" rel="noopener noreferrer"&gt;unix timestamp&lt;/a&gt;&lt;span&gt;. This timestamp is a float so the final step is turning it into an integer with &lt;code&gt;int()&lt;/code&gt;.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;The final building block for the request is the headers where you will pass in the access token to prove that you have authorized your account. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;headers = {'authorization': access_token}&lt;/code&gt; &lt;/pre&gt;

&lt;p&gt;Finally, bring all these pieces together and wrap it in a a try except block to capture any exceptions.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;try: 
    response = requests.post('https://api.nylas.com/events', headers=headers, json=json) 
    if response.status_code == 200: 
        return f'Wohoo! {title} was scheduled from {start} to {end}' 
    else: 
        return f'Error! Our response has a status of {response.status_code} and text {response.text}' 
except Exception as e: 
    return f'An exception {e} occurred creating the event!'&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Use the requests library to make a post request to the URL &lt;code&gt;'https://api.nylas.com/events'&lt;/code&gt; passing in the headers and the json as key word arguments. &lt;/p&gt;

&lt;p&gt;Your app.py file will end up like&lt;span&gt; &lt;/span&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;from flask import Flask, request
import re
import os
import dateparser
import requests

app = Flask(__name__)
PORT = 4390
@app.route('/')

def homepage():
    return "Howdy hacker!!"

@app.route('/scheduleme', methods=['POST'])
def scheduleme():
    raw_text = request.form.get('text')
    
    text_array = re.findall(r'["“”‘’\'](.*?)["“”‘’\']', raw_text)
    if len(text_array) != 3:
        return 'The format is /scheduleme "[title]" "[start date &amp;amp; time]" "[end date &amp;amp; time]"'
    title, start, end = text_array
    
    calendar_id = os.environ.get('CALENDAR_ID')
    assert calendar_id is not None, 'Missing `CALENDAR_ID` config variable'
    access_token = os.environ.get('ACCESS_TOKEN')
    assert access_token is not None, 'Missing `ACCESS_TOKEN` config variable'
    timezone = os.environ.get('TIMEZONE')
    assert timezone is not None, 'Missing `TIMEZONE` config variable'

    timezone_settings = {'TIMEZONE': timezone, 'RETURN_AS_TIMEZONE_AWARE': True
    json = {
        'calendar_id': calendar_id, 
        'title': title, 
        'when': {
                'start_time': int(dateparser.parse(start, settings=timezone_settings).timestamp()), 
                'end_time': int(dateparser.parse(end, settings=timezone_settings).timestamp())
            }
        }
    headers = {'authorization': access_token}

    try:
        if response.status_code == 200:
            return f'Wohoo! {title} was scheduled from {start} to {end}'
        else:
            return f'Error! Our response has a status of {response.status_code} and text {response.text}'
    except Exception as e:
        return f'An exception {e} occurred creating the event!'

if __name__ == '__main__':
    app.run(debug=True, port=PORT)&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Add, commit and push this. Yay, your Slackbot is complete! Head over to Slack and enter &lt;code&gt;/scheduleme "Post tutorial celebration dance" "Now" "Today at 11pm".&lt;/code&gt; Then, head over to your calendar and you will see a well deserved celebration dance for scheduled, appropriately, for the rest of the day. &lt;/p&gt;

&lt;p&gt;If you run into any issues along the way, checkout the debugging section for help! &lt;/p&gt;

&lt;h2&gt;Next steps for those who are so inclined… &lt;/h2&gt;

&lt;p&gt;This is a basic scheduler Slack bot — it just adds an event with a title. The Nylas API lets you create events with a lot more information associated with them. Some cool extensions would be to add the functionality to support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;span&gt;Creating events with descriptions&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Creating events with locations &lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Adding participants to events&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Creating recurring events&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Updating existing events&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Deleting existing events&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Adding events to different calendars&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt; &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

</description>
      <category>api</category>
      <category>engineering</category>
      <category>tips</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to Tackle the Learning Curve of your First Major Engineering Project</title>
      <dc:creator>annielcook</dc:creator>
      <pubDate>Wed, 28 Feb 2018 17:00:00 +0000</pubDate>
      <link>https://forem.com/nylas/how-to-tackle-the-learning-curve-of-your-first-major-engineering-project-1fhi</link>
      <guid>https://forem.com/nylas/how-to-tackle-the-learning-curve-of-your-first-major-engineering-project-1fhi</guid>
      <description>&lt;p&gt;Six weeks into my first full-time engineering job I was tasked with &lt;a href="https://dev.to/nylas/lessons-learned-syncing-800-million-contacts-to-our-database-4ff4"&gt;a major project that put my programming abilities to the test&lt;/a&gt;. I had to switch from the language and mindset I was most familiar with, Javascript, and dive head first into &lt;a href="https://www.nylas.com/"&gt;Nylas’s&lt;/a&gt; 4-year-old Python codebase in order to build a API to sync over 800 million contacts (and rising). What ensued was a 16 week-long project that deepened my understanding of engineering fullstack systems at scale and taught me about the challenges of tackling your first major project.&lt;/p&gt;

&lt;p&gt;Many engineers question their abilities early on. Diving into a new company with an entirely new codebase as well as a unique set of infrastructure, monitoring systems, best practices, and workflows can feel overwhelming. My goal with this blog is to offer advice to other engineers who are early on in their careers. But first, let me provide some context about the product that Nylas offer and the project I was tasked with: updating our Contacts API.&lt;/p&gt;

&lt;p&gt;If you’re curious about the technical details of the project, &lt;a href="https://www.nylas.com/blog/lessons-learned-syncing-800-million-contacts-to-our-database"&gt;check out my previous post&lt;/a&gt;. What you need to know for now is that the Nylas APIs allow developers to build email, calendar, and contacts functionality (and two-way sync) into their apps. From early product feedback, we found that customers were asking for new features to our contacts API. We previously had a read-only functionality for very limited contact data, and customers were asking for more functionality. In short, this was by far my biggest and most complex fullstack project to date. I was excited, albeit nervous, to tackle this project, and I learned a lot along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Writing good tests is the best way to ensure you are writing good code.
&lt;/h3&gt;

&lt;p&gt;The importance of writing good tests is one of those lessons that resurfaces with every project I work on. Your objective as a programmer is to write good code. The best way to evaluate your code, and continue to evaluate it as other factors in the code or environment change, is to write good tests. It’s hypothetically a simple concept, but one that’s surprisingly difficult to implement, even for people who aren’t new to the industry.&lt;/p&gt;

&lt;p&gt;On this project I learned to appreciate the power of unit tests for debugging. As the name suggests, unit tests isolate and test a specific unit of interest. Previously, when I would run into a bug that I was unsure about, I would immediately dive into trying to solve it. This was usually before I had a good understanding of what was going on and before I could replicate it. This meant that I spent more time debugging, had less confidence in my solutions, and had very little understanding of the causes. &lt;/p&gt;

&lt;p&gt;Using unit tests to debug an issue is a different process. With unit tests in place, I start by isolating the problem and write a test that mocks out everything else before I dive into debugging. It’s essential to do this because you want to make sure that the buggy behavior isn’t influenced by other elements. Once you have the problem isolated, the test starts failing because of the bug. Now this failing test is a solid way to measure when the bug is fixed and if it will stay fixed. Once I have the test or tests in place, I can confidently dive into debugging knowing that the goal is to get the test to pass. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Although writing tests takes time that isn’t directly going towards the product, it will ultimately save time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Use your mentor as a resource.
&lt;/h3&gt;

&lt;p&gt;Joining a new company can be intimidating because there is so much to learn. To lessen the learning curve, Nylas pairs new hires with senior engineers. For the Contacts v2.0 project, I worked with &lt;a href="https://dev.to/karim_hamidou"&gt;Karim&lt;/a&gt; as my coding partner, mentor, and overall resource.&lt;/p&gt;

&lt;p&gt;In the beginning of the project, we worked exclusively through pair programming with me typing and him guiding and explaining along the way. As I gained confidence and understanding, I slowly began to take over and do larger pieces on my own. For a while, I still relied on him heavily to field questions as I worked. Over time, my volume of questions decreased. Eventually, I became the project lead and the go-to resource to answer questions coming from the project manager, our customers, and my fellow engineers.&lt;/p&gt;

&lt;p&gt;This paring was an essential part of my growth in the first few months at Nylas. If your company doesn’t already do pair programming, I recommend suggesting it because it is incredibly beneficial, especially for new hires.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Without Karim’s mentoring throughout, my project would not have been as successful, and I would not feel as confident in my abilities going forward.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Ask questions even when you don’t know what to ask.
&lt;/h3&gt;

&lt;p&gt;In the beginning of Contacts v2.0, I often didn’t speak up when I was confused because I felt that I didn’t even know what to ask. Some concepts felt so far beyond my understanding that I wasn’t able to piece together a question. I was embarrassed to ask what might be seen as basic questions, and even more worried that it might expose some fatal flaw in my ability. So instead, I would silently trudge along in my haze of confusion hoping that time would bring eventually clarity.&lt;/p&gt;

&lt;p&gt;I quickly learned that this was not the best strategy. While it’s possible that time might bring clarity, often it doesn’t. And no matter what, clarity would have come more quickly had I just asked. Instead of my silence making me look like I understood exactly what was going on, it simply hid that I wasn’t following along. These gaps in my understanding would almost certainly have backfired later in the project at a time when it was more critical. In addition, if came out at that point that I not only didn’t understand the current concept, but had been in the dark all along, it would have reflected poorly on my entire performance.&lt;/p&gt;

&lt;p&gt;However, simply knowing that you &lt;em&gt;should&lt;/em&gt; ask a question doesn’t always help with knowing &lt;em&gt;what&lt;/em&gt; to ask. My strategy for how to ask the questions when you can’t easily piece together the problem is to back up. If you are nested deep into some complex process, it can be helpful to continue to take steps back until you are at a level that you feel comfortable with. If you have some uncertainty regarding a function, follow the stack trace up until you get to a process you are familiar with. If all else fails, be very forthcoming about your confusion and ask your mentor to start with the basics.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It’s also important to keep in mind that you’re probably not the only one to have these questions. Asking questions is not a sign of weakness, but a desire to learn.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Reflection takes time, but is time well spent.
&lt;/h3&gt;

&lt;p&gt;When I first joined Nylas, I felt like every day was full of new terms, processes, techniques, tips and projects. I would ask a lot of questions and receive great answers, but I felt like a lot of the information wasn’t sticking.&lt;/p&gt;

&lt;p&gt;To get more information to sink in and feel like I could answer questions about previous work, I started keeping a work log. It’s very simple; just a Google doc where I write things that come up throughout each workday — tips that I learn, how to use certain scripts, bugs that I run into, questions that arise, and the subsequent answers to those questions. &lt;/p&gt;

&lt;p&gt;In a fast-paced startup environment it’s easy to always be in forward motion, finishing one project and moving to the next. Keeping this log forces me to slow down and reflect on my work. Sometimes, the process of writing things down reveals gaps in my understanding.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In a field where we are constantly needing to stay on top of new technologies, strategies that ease the learning process are important. Keeping a Nylas learning diary is one thing that helps me. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;My experience with this project — from questioning my abilities as a programmer, to slowly becoming the lead engineer, to beta testing directly with customers, and updating the SDKs to ultimately launching — has given me greater confidence. I am excited for all the learning to come in the next project. Every engineer’s experience is different, but I hope that sharing what I learned in my first major engineering project will help others!&lt;/p&gt;




&lt;p&gt;This blogpost was originally published on the &lt;a href="https://www.nylas.com/blog/how-to-tackle-the-learning-curve-and-self-doubt-of-your-first-major-engineering-project"&gt;Nylas Engineering Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>career</category>
      <category>learning</category>
    </item>
    <item>
      <title>Lessons Learned Syncing 800 Million Contacts To Our Database</title>
      <dc:creator>annielcook</dc:creator>
      <pubDate>Wed, 14 Feb 2018 20:34:03 +0000</pubDate>
      <link>https://forem.com/nylas/lessons-learned-syncing-800-million-contacts-to-our-database-4ff4</link>
      <guid>https://forem.com/nylas/lessons-learned-syncing-800-million-contacts-to-our-database-4ff4</guid>
      <description>&lt;p&gt;The Nylas APIs allow developers to build email, calendar, and contacts functionality into their applications. Our goal is to support all three of these points fully, but until recently, our Contacts API didn’t have the full functionality that we wanted. Nylas is my first full-time engineering job, and shortly after joining, this need to upgrade the Contacts API surfaced again. It was a big task, but one that I was excited to take point on. In this blogpost, I’ll walk through the problem, the solution we came up with, and a few of the things that I learned as a result.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Our Contacts API gave customers (mostly B2B software companies) read-only functionality which allowed them to get a list of contacts with three fields: name, email and phone numbers. We wanted to give them access to more data so they could empower their users to make better connections with peers, colleagues, candidates, and more. To do this, they needed to be able to read, sync, update, and auto-complete data including contact addresses, multiple email addresses, birthdays, job titles and more.&lt;/p&gt;

&lt;p&gt;Changing our product to sync and store huge amounts of new data without affecting current customers was a big challenge to tackle for the team in addition to all of the other features we’re building, but one worth taking on for the sake of our customers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The first component of the solution was to expand our contact model to store more fields. Below is a comparison of the old contact model, Contact v1.0, and the new model, Contact v2.0. Both of these have the system assigned fields of &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;account_id&lt;/code&gt; and &lt;code&gt;object&lt;/code&gt;. Apart from those, we significantly increased the number and granularity of the contact fields for our new and improved contact model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contact v1.0:&lt;/strong&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x2x2x2x2x2x2x2x2x2x2x2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"z3z3z3z3z3z3z3z3z3z3z3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"phone_numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1 800 123 4567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mobile"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Contact v2.0:&lt;/strong&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"account_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"x2x2x2x2x2x2x2x2x2x2x2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"birthday"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1960-12-31"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"company_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;"Nylas"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"emails"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"john@doe.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"work"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"given_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;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"z3z3z3z3z3z3z3z3z3z3z3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"im_addresses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"im_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myaimaddress"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"aim"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"job_title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Software Engineer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"manager_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bill the manager"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"middle_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;"Jacob"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"nickname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"JD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"notes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Loves ramen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"contact"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"office_location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123 North Pole Dr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"phone_numbers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1 800 123 4567"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mobile"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"physical_addresses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"structured"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"home"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"street_address"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200 Santa Clause Ln"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"city"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"North Pole"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"postal_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123123"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USA"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"picture_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.nylas.com/contacts/427abc427abc427abc/picture"&lt;/span&gt;&lt;span class="p"&gt;,,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"suffix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Jr."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"surname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"web_pages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"johndoeblog.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blog"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also expanded the API endpoints and underlying functionality for contacts. With Contacts v2.0, customers have the ability to create, update, and delete contacts from their own applications.&lt;/p&gt;

&lt;p&gt;Here are the new endpoints we’ve included in Contacts v2.0:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.nylas.com%2Fhs-fs%2Fhubfs%2Fblog%2520images%2FContacts%2520v2.0%2520-%2520Technical%2Fcontactsv2-functionality.png%3Ft%3D1518652965690%26width%3D558%26name%3Dcontactsv2-functionality.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%2Fwww.nylas.com%2Fhs-fs%2Fhubfs%2Fblog%2520images%2FContacts%2520v2.0%2520-%2520Technical%2Fcontactsv2-functionality.png%3Ft%3D1518652965690%26width%3D558%26name%3Dcontactsv2-functionality.png" alt="contacts v2 functionality"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This new functionality can be used in many different ways, each of which streamline the problems that our customers are facing on a daily basis. For instance, automatically creating a new contact record in the Contacts API reduces the need for sales, marketing and recruiting teams to manually create new contacts in their CRM, since that data syncs seamlessly in the background from their inbox to their CRM.&lt;/p&gt;

&lt;p&gt;We also added the ability to sort contacts by a variety of parameters. For example, by postal code: I want to know all the sales reps working in zip code 94105, by phone number: Who called me?, by email: Show me the contact for this email, etc…&lt;/p&gt;

&lt;p&gt;Check out the Contacts v2.0 &lt;a href="https://docs.nylas.com/reference#contacts-intro" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for more information on the new functionality and endpoints that are available.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Takeaways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Put your product to the test.
&lt;/h3&gt;

&lt;p&gt;Writing good tests is important, but so is really using your product. When I thought I was finished with my Contacts v2.0 work, I started updating our Nylas NodeJS SDK to support the new functionality. This SDK is a NodeJS wrapper for our API which makes integrating the Nylas API into Javascript applications much easier. For the SDK, I had to add functions that enabled it to access the new endpoints, write tests for these functions, and expand the example applications in the SDK to include Contacts v2.0 functionality.&lt;/p&gt;

&lt;p&gt;The process of updating the SDK meant that I had to use all of the new features and functionality that I had just added to the API for Contacts v2.0. This opened my eyes to previously unknown bugs. For example, I was trying to populate the contact model for the NodeJS SDK with contact data from our API and everything except the middle_name field was working. It turns out I had forgotten to encode and return the middle_name field for contact GET requests.&lt;/p&gt;

&lt;p&gt;Actually using the product also showed me processes that while not broken, had a poor user experience. For example, our API used to require that the JSON for a contact’s birthday be in the form "birthday": { "date": "1995-01-13", "object": "date"}. Wrapping the date into this object was unnecessary and just added more work to represent the contact’s birthday as a Date in the SDKs. I simplified the birthday field to be "birthday": "1995-01-13".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Having to develop on top of the Contacts v2.0 API revealed bugs and put me into the customer’s perspective to see design flaws that I would not have noticed otherwise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  You don’t have to, and often shouldn’t, ship big projects all at once.
&lt;/h3&gt;

&lt;p&gt;Contacts v2.0 was a major fullstack project that affected a significant portion of our code base.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We had to expand the previous contacts table in the database and create many related tables to support the huge volumes of new data we would now be storing.&lt;/li&gt;
&lt;li&gt;We had to build out two-way data sync between the providers and our system.&lt;/li&gt;
&lt;li&gt;We had to build new API endpoints and underlying functionality to support the new create, update, and delete features.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of this, we knew we would need to test extensively on production. The only way to encounter the wide variety of edge cases was to sync real customer data on production, which meant we had to deploy possibly faulty code to prod without affecting our existing customers. In order to accomplish this, we shipped code under a feature flag. This meant that we could deploy code periodically and in manageable pieces without exposing any changes to our customers. Once the project was near completion, we held an open beta to get a few interested customers to try out and test the new features. Before this official release, the code has been running on production for over 4 months and a handful of customers have tested it and given us feedback.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Shipping code under a feature flag throughout development and letting customers beta test gave me confidence in the code before I officially release it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Tradeoffs of API versioning
&lt;/h3&gt;

&lt;p&gt;The way we handled this feature flag was by versioning our API and keeping the new version we were developing private. The decision to version the API, thus enabling us to make breaking changes, was challenging and interesting.&lt;/p&gt;

&lt;p&gt;On one hand, API versioning is intimidating to customers. This is especially true the first time customers have to update versions. They might be wary of the new version and uncertain whether updating is worth the additional engineering effort that comes with updating versions.&lt;/p&gt;

&lt;p&gt;On the other hand, versioning our API lets us evolve it to better meet customer’s needs while keeping the product polished and clean. Getting customers used to updating versions of the API will allow our engineering focus to be exclusively on building out new functionality rather than supporting legacy functionality. Versioning also gives our customers an easy upgrade path and puts them in control of updating their applications.&lt;/p&gt;

&lt;p&gt;The pros and cons of API versioning differ based on your product and what benefits the new version will bring to customers. Reasoning through this decision forced me to think about API versioning from a few different perspectives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What would be best for the future of our product?&lt;/li&gt;
&lt;li&gt;What would be best for customer experience?&lt;/li&gt;
&lt;li&gt;What would be the cleanest and most sustainable engineering solution?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;This experience broadened my understanding of how to make design decisions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This was a big project to tackle during my first six months at Nylas, but I’m really excited about how it turned out. The fact that I was able to work on something that directly impacted our customers allowed me to learn a lot about their needs along the way. As I’ll share in a future post, this experience has helped me grow as an engineer and learn more about myself along the way. Check out &lt;a href="https://docs.nylas.com/reference#contacts-intro" rel="noopener noreferrer"&gt;Contacts v2.0&lt;/a&gt; and let us know what you think!&lt;/p&gt;




&lt;p&gt;This blogpost was originally posted on the &lt;a href="https://www.nylas.com/blog/lessons-learned-syncing-800-million-contacts-to-our-database" rel="noopener noreferrer"&gt;Nylas Engineering Blog&lt;/a&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>learning</category>
      <category>email</category>
      <category>scale</category>
    </item>
  </channel>
</rss>
