Node and TypeScript
Nexyron runs inside your Node process as a native module, with TypeScript types included.
Opening a database
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().
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
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
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:
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.
// 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:
process.on('SIGTERM', async () => {
await db.close();
process.exit(0);
});