# First use

> Read a .well file with @welldot/core, handle validation errors, inspect the typed data, change it and save it again.

This page reads a well, looks at a few fields, changes one and writes the file back. It runs in Node 18 or later; in a browser, replace the file system calls with a `File` or `fetch`.

## Read and validate

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

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

`parseWell` takes the JSON text (not an already parsed object), validates it against the schema and returns a typed `Well`. A file with `"version": 1` is upgraded to v2 first. It throws when:

- the text is not JSON (a `SyntaxError` from `JSON.parse`);
- the version is malformed or unsupported (an `Error` naming the value);
- the document does not match the schema (a `ZodError` listing every problem).

```ts
type Issue = { path: (string | number)[]; message: string };

try {
  parseWell(text);
} catch (error) {
  if (error instanceof Error && error.name === 'ZodError') {
    for (const issue of (error as Error & { issues: Issue[] }).issues) {
      console.log(issue.path.join('.'), issue.message);
      // well_screen.0.screen_slot Required
    }
  } else {
    console.log((error as Error).message);
  }
}
```

Checking `name` rather than `instanceof ZodError` avoids importing `zod` yourself and keeps working if your project resolves a different copy of it.

[Parsing and validating](/core/guides/parsing-and-validating) shows how to turn these into readable messages.

## Inspect the data

Every block is typed. The construction and geology arrays are always present; the other blocks are optional.

```ts
console.log(well.name, well.well_type);

for (const layer of well.lithology) {
  console.log(`${layer.from}–${layer.to} m  ${layer.description}`);
}

const screens = well.well_screen.map(s => `${s.from}–${s.to} m`);
const tests = (well.hydrodynamic_events ?? []).filter(
  e => e.type === 'constant_rate' || e.type === 'step_drawdown',
);
```

`HydrodynamicEvent` is a union discriminated by `type`, so narrowing on `type` gives you the fields of that event kind.

For derived values, such as the latest static level, gravel pack volume or permit status, use [`@welldot/utils`](/utils).

## Change and save

```ts [save.ts]
import { writeFile } from 'node:fs/promises';
import { serializeWell } from '@welldot/core';

well.well_purpose = ['production', 'monitoring'];
await writeFile('PT-01.well', serializeWell(well), 'utf8');
```

`serializeWell` writes `"version": "2.4"`, leaves out empty optional fields and keeps every member it does not know, including `x-` extensions, as the spec requires ([Serializing and sharing](/core/guides/serializing-and-sharing)).

## Show labels and convert units

Values are stored as keys and in SI units. Convert for display:

```ts
import {
  CONSTRUCTION_MATERIALS,
  getVocabLabel,
  metersToFeet,
  mmToInches,
} from '@welldot/core';

const casing = well.well_case[0];
getVocabLabel(CONSTRUCTION_MATERIALS, casing.type, 'pt'); // 'Aço carbono'
mmToInches(casing.diameter); // 8.62… for 219 mm
metersToFeet(casing.to);
```

## Next

- [Parsing and validating](/core/guides/parsing-and-validating)
- [Vocabularies and textures](/core/guides/vocabularies-and-textures)
- [API reference](/core/api)
