Donor Fieldnotes

Designing an HTML-first charity data directory

Explore a static publishing model for complete profiles, clear data definitions, useful indexes, and an honest product boundary.

Designing an HTML-first charity data directory — DonorAPI.com typographic artwork

A data-driven website does not need to assemble every page when a visitor requests it. A publishing process can read structured records ahead of time and turn them into complete HTML documents. That is the approach used for DonorAPI.com: the data informs the build, while visitors receive ordinary static pages. This guide explains the design choices behind that model, what it offers for a charity directory, and how it differs from an authenticated donor API or a live database service.

Separate data preparation from page delivery

During preparation, a build process reads the source records, checks their fields, calculates explicitly labeled summaries, and renders profile and index pages. After publication, the server delivers the resulting files. The visitor does not need the original spreadsheet or a browser script that reconstructs the directory. The distinction is between using data to create content and requiring a database to deliver that content each time. Both are data-driven approaches, but their operational requirements differ.

For background, MDN's introduction to server-side development distinguishes static file delivery from dynamically generated responses. On this site, the practical consequence is straightforward: charity descriptions, financial fields, browse lists, and pagination links are already present in the HTML. The small amount of interface scripting is not responsible for supplying the records. Disabling it does not turn the directory into an empty shell waiting for a separate data request.

Give every source record a durable identity

The first design task is to define what constitutes one record. In the supplied file, the EIN values are unique, while a few names repeat. Profile paths therefore combine a readable name with the EIN digits. This makes collisions less likely than using a name alone and gives a human reader a useful cue about the page. The original EIN remains visible on the page, so the address is not the only place where identity can be checked.

A directory should also avoid generating a new profile for every route into the same record. A cause page, state page, and size page all link to the same canonical charity address. Those are indexes, not separate versions of the organization. This keeps the publication easier to validate and makes references more consistent. It also prevents a raw count of index appearances from being mistaken for a count of unique charities, a particularly easy error when one dataset supports many different browsing views.

Preserve the source and the readable presentation

A useful rendering model has two layers: source fidelity and human readability. The readable layer formats dollar amounts, converts fraction fields to percentages, and groups related observations into clear sections. The source-fidelity layer retains all 23 original field values on the profile. A reader can therefore inspect the exact string behind a rounded display or check a missing value without needing a separate downloadable data file. This is a presentation choice, not a claim that the raw file has been independently verified.

The data dictionary connects the two layers. It identifies each field, explains its documented meaning, and records the display treatment. It also states where the metadata leaves something unspecified, such as the denominator of the leadership compensation percentage. A transformation should never quietly fill such a gap. If a later project adds an inferred measure, it should label that measure as derived, show the calculation, and keep it separate from the original source value.

Render useful indexes without a combinatorial explosion

A dataset with several classifications could produce a very large number of possible filter combinations. Not every combination deserves a page. DonorAPI.com uses broad cause, subcategory, location, size, score-band, and alphabetical indexes, plus nonempty cause-and-location combinations. These are practical routes through the actual collection. They avoid publishing thousands of empty or nearly identical pages merely because a mathematical combination can be generated.

Pagination keeps long lists manageable while retaining ordinary links between pages. Every paginated page has its own address and describes the records shown there. A useful build check verifies that the union of a directory's pages contains all expected records without accidental omissions or duplicates within that view. It should also confirm that page links connect correctly in both directions. A polished first page is not enough when the last several thousand records depend on pagination being complete and discoverable.

Keep the product boundary honest

A static collection of HTML profiles is not an authenticated JSON API. It does not create access keys, accept writes, validate donations, or synchronize a donor database. The Donor API page explains the published resource model and related integration concepts without presenting a nonexistent service as operational. A code-style panel can illustrate a record, but its label must make clear whether it is an example, a real file, or an actual endpoint available to callers.

That boundary is important for implementation planning. A team requiring live updates, private donor data, payment processing, or per-user permissions would need capabilities beyond this static publication. Those needs should be defined separately rather than implied by the site's name. The current deliverable is a research directory with complete pre-rendered content. Understanding that scope makes the site easier to deploy and prevents an attractive interface from promising work that no backend exists to perform.

Validate the whole publication, not just templates

A template can be correct while specific records still cause problems. Long organization names, unusual punctuation, missing compensation fields, negative revenue values, and repeated names all test assumptions that a small sample might miss. A robust build checks every record against its rendered profile and verifies that all original fields survive the process. It should escape source text safely, preserve identifiers as text, and avoid replacing inconvenient values just to make the design look tidier.

Site-level checks are different but equally important. Verify that internal links resolve, all images exist, each indexable page has a canonical address, and the sitemap covers the intended pages. Inspect representative layouts at narrow and wide widths, and test key routes with scripting disabled. These checks serve different purposes: data fidelity, navigation integrity, and visual usability. Passing one does not guarantee the others, so a useful quality report keeps them distinct rather than collapsing them into an unexplained “passed” badge.

Plan updates as new publications

A static site can be updated, but the update is a publishing action rather than a live query. A future build should identify its source version, validate new records, and decide how changed names or identifiers affect existing addresses. Keeping a consistent identity rule makes that work more predictable. If the source changes its definitions, the new publication should explain the change rather than silently making old and new values appear comparable.

Licensing and provenance belong in that process too. The supplied metadata labels the dataset license as unknown. That observation should remain visible in the source notes; technical access to a record is not the same thing as documented permission for every reuse. Before expanding the publication or building another distribution format, establish the appropriate rights and source conditions. A well-engineered rendering pipeline cannot substitute for clarity about what information is being published and on what basis.

Design for inspectable evidence

The strongest static data directory is not merely a collection of generated pages. It is a consistent relationship between source records, readable profiles, useful indexes, and documented transformations. Preserve identity, show the data before scripts run, distinguish examples from working services, and validate the complete output. That approach turns a large source file into a navigable publication without hiding either its structure or its limits.

Keep exploring.

All fieldnotes ↗