
Security News
Browserslist-rs Gets Major Refactor, Cutting Binary Size by Over 1MB
Browserslist-rs now uses static data to reduce binary size by over 1MB, improving memory use and performance for Rust-based frontend tools.
twilio-functions-utils
Advanced tools
A powerful, RxJS-powered utility library that revolutionizes Twilio serverless function development with reactive streams, functional composition, and zero-boilerplate dependency injection.
โจ What's New in v2.4+:
npm install twilio-functions-utils
twilio
- Twilio JavaScript SDK@twilio/runtime-handler
- For local developmentnpm run build # Compile TypeScript to JavaScript
npm run prebuild # Clean build directory before building
npm run test # Run Jest test suite with coverage
NODE_ENV=test npm test # Ensure test environment
npm run docs # Generate JSDoc documentation
The library supports flexible directory structures:
./functions/
or ./src/functions/
for your Twilio Functions./assets/
or ./src/assets/
for static assetsconst { useInjection, Response, BadRequestError, Result } = require('twilio-functions-utils');
// Provider: Your business logic
const sendSmsProvider = async function (to, message) {
const { client } = this;
try {
const result = await client.messages.create({
to,
from: '+1234567890',
body: message
});
return Result.ok({ sid: result.sid, status: 'sent' });
} catch (error) {
return Result.failed(error.message);
}
};
// Handler: Your Twilio Function
async function sendSmsHandler(event) {
const { env, providers } = this;
const { to, message } = event;
if (!to || !message) {
return new BadRequestError('Missing "to" or "message" parameters');
}
const result = await providers.sendSms(to, message);
if (result.isError) {
return new BadRequestError(result.error);
}
return new Response(result.data, 201);
}
// Export for Twilio
exports.handler = useInjection(sendSmsHandler, {
providers: { sendSms: sendSmsProvider }
});
const {
twilioEffect,
injectEvent,
injectClient,
requireFields,
ok,
handleError
} = require('twilio-functions-utils');
const { switchMap, map } = require('rxjs/operators');
const sendSmsEffect = context$ =>
context$.pipe(
requireFields('to', 'message'),
injectEvent(),
injectClient(),
switchMap(([event, client]) =>
client.messages.create({
to: event.to,
from: '+1234567890',
body: event.message
})
),
map(result => ({ sid: result.sid, status: 'sent' })),
ok(),
handleError()
);
exports.handler = twilioEffect(sendSmsEffect);
import {
useInjection,
Response,
BadRequestError,
Result,
InjectorFunction,
ProviderFunction
} from 'twilio-functions-utils';
// Type your environment variables
interface MyEnv {
ACCOUNT_SID: string;
AUTH_TOKEN: string;
FROM_NUMBER: string;
}
// Type your event data
interface SmsEvent {
to: string;
message: string;
}
// Type your providers
interface MyProviders {
sendSms: (to: string, message: string) => Promise<Result<{ sid: string }, string>>;
}
// Provider with full typing
const sendSmsProvider: ProviderFunction<SmsEvent, MyEnv> = async function (
to: string,
message: string
) {
const { client, env } = this;
try {
const result = await client.messages.create({
to,
from: env.FROM_NUMBER,
body: message
});
return Result.ok({ sid: result.sid });
} catch (error: any) {
return Result.failed(error.message);
}
};
// Handler with full typing
const sendSmsHandler: InjectorFunction<SmsEvent, MyEnv, MyProviders> = async function (event) {
const { env, providers } = this;
const { to, message } = event;
if (!to || !message) {
return new BadRequestError('Missing required parameters');
}
const result = await providers.sendSms(to, message);
if (result.isError) {
return new BadRequestError(result.error);
}
return new Response(result.data, 201);
};
// Export with types
export const handler = useInjection<SmsEvent, MyEnv, MyProviders>(sendSmsHandler, {
providers: { sendSms: sendSmsProvider }
});
import {
twilioEffect,
EffectWithContext,
EffectContext,
injectEvent,
injectClient,
requireFields,
ok
} from 'twilio-functions-utils';
import { switchMap, map } from 'rxjs/operators';
interface SmsEnv {
FROM_NUMBER: string;
}
interface SmsEvent {
to: string;
message: string;
}
const sendSmsEffect: EffectWithContext<SmsEnv, {}, Response> = (context$) =>
context$.pipe(
requireFields<SmsEvent>('to', 'message'),
injectEvent<SmsEvent>(),
injectClient(),
switchMap(([event, client]) =>
client.messages.create({
to: event.to,
from: process.env.FROM_NUMBER,
body: event.message
})
),
map(result => ({ sid: result.sid, status: 'sent' })),
ok()
);
export const handler = twilioEffect<SmsEnv, {}>(sendSmsEffect);
Access everything you need through clean this
context:
async function myHandler(event) {
const {
env, // Environment variables
providers, // Your business logic
request, // HTTP headers & data
cookies // Request cookies
} = this;
// Your logic here...
}
// In your providers
const fetchUser = async function (userId) {
const { client, env } = this;
try {
const user = await client.api.accounts(env.ACCOUNT_SID)
.calls
.list({ limit: 1 });
return Result.ok(user[0]);
} catch (error) {
return Result.failed('User not found');
}
};
// In your handlers
const userResult = await this.providers.fetchUser(event.userId);
if (userResult.isError) {
return new NotFoundError(userResult.error);
}
return new Response(userResult.data);
// JSON Responses
return new Response({ success: true, data: results }, 201);
// TwiML Responses
const twiml = new Twilio.twiml.VoiceResponse();
twiml.say('Hello from RxJS-powered Twilio!');
return new TwiMLResponse(twiml.toString());
// Error Responses
return new BadRequestError('Invalid input');
return new NotFoundError('Resource not found');
return new UnauthorizedError('Access denied');
return new InternalServerError('Something went wrong');
For advanced use cases, leverage the full power of reactive programming:
const complexWorkflow = context$ =>
context$.pipe(
// Validation
requireFields('customerId', 'action'),
authenticated(ctx => ctx.event.token),
// Data fetching
switchMap(ctx =>
ctx.providers.customerService.getProfile(ctx.event.customerId)
),
// Business logic
map(customer => ({
id: customer.id,
name: customer.name,
tier: customer.subscriptions.length > 0 ? 'premium' : 'basic'
})),
// Response formatting
apiResponse({ message: 'Profile retrieved successfully' }),
// Error handling
handleError(error => {
if (error.code === 'CUSTOMER_NOT_FOUND') {
return new NotFoundError('Customer not found');
}
return null; // Use default error handling
})
);
// Validation
requireFields('email', 'phone')
validateEvent(event => event.email.includes('@'))
authenticated(ctx => checkApiKey(ctx.event.apiKey))
// Data injection
injectEvent() // Get event data
injectEnv() // Get environment vars
injectClient() // Get Twilio client
injectProviders() // Get all providers
injectProvider('userService') // Get specific provider
// Response formatting
ok() // 200 response
created() // 201 response
apiResponse({ meta: { version: '1.0' } })
toTwiMLResponse() // Convert TwiML to response
// Error handling
handleError() // Comprehensive error handling
retryWithBackoff(3) // Retry failed operations
timeoutWithError(5000) // Timeout after 5 seconds
fallback(defaultValue) // Provide fallback value
require('twilio-functions-utils/dist/lib/twilio.mock.js');
const { useMock, Response } = require('twilio-functions-utils');
const { myHandler } = require('../functions/myHandler');
const mockFn = useMock(myHandler, {
providers: {
sendSms: async (to, message) => ({ sid: 'SM123', status: 'sent' })
},
env: { ACCOUNT_SID: 'AC123' },
client: { /* mock Twilio client */ }
});
test('should send SMS successfully', async () => {
const result = await mockFn({ to: '+1234567890', message: 'Hello!' });
expect(result).toBeInstanceOf(Response);
expect(result.statusCode).toBe(201);
});
const { testEffect, marbleTest, expectEmissions } = require('twilio-functions-utils');
test('should handle SMS sending with marble testing', () => {
marbleTest(({ cold, expectObservable }) => {
const context$ = cold('a|', {
a: { event: { to: '+1234567890', message: 'Test' } }
});
const result$ = sendSmsEffect(context$);
expectObservable(result$).toBe('a|', {
a: expect.objectContaining({ statusCode: 200 })
});
});
});
Built-in support for Twilio Flex token validation:
// Simple API
exports.handler = useInjection(myHandler, {
providers: { taskService },
validateToken: true // Automatically validates Flex tokens
});
// RxJS API
const flexEffect = context$ =>
context$.pipe(
validateFlexToken(), // Validates token from event.Token
// ... rest of your logic
);
// Custom token validation
const customFlexEffect = context$ =>
context$.pipe(
validateFlexTokenWithOptions({
tokenField: 'customToken',
onValidation: (result) => console.log('Token validated:', result)
}),
// ... rest of your logic
);
Your existing code works without any modifications:
// This code works exactly the same in v2.5.0
const { useInjection, Response, Result } = require('twilio-functions-utils');
async function existingHandler(event) {
const result = await this.providers.existingProvider(event);
return new Response(result.data);
}
exports.handler = useInjection(existingHandler, {
providers: { existingProvider }
});
Function | Description |
---|---|
useInjection(fn, options) | Main dependency injection wrapper |
twilioEffect(effect, options) | RxJS Effects wrapper |
useMock(fn, options) | Testing utility (test environment only) |
Class | Status Code | Usage |
---|---|---|
Response(body, statusCode) | Custom | General responses |
TwiMLResponse(twiml) | 200 | TwiML responses |
BadRequestError(message) | 400 | Invalid input |
UnauthorizedError(message) | 401 | Authentication required |
NotFoundError(message) | 404 | Resource not found |
InternalServerError(message) | 500 | Server errors |
Class | Description |
---|---|
Result.ok(data) | Success result wrapper |
Result.failed(error) | Error result wrapper |
typeOf(value) | Enhanced type checking |
# Ensure you've installed the package
npm install twilio-functions-utils
# For TypeScript projects, ensure proper types
npm install --save-dev typescript @types/node
// โ Wrong - Missing mock import
const { useMock } = require('twilio-functions-utils');
// โ
Correct - Import mock first
require('twilio-functions-utils/dist/lib/twilio.mock.js');
const { useMock } = require('twilio-functions-utils');
// Ensure NODE_ENV=test
process.env.NODE_ENV = 'test';
# Ensure RxJS is installed
npm install rxjs@^7.8.2
# Import operators correctly
const { switchMap, map } = require('rxjs/operators');
// tsconfig.json - Ensure proper configuration
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
}
}
// โ Wrong - Arrow functions lose 'this' context
const handler = (event) => {
// 'this' is undefined here
};
// โ
Correct - Use regular functions
async function handler(event) {
// 'this' context available here
const { env, providers } = this;
}
injectMany()
instead of multiple inject()
callsResult
pattern to avoid try-catch overheadWe welcome contributions! Here's how you can help:
MIT License - see LICENSE file for details.
Iago Calazans - Senior Node.js Engineer
โญ If this library helps you build amazing Twilio Functions, give it a star! โญ
Made with โค๏ธ and โ for the Twilio community
FAQs
Twilio Functions utils library
The npm package twilio-functions-utils receives a total of 162 weekly downloads. As such, twilio-functions-utils popularity was classified as not popular.
We found that twilio-functions-utils demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Browserslist-rs now uses static data to reduce binary size by over 1MB, improving memory use and performance for Rust-based frontend tools.
Research
Security News
Eight new malicious Firefox extensions impersonate games, steal OAuth tokens, hijack sessions, and exploit browser permissions to spy on users.
Security News
The official Go SDK for the Model Context Protocol is in development, with a stable, production-ready release expected by August 2025.