Dark allows you to build backends (API endpoints, workers, cron, and data storage) by writing only your business logic, using production traces.
You can access you canvas at darklang.com/a/USERNAME (or USERNAME-CANVASNAME). For this project we recommend darklang.com/a/USERNAME-twilioreminder.
We recommend building a hello world API endpoint to get a feel for Dark, as follows:
All the major handlers work the same way, but the key for many requests is working directly with incoming data.
Daily Text Reminder App
We’re building this app: https://sample-textreminder.builtwithdark.com It’s an app that will send a text message once per day at a given time to an end user. In this case, we’ll ask the user “did you drink enough water today?” and track their response.
To try out the app and see your traces in the sample canvas text “start” to +12482653257.
To build your own, you’ll need to set up a Twilio account. Your Twilio SID & Token are on the Dashboard.
You’ll also need to add a phone number from the “more” menu on the left-hand side, then to the phone number section.
Once you’ve added a phone number, you can configure a webhook:
Sending a Message
Dark has a built in
Twilio::sendText function. We can call it in a REPL with
our own number to verify our webhook.
After we receive a response, we’ll see a 404 in the side bar section for our webhook, which we can create by hitting the “+” button.
Once the handler is created, we’re able to see the full incoming trace.
Processing & Storing Responses
In Dark, you can work directly with incoming traces. More on this in Trace Driven Development. For this handler, we can parse out the things we care about: a user deciding to start/stop using our service, or telling us if they drank enough water or not.
At each expression, we can see what the expression evaluates to for a given trace. Here, we see the From number. In this case we also do some cleaning on the response in case the user had a capital letter or an extraneous space.
For processing the various responses, we’ll use a match statement. More on Match is available, but the tutorial example has enough context to understand how it works for this case.
Sign up & Quit
Now, let’s do the “start” case. This allows us to give the user our Twilio
number, and have them text “start” to begin receiving a daily reminder. We’ll
need a data store to keep track of our users, which can be created from the
Cmd/Ctrl-k) or the side bar.
Then, we can write the logic to add users to the datastore when they text “start.” In this case we use the user’s phone number as the unique key.
The stop case is very similar, but removes the user from the datastore.
To test, you can always send a new reply to the text message (or just message your number) and walk through the trace to see the live values at each step.
Now we’ll want the cases for when the user responds to the daily question of “did you drink enough water today?”
We’ll want another data store so we can track the daily response by user (side
In this case since we do not want to use the phone number as the unique key, we
generate a unique key when storing using the built in
Finally, we need a case for when we don’t match on one of our earlier responses. We’ll want to send a text back to tell the user about the error. To prevent this from blocking other actions, we’ll emit to a background worker.
Here, we emit the user’s number (user) to a background worker named Misunderstood (and see the event as a trace). Once you’ve hit “play” on the emit expression, you should have a 404 for the worker in the side bar that you can hit the + to add. If it does not appear in the sidebar, wait ~30s or refresh the browser.
Then we can write the code similar to our earlier REPL to send a message asking for a more clear response:
Sending Reminder Messages Once A Day
To send each user a reminder once per day, we’ll want to have a cron set to run daily. In this case we’ll get all the users, then emit to a background worker to send the messages.
Much like before, this will add the worker to the 404 section where you can add it, with the traces.
The code is identical to the code in our REPL:
This is now a fully functioning application.
You could do similar ones for:
- A reminder to stand up once per hour
- A daily reminder to do a task (fill in a personal CRM, floss)
- A weekly reminder to take out the trash
Additional Topics (for Fun!)
Cron at Time of Day
If you’d like to set the reminder to occur at a specific time of day, you can
set this using a datastore. There’s a sample canvas
here. For this
application, add the “reminder time” in the
Users table, and add a case to
allow users to specify a time (that you would then convert to a Date object).