Designed & built In development

1800Outdoor

A structured directory of outdoor resources built on a 15-table relational taxonomy, with Postgres full-text search, synonym expansion, and editorial publishing rules that prevent thin pages.

Project
1800Outdoor.com
Role
Founder & Developer
Timeline
2026
localhost:1800 — local development build
The 1800outdoor homepage: a hero headed "Find trusted outdoor resources faster" over a forest photograph, with a resource search field and a Browse by activity row of ticket-stub cards for Archery, Backpacking, Birdwatching, Camping, and Canoeing.

A directory is an information architecture problem

1800Outdoor is a curated directory of outdoor resources — brands, blogs, apps, guides, retailers, local businesses, nonprofits, and government and safety organizations — connected through categories, locations and tags.

Directories look like websites and behave like taxonomies, which is what drew me to building one.

Modeling listings as real relationships

The schema runs to 15 tables, modeling listings against categories, locations and tags as genuine relationships rather than a flat table with a type column. That’s what lets a single listing be reached down several legitimate paths — by category, by place, by tag, or through a curated “best of” page — while existing once.

Content is authored as markdown and imported into the database through a pipeline, so entries stay reviewable as files and queryable as data.

Search that speaks the visitor’s language

Postgres full-text search with synonym expansion is the detail that carries the product. People search in their own words, not the taxonomy’s — someone looking for a “rucksack” should find what’s filed under “backpack.” Expanding query terms against a synonym set is what separates a directory that answers questions from one that works only if you already know its vocabulary.

It’s the same principle as metadata and title work on an enterprise knowledge base: findability belongs to the searcher’s words.

An editorial rule, enforced in code

City pages publish once they have at least five listings.

Thin local pages built for search rankings offer visitors nothing and cost the whole site credibility. Holding pages back until they’re worth landing on keeps the directory’s quality bar in the code rather than in good intentions. The same judgement applies to the site as a whole: the platform is built, and the public domain serves a launching-soon page while the catalog fills out — so the screenshots here show the application running against its seeded catalog.

What’s built

34 pages including per-type detail routes and an admin area for listings, taxonomy, content pages, submissions and claims. Submission and claim flows let listings be proposed and owned. Dynamic OG images and favicons are generated at the edge, and JSON-LD structured data is built per page type. Local development runs against embedded PGlite, so there’s no database to provision to work on it.

Inside the project

2
localhost:1800 — local development build
The Oregon hub listing resources as ticket-stub cards, each showing a category and region label, a "Best for" line, descriptive copy, tags such as Official Source and Family Friendly, and a trust score, status, and scope footer.
Every listing carries a trust score, a verification status, and a scope — regional or statewide — so the directory says how much confidence to place in each entry rather than presenting all results as equivalent.
localhost:1800 — local development build
The Outdoor categories index showing the Activities section as a grid of cards for Archery, Backpacking, Birdwatching, Camping, Canoeing, Cross-Country Skiing, Fishing, Geocaching, Hiking, Hunting, Kayaking, and Mountain Biking, each with a one-line scope description.
The category index exposes the taxonomy directly. Each activity states its scope in a line, so the browse path tells you what a category contains before you commit a click.

Questions about this work?

Happy to go deeper on any of it — the decisions, the trade-offs, or what I'd do differently.