# neomodel: a class based object graph mapper over the Neo4j Python driver

> neomodel wraps the Neo4j Python driver in a familiar model layer, with class based definitions, a query API, schema enforcement through cardinality restrictions, transactions, hooks and a Django integration, and since 6.0 a dataclass based configuration system and native support for vector and full-text semantic indexes. It can also use a Rust extension to the driver for faster transport.

**neo4j-contrib/neomodel** — An Object Graph Mapper (OGM) for the Neo4j graph database.

- Repository: https://github.com/neo4j-contrib/neomodel
- Website: https://neomodel.readthedocs.io
- Stars: 1,090 · Forks: 241
- Language: Python
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/neo4j-contrib-neomodel

## What neomodel is: a model layer over the official driver

neomodel is described as an Object Graph Mapper for the Neo4j graph database, built on the neo4j_driver, and the package metadata calls it an object mapper for the Neo4j graph database. The distinction from a plain driver wrapper matters, because the driver already gives you a way to run Cypher and get results back. What neomodel adds is a model layer in the Django or SQLAlchemy mould. The feature list names familiar class based model definitions with proper inheritance, a powerful query API, schema enforcement through cardinality restrictions, full transaction support, thread safety, and pre and post save and delete hooks, plus Django integration via the separate django_neomodel project. The dependency is a single pinned driver, neo4j at about 6.1.0, plus typing-extensions, and the licence is MIT.

## The 6.0 rewrite: semantic versioning, a dataclass config and native indexes

Version 6.0.0 is where the project changed shape, and the release notes are specific. From now on neomodel uses SemVer, major.minor.patch, for versioning. The version introduces a modern configuration system, using a dataclass with typing, runtime and update validation rules, and environment variables support. And semantic indexes, vector and full-text, are now natively supported, so you no longer have to use a custom Cypher query for them, with credit given to a named contributor. The configuration change is the one that touches every user, since the old ad hoc configuration is replaced rather than extended, and a dataclass with validation rules means a misconfigured setting fails early instead of at connection time.

## The breaking change that catches people out: list depth in results

The one breaking change called out in detail is a good example of why an OGM is not a transparent layer. List object resolution from Cypher was creating two depth lists for no apparent reason, and this release fixes it, so that for example RETURN collect(node) returns the nodes directly as a list in the result. In other words, you can extract the list at results[0][0] instead of results[0][0][0]. That is an extra unwrap that every existing consumer of that shape has to remove, and it is a reminder of the general property: an object mapper decides what the driver returns, so its convenience features are not free of cost. The README notes there are more breaking changes in the documentation, so a migration from 5.x is not a single edit.

## Version support matrix, and the split between 6.x, 5.x and 4.x

The Requirements section is laid out by release line, which is unusual and useful. For neomodel 6.x, Python 3.10 and later, and Neo4j 2025.x.x, 5.x or 4.4 LTS, with Neo4j Enterprise, Community and Aura all supported. For 5.x, Python 3.8 and later with Neo4j 5.x or 4.4 LTS. For 4.x, Python 3.7 to 3.10 with Neo4j 4.x, including 4.4 LTS from neomodel version 4.0.10. The Aura entry in the 6.x line is the one to note. Aura is the hosted version of Neo4j, so supporting it means the mapper works against a managed service with its own authentication and connection model, and the test suite has specific environment variables for it, AURA_TEST_DB_USER, AURA_TEST_DB_PASSWORD and AURA_TEST_DB_HOSTNAME, with the tests in test_connection.py failing locally if you do not set them. Current releases are 6.1.0 in March 2026, 6.2.0 in June and 7.0.0 in August.

## Installing, and the Rust driver extension as an option

Installation from PyPI is the recommended route with pip install neomodel, and there are two extras worth knowing. The first is rust-driver-ext, because neomodel can use the Rust extension to the Neo4j driver for faster transport, and you install it with pip install neomodel[rust-driver-ext]. The second is extras, which pulls the optional dependencies Shapely, pandas and numpy, and the two combine as pip install neomodel[extras, rust-driver-ext]. The optional dependencies are each also available individually, with shapely, pandas, numpy and pydantic named as their own extras, and Shapely being pinned to 2.0.0 or later and pydantic to 2.0 or later. You can also install from GitHub with pip install git+git://github.com/neo4j-contrib/neomodel.git@HEAD and the egg name neomodel-dev. The Rust extension is worth attention if you are latency sensitive, since it changes the transport rather than the mapper.

```bash
$ pip install neomodel
$ pip install neomodel[rust-driver-ext]
$ pip install neomodel[extras, rust-driver-ext]
```

## One codebase, two APIs, and how the async and sync versions are kept in step

neomodel maintains both a sync and an async API, and the way it does that is a detail worth knowing if you contribute. The project uses the unasync library to automatically transpile async code into its sync version, so when contributing you only update the async code in neomodel/async_ and then run bin/make-unasync, isort and black, with the pre-commit hooks available as an alternative. The transpiling script does two things, removing the await keywords and the Async prefixes in class names, and making specific replacements like adb to db and mark_async_test to mark_sync_test. Where a piece of code should only run in one mode, there is a utility function taken from the official Neo4j Python driver code, AsyncUtil.is_async_code, which is always True in async mode, so you can branch on it. The performance section adds the practical reason to care, that async neomodel is faster than concurrent sync neomodel and a lot faster than serial queries.

## Running the test suite, and the safeguard against wiping a database

The test suite needs a Neo4j database version 4 or higher, and the connection is given through the NEO4J_BOLT_URL environment variable in the form bolt://username:password@localhost:7687, with the check on the username and password, and the database configuration file must have dbms.security.auth_enabled set to true. You set up a virtual environment, install neomodel for development with pip install -r requirements-dev.txt, and run the suite with pytest. The safeguard is the interesting part. If you are running a Neo4j database for the first time, the test suite will set the password to test, and if the database is already populated, the test suite will abort with an error message and ask you to re-run it with the --resetdb switch, so that it does not accidentally wipe out a database if you have not restarted your Neo4j server to point at a usually named debug.db database. There is also a docker-compose route, sh ./tests-with-docker-compose.sh, which runs the suite against all supported Python interpreters and Neo4j versions.

```bash
$ export NEO4J_BOLT_URL=bolt://<username>:<password>@localhost:7687
$ pip install -r requirements-dev.txt
$ pytest
```

## Repository layout, packaging and where the project stands

The top level of the repository is a conventional Python project with a few extras. There is a neomodel/ package, a test/ directory, a doc/ directory for the Sphinx documentation, a bin/ directory holding the make-unasync script, a docker-scripts/ directory and a docker-compose.yml, plus requirements.txt and requirements-dev.txt, a pyproject.toml, a .pre-commit-config.yaml, a .sonarcloud.properties, an AUTHORS.txt, a Changelog and a LICENSE. Two files at the root are unusual and interesting: model_diagram.json and model_diagram.puml, which is a PlantUML source alongside its rendered form, presumably a diagram of the model layer itself. The package name is neomodel, the version is dynamic and read from neomodel._version.__version__, the development status is Production/Stable, and the keywords list ORM, OGM and mapper side by side, which tells you the project is deliberately positioned between the two worlds. The last push to the repository was on 2026-09-19 and it is not archived.

## Conclusion

neomodel suits a Python team that works with Neo4j and wants the ergonomics of a Django style model layer, with schemas, relations and hooks expressed as classes, without dropping to raw Cypher for ordinary work. It is the wrong choice for a service whose query patterns are analytical rather than transactional, since an object mapper is the wrong shape for aggregation, and a poor fit if you need a mapping layer that supports every Neo4j feature, because the release notes call out cases where the mapper's behaviour differs from the raw driver's. Before adopting it, read the 6.0.0 breaking changes, particularly the list object resolution change that alters the depth of returned lists, and decide whether the async path is your primary one, because the library maintains two code bases generated from one and the performance comparison says async is faster than concurrent sync.

## FAQ

### What is neomodel?

It is an Object Graph Mapper for the Neo4j graph database, built on the official neo4j Python driver. It provides class based model definitions with proper inheritance, a query API, schema enforcement through cardinality restrictions, full transaction support, thread safety, pre and post save and delete hooks, and a Django integration via django_neomodel.

### What changed in neomodel 6.0.0?

The project moved to SemVer versioning, introduced a modern configuration system using a dataclass with typing, runtime and update validation rules plus environment variables support, and added native support for vector and full-text semantic indexes. It also fixed list object resolution, so RETURN collect(node) now returns the nodes directly as a list, extractable at results[0][0] rather than results[0][0][0].

### Which Python and Neo4j versions does neomodel support?

For 6.x releases, Python 3.10 and later with Neo4j 2025.x.x, 5.x or 4.4 LTS, with Enterprise, Community and Aura supported. For 5.x, Python 3.8 and later with Neo4j 5.x or 4.4 LTS. For 4.x, Python 3.7 to 3.10 with Neo4j 4.x, including 4.4 LTS from version 4.0.10.

### How do I install neomodel and speed up the transport?

pip install neomodel from PyPI is the recommended route. neomodel can use the Rust extension to the Neo4j driver for faster transport, installed with pip install neomodel[rust-driver-ext], and the optional Shapely, pandas and numpy dependencies come with pip install neomodel[extras, rust-driver-ext].

### How is the neomodel test suite run?

You need a Neo4j database version 4 or higher, export NEO4J_BOLT_URL with the bolt://username:password@localhost:7687 form, ensure dbms.security.auth_enabled is true, then install requirements-dev.txt and run pytest. If the database is already populated the suite aborts and asks for the --resetdb switch, and sh ./tests-with-docker-compose.sh runs the suite across supported Python and Neo4j versions.

## Sources

- [License: MIT](https://github.com/neo4j-contrib/neomodel/blob/master/LICENSE)
- [neo4j-contrib/neomodel on GitHub](https://github.com/neo4j-contrib/neomodel)
- [Project website](https://neomodel.readthedocs.io)
- [README](https://github.com/neo4j-contrib/neomodel/blob/master/README.md)
- [Releases](https://github.com/neo4j-contrib/neomodel/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/neo4j-contrib-neomodel
