Skip to content
neode 1.0 alpha

Define your graph in TypeScript. neode writes the Cypher.

A TypeScript object-graph mapper for Neo4j, Aura and Memgraph, with an embedded Relate engine for running in-process. Models in, one Cypher statement per read out. The cell below replays a verified Neo4j run of the quickstart recipe.

Open the playground
ACTED_INACTED_INACTED_INDIRECTEDACTED_INACTED_INDIRECTEDACTED_INACTED_INDIRECTED Ray Liotta Martin Scorsese Goodfellas Casino Robert De Niro Joe Pesci The Irishman
quickstart.ts runs neode@next · playback of a verified Neo4j run Open in playground
import { Direction, NodeEntity, OneToMany, Property, Uuid, fromEnv } from 'neode'

@NodeEntity()
export class Person {
  @Uuid({ primary: true })
  id: string

  @Property({ type: 'string', unique: true, required: true, min: 2, max: 50 })
  name: string

  @Property({ type: 'int' })
  age: number
}

@NodeEntity()
export class Movie {
  @Property({ type: 'string', unique: true, primary: true })
  title: string

  // direction 'in': the stored relationship is (Person)-[:ACTED_IN]->(Movie)
  @OneToMany(() => Person, 'ACTED_IN', { direction: 'in', eager: true })
  actors: Person[]
}

async function main(): Promise<void> {
  const neode = fromEnv()

  try {
    // CREATE — validated against the model before the write
    const ray = await neode.create(Person, { name: 'Ray Liotta', age: 40 })

    // READ — by primary key
    const found = await neode.find(Person, ray.id)

    // UPDATE, then relate: Ray Liotta ACTED_IN Goodfellas — the relationship
    // key is declared on the Movie model, so the relate runs from that side
    await neode.update(found, { age: 41 })
    const goodfellas = await neode.create(Movie, { title: 'Goodfellas' })
    await neode.relate(Movie, goodfellas, found, 'actors')

    // One eager read — the movie and its cast, hydrated in one statement
    const movie = await neode.find(Movie, goodfellas.title, { with: { actors: true } })
    console.log(`${movie?.title}: ${movie?.actors.length} actor(s) — ${movie?.actors[0]?.name}`)
  } finally {
    await neode.close()
  }
}

main().catch(console.error)
QUERY_EXECUTED · captured from the verified build run
// Run to see the Cypher neode generated

Same code. Bring your own engine.

Neo4j Community, Enterprise or Aura. Or Relate, embedded in-process with Neode.forRelate() and compiled to WebAssembly, which is why the playground needs no server.

Neo4j
Community · Enterprise
Aura
managed Neo4j
Relate
embedded · WASM

Decorators or a plain map. Same metamodel.

Hover a property in either style and find it on the schema card. The map API is also the migration path from 0.x.

decorators
import { NodeEntity, Property, Uuid } from 'neode'

@NodeEntity()
export class Person {
  @Uuid({ primary: true })
  id: string

  @Property({ type: 'string', unique: true, required: true, min: 2, max: 50 })
  name: string

  @Property({ type: 'int' })
  age: number
}
map-based
import { defineModel } from 'neode'

export const Person = defineModel('Person', {
  id: { type: 'uuid', primary: true },
  name: { type: 'string', unique: true, required: true, min: 2, max: 50 },
  age: 'int', // shorthand: property : type
})
:Person
id uuid · primary
name string · unique
age int
one metamodel · two ways to say it

One query per read.

Eager loading and read-model decorators compile into a single Cypher statement. No N+1, however deep the hydration.

movie.ts
@NodeEntity()
export class Movie {
  @Property({ type: 'string', unique: true, primary: true })
  title: string

  @OneToMany(() => Person, 'ACTED_IN', { direction: 'out', eager: true })
  actors: Person[]
}

const movie = await neode.find(Movie, title, { with: { actors: true } })
// one Cypher statement, fully hydrated
hand-rolled hydration · 1 + N round trips
neode · 1 statement
See the side-by-side

Honest caveats.

1.0 is an alpha
Install with @next. A plain install still gives 0.4.9.
Node.js 20+
Required.
Models are process-global
One schema per process.
exists constraints
Need Neo4j Enterprise.

Coming from 0.4.9? Map-based schemas keep working. Read the migration guide.

From install to first query in four steps.

  1. 1
    Install
    npm install neode@next
  2. 2
    Connect
    Neo4j, Aura, or Neode.forRelate(database)
  3. 3
    Define a model
    @NodeEntity() or defineModel()
  4. 4
    Create, read, update, delete
    neode.create · find · update · delete
Run it in the playground Browse the cookbook