Building an editor or database on .well
This guide is for developers building a second editor, a well registry, a database import or a data pipeline on the format. It lists the decisions that are easy to get wrong and the spec rules that settle them.
Reading
- Accept every version you can. Read
1,2and"2.x"; upgrade v1 on read; reject malformed and unknown major versions with a clear message (parser version handling).parseWellin@welldot/coredoes all of this. - Do not assume order. Events, samples and log entries may appear in any order. Sort by instant (as absolute UTC time) and use
sequenceto break ties (ordering and ties). - Treat absent as unknown. A missing static level is not zero; a missing status means the status is unknown (absent values).
- Show unknown vocabulary values as they are. Do not map
x-jettingto "other" (recommended vocabularies).
Writing
- Round-trip everything. Preserve
x-members and any member you do not know, at every level (design principles, "Preserve unknown members"). The safest pattern is to parse into an object, change the fields you edit, and serialize the same object. - Write the current version. New files are
"2.4". Upgrading2to"2.4"is safe;serializeWellwrites it. - Omit, do not null. Remove optional fields that have no value (absent values).
- Store SI, display anything. Convert to the user's units on screen and back on input. The file never changes unit (Units).
- Store keys, not labels. Write
stainless_steel, show "Stainless steel" or "Aço inox".
Ids and references
- Ids are strings, unique per block; generate UUID v4 for new records (
idfield convention). - References resolve only within the same file (cross-array references).
- A dangling reference is a warning; never delete it silently on import or export (dangling references).
- Duplicate ids are malformed. An interactive editor asks the user to merge, rename or abort; a batch importer rejects the file. Never rename automatically (duplicate ids).
Editing history correctly
Each top-level list is one of three kinds, and the kind decides how a correction is recorded (block kinds):
| Kind | Blocks | How to correct |
|---|---|---|
| Ledger | hydrodynamic_events, production, water_samples | Append a new entry with corrects. Editing in place is allowed but not recommended after export. |
| Mutable record | history_logs, permits, operating_regime | Edit in place and update updated_at. |
| Installation | pump_installations, meters | Edit in place; a reinstalled unit is a new entry with the same serial. |
Construction and geology are edited in place. An aquifer_analysis is an interpretation: a new interpretation is a new entry, and earlier ones stay.
Derived values
Never store what can be computed: drawdown, specific capacity from readings, production volumes, permit status, condition deadlines, exceedances (design principles). Compute them when you display or query. @welldot/utils implements the spec's derivations, including the normative ones for volumes and permit deadlines, and Querying current state describes how to answer "what is the well's status now".
Databases
A .well file is a document, and the simplest durable storage is the document itself: a JSON column, a document store or files in object storage. Index what you query (identifiers, location, depth, status) in separate columns, derived from the document on write.
When you normalize into tables:
- Key a well by its
well_identries (authority plus id), not byname; the pair is unique within a file (well_id). - Key child records by the well plus their
id; ids are only unique within one block of one file. - Keep the original document, or at least its
x-members and unknown fields, so you can export a faithful file again. - Collective permits and campaign blanks are repeated in each well's file with the same id; you may merge them by id (repeated records).
Sharing and privacy
To publish a well without its owner data, permits or location, remove those parts rather than blanking them, so the result is still valid. redactWell in @welldot/core does this by section (Serializing and sharing).
Interoperability
- GroundWater ML 2 (GWML2) is the OGC standard for groundwater data; the spec maps
.wellobjects to its classes (relationship to GWML2). @contextis reserved for a JSON-LD context; it is optional and readers must accept it (JSON-LD context).- The MIME type is
application/vnd.well+jsonand the extension.well(overview).
Testing your implementation
Run your reader and writer against all 19 example wells: every one must parse, and writing it back must keep every member. Include the inconsistent data and incomplete record examples, which are valid on purpose.