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:
frontendandbackend.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-tsand 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-namewith your actual bucket name andYOUR_AWS_REGIONwith the region you are using (must match.envand 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/.envfile 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.envfile and IAM policy.AWS_REGIONin.env) and where your Pinpoint origination number is provisioned.2.3. Configure S3 Bucket CORS
To allow the frontend (running on
localhostduring 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
AllowedOriginsfor 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/.envfile 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-urlEndpoint:filenameandcontentTypefrom the request body.s3Keyusingcryptoto avoid filename collisions. It's good practice to include a prefix likemms-uploads/.PutObjectCommandspecifying the bucket, key, and content type.getSignedUrlfrom@aws-sdk/s3-request-presignerto create a short-lived URL (expiresIn: 300seconds).presignedUrland thes3Keyto 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-mmsEndpoint:destinationPhoneNumber,messageBody, and thes3Key(obtained from the previous step) from the request body.mediaUrlin the requireds3://bucket-name/keyformat.SendMediaMessageCommandwith the destination, origination number (from.env), message body (optional), and theMediaUrlsarray containing the S3 URL.pinpointClientto send the command.MessageIdor 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 theselectedFilestate when a user chooses a file. Includes basic client-side validation for image type and size. Resets theuploadedS3Keyif a new file is chosen.handleSubmit:/api/generate-presigned-urlwith the filename and type. Stores the returnedpresignedUrlands3Key.presignedUrl. The request body is theselectedFileitself. Crucially, it sets theContent-Typeheader to match the file type – S3 relies on this./api/send-mmswith the destination number, message body, and thes3Keyreceived in Step 1.try...catch...finallyblock, 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...catchblocks 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...finallyblock inhandleSubmitto manage the multi-step process.setStatusMessage.axios.isAxiosErrorand checkingerror.responsevserror.request.console.error) for debugging.