We did it! We finished the experiment from a few weeks ago and The Girl can stop making jokes that I stole her present π
Here's the plan:
- Buy cute IoT gimmick for Valentine's Day β
- Reverse engineer its API β
- Build serverless Lambda that sends a picture β π
- Move secrets to secrets manager βοΈ
- Read photos from S3 βοΈ
- Send base64 encoded photos βοΈ
- Run daily βοΈ
The Girl was getting upset with me [name|Friend]. Waiting for those last 3 steps since February while I focused on Serverless Handbook.
She waits no more! It's live
You can see the full code on GitHub. Here's what we built:
Serverless Handbook for Frontend Engineers βΒ free chapter
Dive modern backend. Understand any backend.
Serverless Handbook taught me high-leveled topics. I don't like recipe courses and these chapters helped me to feel like I'm not a total noob anymore.
The hand-drawn diagrams and high-leveled descriptions gave me the feeling that I don't have any critical "knowledge gaps" anymore.
~ Marek C, engineer
Start with a free chapter and email crash course β€οΈ
Secrets in secrets manager
Never deploy code with secret API keys in the files. You will get hacked and your data will get leaked.
And when we're talking anonymous strangers from the internet can send dick pics to your girl ... nu-uh. All identifying info and API tokens went into the AWS Secrets Manager.
Accessed in code like this:
// src/secrets.ts
import { SecretsManager } from "aws-sdk";
const ssm = new SecretsManager({
region: "us-east-1", // make sure this matches your region
});
export async function getBearerToken() {
const secret = await ssm
.getSecretValue({ SecretId: "loveboxBearerToken" })
.promise();
const { BEARER_TOKEN } = JSON.parse(secret?.SecretString || "");
return BEARER_TOKEN;
}
Grab the SecretsManager
, instantiate with our region, write helper function to grab the API token. It talks to secrets secrets manager, retrieves the loveboxBearerToken
secret, parses the JSON response and returns what we need.
I added my other secrets later.
And we remembered to give our Lambda permissions to read secrets:
# serverless.yml
provider:
# ...
iamRoleStatements:
- Effect: "Allow"
Action:
- "secretsmanager:GetSecretValue"
Resource: "arn:aws:secretsmanager:${self:provider.region}:*"
Borrowed all this from the Serverless Handbook chapter on secrets. No point in memorizing what you can look up π
Using secrets for the GraphQLClient
We need the bearer token to authorize our GraphQL client with the API. Means we have to instantiate with an async function.
But talking to SecretsManager is an API call itself and that could make our Lambda slow. The answer is memoization.
let graphQLClient: null | GraphQLClient = null;
async function createGraphQLClient() {
if (graphQLClient === null) {
const token = await getBearerToken();
graphQLClient = new GraphQLClient(
"https://app-api.loveboxlove.com/v1/graphql",
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
}
return graphQLClient;
}
We create a global variable graphQLClient
. When it's defined createGraphQLClient
returns the client, when it isn't it first instantiates our client after reading the token from secrets.
This lets us skip the secrets call when our Lambda gets reused βοΈ
Read photos from S3
Reading photos from S3 happens in two steps:
- Get list of photos in bucket
- Read random photo
List photos
We start by giving our lambda permissions to read S3.
# serverless.yml
provider:
# ...
iamRoleStatements:
# ...
- Effect: "Allow"
Action:
- "s3:listObjects"
Resource: "arn:aws:s3:::lovebox-stash/*"
Yes, the S3 bucket is called lovebox-stash
.
Then we create a helper function that grabs a list of objects from S3. This part was uneventful, but we had to make sure it works.
// src/pictures.ts
import { S3 } from "aws-sdk";
const s3 = new S3({
apiVersion: "2006-03-01",
});
async function listPictures() {
const list = await s3
.listObjects({
Bucket: "lovebox-stash",
})
.promise();
return list.Contents;
}
Instantiate an S3 client, keep it shared between Lambda calls, make a request to S3 to list every object in the bucket.
Read random photo
Grabbing a random photo is a matter of maths and fetching an S3 object. Like this:
// src/pictures.ts
export async function getPicture() {
const pictures = await listPictures();
if (pictures) {
const randomPic = pictures[Math.floor(Math.random() * pictures.length)];
if (randomPic.Key) {
const imageData = await s3
.getObject({
Bucket: "lovebox-stash",
Key: randomPic.Key,
})
.promise();
return imageData;
}
}
return null;
}
Grab list of photos, pick at random, read data. Ifs help with error handling.
Send base64 encoded photos
The final sendPicture
function looks like this:
async function sendPicture(picture: S3.GetObjectOutput) {
const base64 = `data:${picture.ContentType};base64,${picture.Body?.toString(
"base64"
)}`;
const client = await createGraphQLClient();
const { recipient, deviceId } = await getRecipient();
const res = await client.request(
// query,
{
base64,
recipient,
contentType: ["Image"],
options: {
framesBase64: null,
deviceId,
},
}
);
console.log(res);
}
Get random photo, encode in base64
, add the mime type. Create authorized GraphQL client, get recipient secrets, send as variables to the GraphQL query.
And you get a happy Swiz
Run daily
This part's easy π add cron config to serverless.yml
# serverless.yml
functions:
sendNote:
handler: dist/lovebox.sendNote
events:
- schedule: rate(1 day)
Run daily, at a time of your choosing.
Cheers,
~Swizec
PS: the Serverless Handbook paperback is now $36 on Amazon, not sure why but check it out if you're curious
Continue reading about Send daily random photos from an S3 bucket using AWS Lambda β CodeWithSwiz 25
Semantically similar articles hand-picked by GPT-4
- Reverse engineer a GraphQL API to automate love notes β CodeWithSwiz 24
- Serverless file upload to S3 with NextJS and AWS Lambda β CodeWithSwiz 27
- Can you automate love?
- What happens when you push AWS credentials to GitHub
- How serverless beats servers
Want to dive into serverless? Not sure where to begin?
Serverless Handbook was designed for people like you getting into backend programming.
360 pages, 19 chapters, 6 full projects, hand-drawn diagrams, beautiful chapter art, best-looking cover in tech. βοΈ
Learn how to choose the right database, write cloud functions, think about scalability, gain the architecture mindsets for robust systems, and more.
Leave your email to start with a free chapter and email crash course π
Serverless Handbook for Frontend Engineers βΒ free chapter
Dive modern backend. Understand any backend.
Serverless Handbook taught me high-leveled topics. I don't like recipe courses and these chapters helped me to feel like I'm not a total noob anymore.
The hand-drawn diagrams and high-leveled descriptions gave me the feeling that I don't have any critical "knowledge gaps" anymore.
~ Marek C, engineer
Start with a free chapter and email crash course β€οΈ
Have a burning question that you think I can answer? Hit me up on twitter and I'll do my best.
Who am I and who do I help? I'm Swizec Teller and I turn coders into engineers with "Raw and honest from the heart!" writing. No bullshit. Real insights into the career and skills of a modern software engineer.
Want to become a true senior engineer? Take ownership, have autonomy, and be a force multiplier on your team. The Senior Engineer Mindset ebook can help π swizec.com/senior-mindset. These are the shifts in mindset that unlocked my career.
Curious about Serverless and the modern backend? Check out Serverless Handbook, for frontend engineers π ServerlessHandbook.dev
Want to Stop copy pasting D3 examples and create data visualizations of your own? Learn how to build scalable dataviz React components your whole team can understand with React for Data Visualization
Want to get my best emails on JavaScript, React, Serverless, Fullstack Web, or Indie Hacking? Check out swizec.com/collections
Did someone amazing share this letter with you? Wonderful! You can sign up for my weekly letters for software engineers on their path to greatness, here: swizec.com/blog
Want to brush up on your modern JavaScript syntax? Check out my interactive cheatsheet: es6cheatsheet.com
By the way, just in case no one has told you it yet today: I love and appreciate you for who you are β€οΈ