---
title: Node and TypeScript
url: https://nexyron.tech/docs/build/bindings/node
description: Using Nexyron from Node and TypeScript, with the identifier rule that matters most in JavaScript.
---

# Node and TypeScript

Nexyron runs inside your Node process as a native module, with TypeScript types included.

## Opening a database

```ts
import { open } from '@nexyron/node';

const db = await open('./business.nexyron');

const rows = await db.query('MATCH (c:Customer) RETURN count(c) AS customers');
console.log(rows[0].customers);

await db.close();
```

## Read this before anything else: identifiers

**Always return identifiers as strings with `element_id()`.**

```ts
const rows = await db.query(`
  MATCH (c:Customer)
  RETURN element_id(c) AS id, c.name AS name
`);
```

JavaScript numbers cannot represent large integers exactly. An identifier that arrives as a number can be silently rounded, after which it matches nothing. The failure is quiet: no error, just a lookup that returns empty, usually noticed long after the code that caused it.

The same applies to any large integer in your own data, such as external system keys. If a value can exceed roughly nine quadrillion, carry it as a string.

## Parameters

```ts
const rows = await db.query(
  `MATCH (c:Customer)-[:BELONGS_TO]->(s:Site {name: $site})
   WHERE c.joinedAt >= $since
   RETURN element_id(c) AS id, c.name AS name`,
  { site: 'Bergen', since: '2026-01-01' },
);
```

## Writing and transactions

```ts
await db.transaction(async (tx) => {
  await tx.query('CREATE (s:Site {name: $name})', { name: 'Trondheim' });
  await tx.query(
    `MATCH (c:Customer {name: $customer}), (s:Site {name: $site})
     CREATE (c)-[:BELONGS_TO]->(s)`,
    { customer: 'Nora Ellingsen', site: 'Trondheim' },
  );
});
```

Throwing inside the callback rolls the whole transaction back.

## Typing results

Queries return plain objects, so a returned shape can be typed directly:

```ts
type SiteRow = { site: string; customers: number };

const rows = await db.query<SiteRow>(`
  MATCH (s:Site)<-[:BELONGS_TO]-(c:Customer)
  RETURN s.name AS site, count(c) AS customers
  ORDER BY customers DESC
`);
```

The type describes what you returned, so keep `RETURN` explicit rather than returning whole subjects. Your types then stay accurate as the model grows.

## In a server

Open the database once at startup and share it. Opening per request is slow and unnecessary.

```ts
// db.ts
import { open } from '@nexyron/node';
export const db = await open(process.env.NEXYRON_PATH ?? './business.nexyron');
```

Close it on shutdown so the last writes are flushed:

```ts
process.on('SIGTERM', async () => {
  await db.close();
  process.exit(0);
});
```
