Frequently Asked Questions
Use a Node.js backend with the AWS SDK to interact with Pinpoint and S3. The frontend uploads the image to S3 via a presigned URL and the backend triggers the MMS sending via Pinpoint, ensuring secure credential management.
AWS S3 stores the media files (images) included in the MMS message. The Node.js backend generates presigned URLs, allowing the frontend to upload files directly to S3 without exposing AWS credentials.
Presigned URLs allow direct browser uploads to S3 without exposing your AWS secret keys on the client-side. The backend generates these temporary URLs with specific permissions and expiry times, enhancing security.
Configure CORS for your S3 bucket when you're making requests from a different domain (like your frontend app's domain) to your S3 bucket. This is crucial for direct uploads from the browser using presigned URLs.
Standard short codes or 10DLC numbers may require additional registration or may not be universally MMS-capable. Toll-free numbers are generally recommended for MMS and are easier to configure for this purpose. Always verify the capabilities of your number in the Pinpoint console.
On your Node.js backend, use the `@aws-sdk/s3-request-presigner` library. Create a `PutObjectCommand` for the desired S3 object, then call `getSignedUrl` with the S3 client, the command, and expiry options.
Pinpoint SMS and Voice v2 is used to send SMS and MMS messages, including media attachments like images. It provides a reliable and scalable way to deliver messages from your application.
Create a new IAM user with programmatic access and attach a policy allowing S3 `PutObject`, `PutObjectAcl` (if necessary), and `pinpoint-sms-voice:SendMediaMessage` actions, specifying the S3 bucket ARN and region in the IAM policy resource definition.
Use the same AWS region for your S3 bucket, IAM user, and Pinpoint phone number. Consistency across these services is crucial for the proper functioning of the application.
Ensure you have correctly configured CORS on your S3 bucket. The allowed origins in the CORS configuration must include your frontend app's origin (domain, port, protocol).
Use npm: `npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner @aws-sdk/client-pinpoint-sms-voice-v2` installs the required packages to interact with S3 and Pinpoint.
The backend project includes `server.js` (main application), `.env` (environment variables), `package.json`, `node_modules`, and `.gitignore`. The `.env` file stores sensitive AWS credentials and configuration.
Implement `try...catch` blocks around AWS SDK calls in the backend. On the frontend, use `try...catch` and consider `axios.isAxiosError` to distinguish backend errors from S3 issues. Provide informative status updates on the frontend UI.
Use the E.164 format for phone numbers when sending MMS messages with AWS Pinpoint, e.g., +12065550100. Ensure proper formatting both in the frontend input and in the backend validation.
Developer Guide: Sending MMS with Vite (React/Vue), Node.js, and AWS Pinpoint/S3
This guide provides a step-by-step walkthrough for building a system that enables users to send Multimedia Messaging Service (MMS) messages, including text and an image, using a modern web frontend built with Vite (React or Vue) and a Node.js backend interacting with AWS services.
Project Overview and Goals
What We'll Build:
We will create a full-stack application consisting of:
Problem Solved:
This guide addresses the need to securely and reliably send MMS messages containing media (like images) from a web application, without exposing sensitive AWS credentials on the client-side. It leverages AWS's scalable infrastructure for media storage and message delivery.
Technologies Used:
Architecture Diagram:
Prerequisites:
Final Outcome:
A functional web application where a user can specify a recipient phone number_ type a message_ upload an image_ and successfully send an MMS containing both the text and image to the recipient via AWS.
1. Setting up the Project Environment
We'll create two main directories:
frontend
andbackend
.1.1. Backend Setup (Node.js/Express)
1.2. Backend Project Structure (
backend/
)1.3. Configure
.gitignore
(backend/.gitignore
)1.4. Configure
.env
(backend/.env
)This file stores sensitive credentials. Ensure it's listed in
.gitignore
. IMPORTANT: You must replace the placeholder values below with your actual AWS credentials_ bucket name_ region_ and phone number.AWS_ACCESS_KEY_ID
_AWS_SECRET_ACCESS_KEY
: Credentials for your IAM user allowing programmatic access. Must be replaced.AWS_REGION
: The AWS region where your S3 bucket and Pinpoint identity reside. Consistency is crucial. Must be replaced.S3_BUCKET_NAME
: The unique name of the S3 bucket you will create. Must be replaced.PINPOINT_ORIGINATION_NUMBER
: The E.164 formatted phone number you acquired from AWS that is enabled for MMS. Must be replaced.PORT
: The port the backend API server will run on.1.5. Frontend Setup (Vite + React)
(Note: This guide provides React code. For Vue_ use
npm create vite@latest frontend -- --template vue-ts
and adapt the component logic accordingly.)1.6. Frontend Project Structure (
frontend/
)(Structure will vary slightly based on template choices)
2. AWS Setup (IAM_ S3_ Pinpoint Number)
This is a critical step requiring configuration within the AWS Management Console.
2.1. Create an IAM User
Navigate to the IAM service in the AWS Console.
Go to Users -> Add users.
Enter a User name (e.g.,
mms-sender-app-user
).Select Access key - Programmatic access as the credential type.
Click Next: Permissions.
Choose Attach existing policies directly.
Click Create policy. A new tab/window will open.
In the policy editor, switch to the JSON tab.
Paste the following policy. IMPORTANT: You must replace
your-unique-mms-media-bucket-name
with your actual bucket name andYOUR_AWS_REGION
with the region you are using (must match.env
and S3 bucket region).Note on
s3:PutObjectAcl
: Often needed for direct browser uploads using presigned URLs depending on bucket policy/ACLs. Review if necessary for your specific setup. Note onResource: ""*""
for Pinpoint: You can restrict this further by ARN if needed, e.g.,""arn:aws:sms-voice:YOUR_AWS_REGION:ACCOUNT_ID:phone-number/YOUR_PINPOINT_NUMBER_ID""
.Click Next: Tags (optional), then Next: Review.
Give the policy a Name (e.g.,
MMS-Sender-App-Policy
).Click Create policy.
Close the policy editor tab/window and return to the user creation workflow.
Refresh the policy list and search for the policy you just created (
MMS-Sender-App-Policy
). Select it.Click Next: Tags (optional), Next: Review, then Create user.
CRITICAL: On the success screen, copy the Access key ID and Secret access key. Store these securely in your
backend/.env
file immediately (replacing the placeholders). You cannot retrieve the secret key again after leaving this screen.2.2. Create an S3 Bucket
your-unique-mms-media-bucket-name
). Use the same name as in your.env
file and IAM policy.AWS_REGION
in.env
) and where your Pinpoint origination number is provisioned.2.3. Configure S3 Bucket CORS
To allow the frontend (running on
localhost
during development or your domain in production) to upload directly to the S3 bucket using the presigned URL, you need to configure Cross-Origin Resource Sharing (CORS).Go to your newly created bucket in the S3 console.
Select the Permissions tab.
Scroll down to Cross-origin resource sharing (CORS) and click Edit.
Paste the following JSON configuration. Adjust
AllowedOrigins
for your frontend's actual URL in production.Note: Add your production frontend URL (e.g.,
""https://yourapp.com""
) toAllowedOrigins
.Click Save changes.
2.4. Acquire and Verify Pinpoint Origination Number
+18005550199
) and add it to yourbackend/.env
file asPINPOINT_ORIGINATION_NUMBER
, replacing the placeholder.3. Implementing the Backend API (Node.js/Express)
We'll create two main endpoints: one to generate the S3 presigned URL for uploads and another to trigger the MMS sending.
backend/server.js
:Explanation:
.env
. Includes a check for essential environment variables at startup.cors()
: Enables Cross-Origin Resource Sharing, allowing requests from your frontend's origin. Crucially important.express.json()
: Parses incoming JSON request bodies./api/generate-presigned-url
Endpoint:filename
andcontentType
from the request body.s3Key
usingcrypto
to avoid filename collisions. It's good practice to include a prefix likemms-uploads/
.PutObjectCommand
specifying the bucket, key, and content type.getSignedUrl
from@aws-sdk/s3-request-presigner
to create a short-lived URL (expiresIn: 300
seconds).presignedUrl
and thes3Key
to the frontend. The frontend will use the URL to upload, and the key to tell the backend which file to send in the MMS./api/send-mms
Endpoint:destinationPhoneNumber
,messageBody
, and thes3Key
(obtained from the previous step) from the request body.mediaUrl
in the requireds3://bucket-name/key
format.SendMediaMessageCommand
with the destination, origination number (from.env
), message body (optional), and theMediaUrls
array containing the S3 URL.pinpointClient
to send the command.MessageId
or an error response.Running the Backend:
4. Implementing the Frontend (React Example)
This example shows a basic React component. Adapt the state management, styling, and API client (
axios
) usage as needed for your project structure.frontend/src/App.tsx
:frontend/src/App.css
(Basic Styling):Explanation:
handleFileChange
: Updates theselectedFile
state when a user chooses a file. Includes basic client-side validation for image type and size. Resets theuploadedS3Key
if a new file is chosen.handleSubmit
:/api/generate-presigned-url
with the filename and type. Stores the returnedpresignedUrl
ands3Key
.presignedUrl
. The request body is theselectedFile
itself. Crucially, it sets theContent-Type
header to match the file type – S3 relies on this./api/send-mms
with the destination number, message body, and thes3Key
received in Step 1.try...catch...finally
block, attempting to distinguish between backend errors and S3 upload errors.Running the Frontend:
Navigate to the URL provided by Vite (usually
http://localhost:5173
).5. Error Handling and Logging
server.js
):try...catch
blocks around AWS SDK calls and within endpoint handlers.console.error
. For production, integrate a dedicated logging library (like Winston or Pino) to log structured data to files or a logging service (e.g., AWS CloudWatch Logs).error.message
,error.name
). Avoid leaking sensitive stack traces in production responses.App.tsx
):try...catch...finally
block inhandleSubmit
to manage the multi-step process.setStatusMessage
.axios.isAxiosError
and checkingerror.response
vserror.request
.console.error
) for debugging.