I Tried To Map Every Study‑Friendly Spot Near You
Last updated on

I Tried To Map Every Study‑Friendly Spot Near You

A candid, commit‑by‑commit story of building the web’s largest directory of laptop‑friendly places — the indexing scares, the enrichment breakthroughs, the routing rewrites, and the day 44,000 queries turned into 2.

I built Study Near Me to answer a simple question. Where can I go to actually get real work done?

Home was fine. Until it wasn’t. Four years into remote work, my office felt smaller every week. I wanted a change of scene without a monthly bill. I wanted Wi‑Fi that did not stall, outlets I did not have to fight for, and a vibe that made me want to stay. That question pulled the thread that became this project.

Act 1: The map that wasn’t (May 13–18)

I started in May with a clean Astro project and a bold guess. It’s just a directory. A few pages, some data, a fast build. Within a week, 69,000 businesses were in the database and half a million reviews were in the wings. The site was alive. Also buckling.

Broken image links. Unstable URLs. Builds that felt like wet concrete. The kind of problems that don’t show up in a local demo. The kind that make you pick what you actually stand for.

I locked the homepage to static. Split the messy jobs into real scripts. Pushed ingestion to Python so Node could focus on being a website. Then I started cutting noise. Drive‑thru only? Out. Quality over count. A suitability filter took shape.

Act 2: URLs, canonicals, and cleaning up the past (June 16–19)

Early on I used Google place_id in business URLs. The first cracks were casing and canonicals. Netlify did not play well with uppercase path segments. Google began flagging canonical inconsistencies. I still needed to query Mongo by place_id, but the URLs had to be stable, lowercase, and ours.

So I pivoted business URLs to Mongo ObjectIds. At the same time I removed about 40,000 legacy businesses that had used place_id and were no longer on the site. I wanted it to be unambiguous to both people and Google that those old URLs were gone.

I shipped middleware to make that clear. If a place_id maps to a certified business, respond with a 301 to /businesses/{_id}/. If the business was denied or removed, respond with 410 Gone. Otherwise return a 404. Tests back this up.

Act 3: The invisible work of being understood (June 3–28)

Search engines are picky. They should be. The sitemap leaked uppercase. Trailing slashes were inconsistent. Canonicals were not as canonical as they should be. None of this is glamorous. All of it matters.

I normalized URLs. Standardized the slashes. Consolidated images behind one BusinessImage component so thumbnails did not ruin a layout at the worst moment. I learned to respect boring work. It is what keeps everything else standing.

Act 4: Can it travel? (July 24–26)

The US directory felt strong. I wanted to see if the architecture held up in Canada. I added province and city routes, a country picker, postal‑code behavior that felt native, and careful guards so nothing thin got built. The patterns held. That was the point. One codebase. Two countries. Clear rules.

There was a catch. In the US, ZIP code geocoding had a free path. In Canada, there wasn’t. I didn’t want a paid dependency just to keep the basics working. So I loaded the full Canadian postal‑code dataset into my own database and queried that instead. If you want to run a directory on a budget, you have to get creative. Sometimes “free” means doing the work yourself.

Act 5: Teach the directory taste (Aug 1–3)

A list is not enough. People want to know if a place works for what they need. I built feature tags that say something clear. Fast Wi‑Fi. Lots of outlets. Laptop policy. Seating. Noise. Outdoors. Then I stored the reasoning. If a place is tagged plenty of seating, the dashboard shows why. I tuned keywords to avoid false matches. I capped token costs. I made bulk changes safe with dry runs.

The curation dashboard became an honest tool. It highlighted the right reviews. It tracked decisions with receipts. It made the directory feel like a person had thought about it. Because one had.

Under the hood there’s a review processing pipeline. It surfaces candidates that are worth enriching and routes them through bulk actions with dry runs. The dashboard makes it fast to apply tags, add reasoning, and measure the impact.

Reviews turned out to be a small goldmine. They’re written by people about what they actually care about. Mining reviews surfaced what users want: quiet places, meeting‑friendly spots, patios, fast Wi‑Fi that really is fast. It also hints at gaps a product could fill. A future version of this project might monetize by solving a need that keeps showing up in the text.

Act 6: The day 44,000 became 2 (Aug 8)

I still remember the build that broke my patience. The logs said 44,000 plus queries. I took a day, pulled apart the query patterns, merged redundant lookups, and moved the heavy work into aggregations. I removed early connection closes that were causing a slow bleed of errors. The next build did the same work in 2 queries. The site felt lighter in my hands. Speed is a feature.

Act 7: Pages that don’t waste your time (Aug 5–9)

I rewrote the near‑me landers so they said something once, linked to Explore other topics, and offered Browse by location. I added best‑in‑city pages for topics like fast Wi‑Fi or laptop‑friendly cafes. With one rule. Only build them where results exist. No dead ends.

I added a small Open now indicator powered by hours data. I lifted the image experience with photos behind a recaptcha gate. I fixed CLS spikes by reserving space with skeletons and by giving the badge area a fixed home. No more hop when the page warms up.

Running lean and paying attention to scale is important. I once got a $49 Netlify bill because the open‑now calculation ran about ten times per page load during a traffic burst. Fourteen thousand function calls later, I had my reminder. I deduped the calls, limited updates, and added a human gate where it mattered.

reCAPTCHA helps on two fronts. It stops non‑human access to Google’s Places API, which is costly, and it keeps Netlify functions from firing when no person is actually browsing. Google offers it for free. It paid for itself on day one.

A few rules I came back to

  • URLs are a promise. Own the IDs and respect canonicals.
  • Do not link to pages that do not exist.
  • Images are gravity. Load them thoughtfully or they will move your world.
  • Guardrails are product features. Sometimes the best UX is the thing you do not render.
  • Measure first. Fix the right thing once.

What I would do sooner next time

  • Pick durable IDs on day one.
  • Ship sitemap and canonical discipline with the first routes.
  • Store the why for decisions instead of chasing it later.
  • Invest earlier in the curation tool I actually want to use.

What this helps with in real life

This started because I needed better places to work. It turns out a lot of people do. Sometimes you just need quick Wi‑Fi to send a file. Sometimes you want a quiet corner and a long coffee. Sometimes you want a patio seat and a laptop that does not feel out of place. This directory is for those moments.

Where it landed

The directory is 10× more useful than when it started. A few reasons why:

  • Enrichment: Reviews feed a pipeline that adds clear, explainable feature tags with stored reasoning.
  • Landing pages: Near‑me pages read cleanly and link to real choices without repeating themselves.
  • Topical pages: Best‑in‑city topics exist only where they should and point to strong results.
  • Business snippets: Short, human summaries pulled from what people actually say in reviews.
  • Badges: Certification and feature badges make confidence visible at a glance.
  • Filters: Practical filters (Wi‑Fi, outlets, laptop policy, quiet, seating, outdoor) surface what matters.

Search results are consistently great now. You can land on a city or topic page and get usable answers in seconds.

Looking ahead (lightly)

Keep deepening enrichment where it moves the needle. Add topics where reviews show real demand. Expand filters only when they help people decide faster. The goal is the same as day one: fewer guesses, better picks, faster.

If you have a favorite spot that belongs on the map, tell me. I will go there and check. This project looks simple on the surface. Underneath it is a set of choices about trust, speed, and respect for people’s time. That is the work I want to keep doing.