An artist’s depiction of me tackling this side project.

Building Bin Diving: A Tale of Netlify, GPT-4o, and the Search for Quality on Amazon

Caleb Brewer
7 min readAug 16, 2024

--

Sometimes, the best way to learn a new technology is to build something slightly ridiculous. Enter Bin Diving, an application designed to sift through the bargain bin that Amazon has become, uncovering those rare gems worth your hard-earned cash. Along the way, I decided to dip my toes into the murky waters of Netlify and OpenAI’s GPT platform — because why not make things interesting?

Brief aside here — I used generative AI for as much of this project as possible…including drafting this article. That said, I edited heavily to make it (hopefully) more palatable, which is something of a recurring theme throughout this AI experiment. I left that intro completely unedited because 🤘

Why I Chose Netlify and OpenAI

Before doing fun stuff with generative AI, a boring problem must be solved: infrastructure. Where to host the thing. If you’ve read anything else I’ve written you know I like AWS, but that feels too manual, (very counter to the “AI everything” point of this project). Some other options are:

  • GitHub Pages, which has some nice templates and is free, but leaves us without a place to run server-side logic
  • Wix and Squarespace make things that look nice, but they don’t really have free tiers (to my surprise)
  • Supabase covers the database and backend logic layer but lacks the front-end

So Netlify seemed intriguing: something I hadn’t used before and offered all the right parts of the puzzle —I even ended up using the Next.js template despite previously swearing that I would always handwrite my WebPack configurations.

The AI side of the equation was a bit more straightforward: I needed a generative AI solution that could search the web and part of me wanted to use the one that had been giving me okay-ish recipes for the past couple of months. So ChatGPT it is.

Just checking in again…I completely rewrote that section, but I still started from ChatGPT’s original outline.

What Bin Diving Does

At its core, Bin Diving is the lovechild of my disdain for low-quality products and my curiosity about AI. It’s a tool that wades through the deluge of Amazon’s listings to find products that are actually worth buying.

This well-worded intro is ChatGPT’s way of setting the stage for the problem I’m looking to tackle with the chosen technologies, pictured here for your viewing pleasure:

A recording of someone Bin Diving

Building the Application

Ok, I’m going to deviate pretty heavily from what the AI told me to write and jump straight to the interesting parts, (since this isn’t a Netlify or React tutorial). To cover the basics:

  1. Users will hit the static front-end
  2. Users type something they are looking for, i.e. “running socks that won’t give me blisters”
  3. The front end will query a Netlify Function, which in turn sends a request to OpenAI’s chat completion API
  4. The front end does what you would expect with the results

So, skipping past the whole front-end part, the website was basically just this:

const recommendations = await openai.chat.completions.create({
response_format: { type: 'json_object' },
messages: [
{
role: 'user',
content: `
Respond with a JSON array of products, each containing only the following
fields: {product_name, pros[], cons[], amazon_id, source_urls[]}.

The source_urls field should contain valid links to websites which provide
input for the recommendations.

What are the three best options for ${query} that people recommend?
`
}
],
model: 'gpt-4o'
})

Breaking this down:

  1. I used the chat completion endpoint, the GPT-4o model, and specified a json_object response type
  2. As a user, I sent a message that did two things: specified the structure of the JSON to be returned and prompted for recommendations, including the end-user’s query passed from the front-end

Great, we’ve AIed a thing. Only one bummer: generative AI works by generating (fabricating) content. I tried to set some guardrails by suggesting that it shouldn’t totally make up the source_url fields, but my results were mixed. ChatGPT totally made up most of the URLs…and the Amazon ID. Aaand the other fields I experimented with like price and image_url. Not to worry, though, we can always write more code.

Resolving the Recommendations

Much of the information was reliable, including the product name, pros, and cons, which is good because those are the critical aspects of the application. I decided to forgo asking ChatGPT to share its sources and instead focus on resolving accurate Amazon IDs for the products, which Brave’s web search API seemed like a good fit for:

const response = await fetch(
`https://api.search.brave.com/res/v1/web/search?q=${
encodeURI(`amazon ${product.product_name}`)
}`,
{
headers: new Headers({
Accept: 'application/json',
'Accept-Encoding': 'gzip',
'X-Subscription-Token': BRAVE_API_KEY
})
}
)

const result = await response.json()

const productMatch = result.web.results.find((result) =>
result.url.match(/https:\/\/www.amazon.com\/[^\/]*\/dp\/[A-Z0-9]{10}/)
)

I do love writing a regular expression, (which I need to re-learn every time I write one— thanks Scriptular). This also creates a new opportunity to pull other metadata about the products we know to be accurate, (read: not made up by GPT-4o). The final version of Bin Diving runs a subsequent query against Brave’s image search to add thumbnails to the results page.

The big takeaway moment: generative AI is a good place to start. It did the heavy lifting of finding/generating the actual recommendations, and with a bit of refinement, we have a working product. Make no mistake that generative AI is best used in collaboration with human intelligence, the boring linear code that finds the real references is a critical part of this application. In professional environments I’ve found much the same results: let Copilot make a pull request and have a human review it, (there will be changes requested).

Slow Chat Completions and Netlify Timeouts

The next challenge came after a bit of user testing: some searches were resulting in loooong chat completion times. Like, longer than Netlify’s 10-second Function execution limit. A bit of digging showed I could pay to have my execution time limit increased, but that’s less fun than just writing some more code.

Enter Open AI’s new(er) Assistant API. Critically, this API has separate endpoints for asking the question and retrieving the answer, which means we can avoid a Netlify timeout by waiting a bit before polling for responses. It also allows us to define the initial prompt in the platform UI instead of in the API call:

Assistant configuration in the platform UI

Below the fold, you’ll also find controls for temperature, top P, and other options available on the Chat Completion endpoint. With the prompt and configuration sorted our first API call to create the new thread is very simple:

const thread = await openai.beta.threads.create()
await openai.beta.threads.messages.create(thread.id, {
role: 'user',
content: query
})
const run = await openai.beta.threads.runs.create(thread.id, {
assistant_id: ASSISTANT_ID
})

The thread and run IDs get shipped back to the front end for the subsequent call to “poll” for responses:

let complete = false
while (!complete) {
const run = await openai.beta.threads.runs.retrieve(threadId, runId)
if (run.status === 'completed') {
complete = true
}
}

const threadMessages = await openai.beta.threads.messages.list(threadId)
const response = threadMessages.data[0]

If we want to get really fancy we could have this Netlify Function bail after waiting for 10 seconds so the front end can retry, but I haven’t seen enough timeouts to warrant doing more effort. Okay I lied — you may experience a timeout on both of those API calls, and I suppose that’s why it’s behind the beta SDK component.

This all ended up being way more effort than expected, (because AI was supposed to do it all), but also way less effort than doing it with no AI assistance at all. That also goes for writing this article so, without further ado, I’ll turn it over to ChatGPT to take us out…

Looking Ahead

So, what’s next for Bin Diving? If anyone actually uses it to buy something, I might just earn the privilege of accessing Amazon’s Product Advertising API. This would let me ditch the Brave API and get more reliable results straight from the source. But for now, I’m content with what it is — a fun side project that taught me a ton.

Conclusion

In the end, building Bin Diving was a journey filled with learning, mild frustration, and the occasional facepalm. If you’re curious to see it in action, head over to bindiving.com and give it a whirl. And if you do find something worth buying, I’d love to hear about it — just don’t blame me if the link doesn’t work.

Miscellaneous AI Limitations

I reeeally wanted some “bad product” images to serve as placeholders on Bin Diving but the results weren’t up to spec. Prompts are below the images, enjoy:

“A small sedan with cardboard boxes instead of wheels”
“A pair of normal looking gloves that only have three fingers on them. The gloves don’t have four fingers”
“A pet rock that plugs into USB sitting on an empty desk”

Ok, Midjourney nailed that one.

“Someone holding a tiny toilet plunger with perfect hands”

This infuriated me to no end. Why could I not generate gloves for this person?!

--

--