# For developers

> Read, validate and write .well files from code, what the schema guarantees, and which parts of the spec matter most when you build on the format.

A `.well` file is a JSON object with a fixed set of top-level blocks. You can read it with any JSON parser and validate it with the published [JSON Schema](/format/reference/json-schema) in any language. In TypeScript or JavaScript, [`@welldot/core`](/core) gives you types, validation that matches the spec, and the v1 upgrade.

## Read and validate in TypeScript

::code-group

```bash [pnpm]
pnpm add @welldot/core
```

```bash [npm]
npm install @welldot/core
```

```bash [yarn]
yarn add @welldot/core
```

::

```ts [read.ts]
import { readFile } from 'node:fs/promises';
import { parseWell, type Well } from '@welldot/core';

const text = await readFile('PT-01.well', 'utf8');

// Throws a ZodError (with a path for each problem) when the file is invalid.
// A version 1 file is upgraded to v2 on the way in.
const well: Well = parseWell(text);

console.log(well.name, well.bore_hole.length, well.lithology.length);
```

[Parsing and validating](/core/guides/parsing-and-validating) covers the error shape, `safeParse` and legacy files. [Serializing and sharing](/core/guides/serializing-and-sharing) covers writing a file back.

## Validate in other languages

The JSON Schema (draft 2020-12) is published at a stable URL:

```text
https://welldot.org/schema/v2/well.schema.json
```

Any JSON Schema validator can use it, for example `ajv` in JavaScript, `jsonschema` in Python or `santhosh-tekuri/jsonschema` in Go. The schema is generated from the Zod validators of `@welldot/core`, so the two accept the same documents. A few rules can only be checked in code; see [Validating](/format/guides/validating).

## What the shape guarantees

- **Required fields.** `version`, `well_type` ([core identity](/format/spec/v2/format-reference#core-identity)) and the construction and geology arrays (`bore_hole`, `well_case`, `reduction`, `well_screen`, `surface_case`, `hole_fill`, `lithology`, `fractures`, `caves`, possibly empty) are required. Everything else is optional. See the [Well object](/format/reference/objects/well). Note that `parseWell` and the generated JSON Schema currently accept a file without `well_type`; write it anyway.
- **Fixed units.** Every number has one canonical unit set by the spec, never by the file ([Units](/format/spec/v2/format-reference#units)). Convert on display, not in storage.
- **Absent means unknown.** Optional fields without a value are omitted, never `null` ([Absent values](/format/spec/v2/format-reference#absent-values-since-v24)).
- **Unknown members survive.** Readers must preserve fields and blocks they do not recognize, prefixed with `x-` or not ([Extensibility](/format/spec/v2/format-reference#extensibility)). Do not strip them on save.
- **Open vocabularies.** Most string fields recommend values but accept any string. Show unknown values as they are ([Recommended vocabularies](/format/spec/v2/format-reference#recommended-vocabularies)).
- **String ids, local references.** Records point to each other by string `id` within the same file ([Cross-reference rules](/format/spec/v2/format-reference#cross-reference-and-uniqueness-rules)). A dangling reference is a warning, not an error.

::note
`parseWell` in `@welldot/core` currently accepts a file without `version`. The spec says such a file SHOULD be rejected ([parser version handling](/format/spec/v2/format-reference#parser-version-handling)), so always write it.
::

## Parts of the spec to read first

| If you are building             | Read                                                                                                                                                                                                                                                 |
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| A reader or viewer              | [Top-level structure](/format/spec/v2/format-reference#top-level-structure), [Versioning](/format/spec/v2/format-reference#versioning), [Units](/format/spec/v2/format-reference#units)                                                              |
| An editor                       | the above, plus [Block kinds](/format/spec/v2/format-reference#block-kinds-since-v23), [Datetime conventions](/format/spec/v2/format-reference#datetime-conventions), [Duplicate ids](/format/spec/v2/format-reference#duplicate-ids-within-a-scope) |
| A database or registry          | [Cross-reference rules](/format/spec/v2/format-reference#cross-reference-and-uniqueness-rules), [`well_id`](/format/spec/v2/format-reference#well_id), [Interoperability](/format/spec/v2/interoperability)                                          |
| A converter from another format | [Units](/format/spec/v2/format-reference#units), [Precision](/format/spec/v2/format-reference#precision-and-uncertainty), [Changes from v1](/format/spec/v2/overview#changes-from-v1)                                                                |

[Building on .well](/format/guides/building-on-well) collects the decisions that matter for editors, databases and pipelines.

## Draw and analyze

- [`@welldot/render`](/render) draws a profile into an SVG element with D3.
- [`@welldot/utils`](/utils) computes volumes, drawdown, permit status, production totals and water quality indicators.

```ts
import { parseWell } from '@welldot/core';
import { calculateHoleFillVolume, getLatestStaticLevel } from '@welldot/utils';

const well = parseWell(text);
calculateHoleFillVolume('gravel_pack', well); // m³ of gravel pack
getLatestStaticLevel(well); // m below ground, negative if flowing
```

## Test data

The 19 [example wells](/format/reference/examples) are complete, valid files covering crystalline, karst, sedimentary, flowing, hand-dug and monitoring wells, plus incomplete and inconsistent records. They ship with `@welldot/core` as `@welldot/core/examples/<file>` and are served at `https://welldot.org/examples/<file>`.

## Working with an AI assistant

Point your assistant at this site's [MCP server](/ai/mcp) to let it search these docs and validate the `.well` files it writes, or give it [`/llms.txt`](/ai/llms).
