How to Validate Twilio Function Inputs with Zod
Time to read: 7 minutes
How to Validate Twilio Function Inputs with Zod
One of key concepts when creating an API, as you may do with Twilio Serverless Functions, is validating your inputs. Not only will garbage in, result in garbage out but ensuring incoming data has been cleansed is vital for security and proper operation
Zod, a powerful data validation library available via NPM is designed exactly for this use case. It aims to make defining, and managing data structures much easier than would otherwise be possible with vanilla JavaScript or TypeScript.
This article will detail how the Zod NPM package can be leveraged in Twilio Serverless Functions to parse, validate, and transform input data ready for handling in your core business logic.
Prerequisites
- A free Twilio account
- An SMS enabled Twilio phone number - Learn how to get one here
- Postman (for testing purposes)
Creating a Simple SMS Function
To get started, create a new Twilio Functions Service that will contain our custom code. From the Twilio Console, use the left-hand menu to find the Functions & Assets section (You may need to use the Explore Products option to find this).
Under Services, click the blue Create Service button and name your service twilio-zod. Once created, the editor will open for you. There won’t be any functions or assets just yet but this will soon change!
Via the blue Add + button at the top of the editor, create a new function. This function will send an SMS to a number provided so give it a descriptive name such as send-sms.
By default, this function will be protected, meaning only incoming requests with a valid X-Twilio-Signature header can execute the code. This is great for overall security but for now, click on the small lock icon and make this a public function. This means anyone with the URL can now execute your function, not great for security, however this will make it easier for you to test shortly. In development this is usually fine but it is worth considering the various levels as you move towards production level code. The visibility you choose for functions & assets will largely depend on how you expect the function to be called in your system, and choosing the strictest access policy is best practice.
The function will have some boilerplate code to get started. Replace the content with the code below which will set up our function to send an SMS. The code comments describe what takes place:
Now test and make sure everything is functional up to this point. Save the code with the Save button and use the blue Deploy All button in the button level to prepare your code ready to use. This will take a few seconds so whilst waiting you can prepare to send your first request.
Click on the 3 vertical gray dots next to the function name and then click Copy URL to copy the URL of the new function. This is the publicly accessible address where a request can be sent to dispatch an SMS. This can be tested with a tool like Postman. In the Postman web app or desktop application, Create a new HTTP request, set the URL to the one which you copied and make this a POST method. Under the Body tab, select the raw option and provide a JSON block with the variables your function needs. These will be a From number, this should be your SMS enabled Twilio phone number, a To number which can be your own mobile number, and a message Body containing the text to send:
The request on Postman should look similar to the following:
Go ahead and press Send. You should see ‘Success…’ appear in the response section and an SMS delivered to the phone number you provided. Great stuff you're on a roll, let's continue!
Adding Validation
Our SMS function can take in From
, To
, and Body
parameters. This is visible in the code from the Event
object in lines 5-7. What isn’t included however is any form of validation. You can see this if we send the request with the From
property removed. Instead of the 200 success response, in Postman we’ll instead see a 500 error code and an error message. Although you know this parameter should be set and how it should be formatted, others using the endpoint might not. We cannot guarantee that all parameters are always present or in the correct format.
It would be possible to add a null
/undefined
check to the code but this has a few drawbacks. This would need to be done for every event variable, which is rather tedious and prone to someone forgetting if the code is extended in the future, the input is only then asserted as not empty, and further conditionals will be needed for any other checks e.g. length, format, and you’ll find yourself repeating the same large blocks of code each function you make.
This is where Zod comes in, Zod helpers can be leveraged to encapsulate this type of code into readable chunks that are easy to define and extend whilst giving a lot of power in how inputs are validated.
Including the Zod Dependency
Back in the editor, navigate to the Dependencies section under Settings & More. There will be some default dependencies already defined, and using the module input, Zod can be added also. For the Module type zod and Version use 3.23.8, click Add. Zod will now be accessible in our function handlers.
Using Zod
The SMS function can now be extended with the code below. Copy and replace the existing code in the /send-sms Function with the following:
The zod package is required at the top of the function, this package exposes a helpful z
property containing all the utilities needed. Our schema is then defined using z.object({...})
. This is used to describe the expected incoming event and is currently pretty simple, with 3 string properties. Notice how the .optional()
tag has been added to the Body
property since a default is declared for this below on line 24.
The remainder of the code is largely the same, the key area to note is the usage of Zod’s .safeParse()
helper to parse our actual event (which was used directly before) to ensure it adheres to the defined schema. If it does not, a 400 error is returned informing the caller of bad input data.
Click Save and Deploy All to redeploy the application with the new changes. Navigate back to Postman and send another POST request with the same body from the previous section. With all the expected parameters defined there should be no visible change in behavior externally. Try removing the From property however and what you’ll see instead is a 400 response with the error message Invalid input.
That is solid progress. We have now clearly indicated to the caller, via the 400 status code, that the incoming request is malformed and our message sent back indicates this too.
Further Restricting Inputs
The validation logic enforces that the properties described in the schema exist and are not undefined or null, increasing the confidence that data is present during the subsequent code execution. If further properties were required in this function, the schema could be extended in a single location and they would be accessible on the parsedEvent
variable.
This only scratches the surface of possible Zod validation. The schema can be extended a little further for this use case to demonstrate some additional helpers. Copy and replace the existing code in the /send-sms Function with the following:
The first change made is the extended schema definition. The phone number fields, From
and To
have been extended to ensure they start with a +
, providing a strong indication that it is in fact a phone number being provided. Messages have also been added which are shown as the errors if the event that the defined conditions are not met. This becomes helpful when returning 400 errors. Instead of naively returning invalid data as before, the response text is set to the validation error which will be the message defined in the schema, meaning that the caller now knows exactly the issue faced making it easier to correct on their side.
Save & redeploy once more. This time if you send a request without the From
field you will receive a “ A ‘From’ number is required” message. Add this field back in but this time remove the +
and now the message will be “ From number must be E.164 formatted”.
Next Steps
From just this simple example it is clear to see how powerful a tool like Zod can be for input validation without bloating code excessively with a large number of checks & conditionals. The Zod documentation has an exhaustive list of helpers for various types such as strings, boolean, or numbers.
A good challenge would be enhancing the E.164 phone number validation. Starting with a +
is a good start but it’s possible to be stricter on the inputs allowed, a regex might be a good fit. You could also try instead accepting local phone numbers. If you know valid inputs then you could use a transform to convert this to an E.164 number ready to pass on to the SMS API without adding any additional logic to your main function handler.
Twilio Zod Package
For many common Twilio use cases, the Twilio Zod NPM package provides additional utilities building upon what Zod already offers. If you commonly need to validate Twilio SID’s, parse JSON attributes on Calls, Conversations, Tasks, or gracefully handle errors from Twilio it can be a helpful addition.
Conclusion
Great job! Your Twilio Functions now have a validation layer that you can adjust to fit exactly what each function requires and is easily extendable going forward.
With validation included upfront in your function executions you can have a higher degree of confidence in the data you are consuming which can help reduce errors and increase security. Be sure to check out the Twilio Serverless Functions documentation, the Serverless Toolkit for moving your development to a local machine to iterate even faster, as well as the Zod Documentation for almost any validation use case you can imagine.
Tristan Blackwell is a Twilio Champion & Software Developer at Zing.dev, a Twilio Preferred Partner. He is a ‘tinkerer’, exploring what is possible through technical experimentation which he often shares on LinkedIn. In his spare time, he is usually out running.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.