Neo4j property constraints are the database’s way of enforcing rules about your data, and the most common one you’ll run into is ensuring a property always exists on a node or relationship.

Let’s see it in action. Imagine we have a User node, and we want to make sure every User always has an email property.

// This will create a User node with an email
CREATE (:User {name: 'Alice', email: 'alice@example.com'});

// This will FAIL because the email property is missing
CREATE (:User {name: 'Bob'});

The error you’ll get when trying to create Bob is:

Neo.ClientError.Schema.ConstraintValidationFailed: Node(123) with labels ['User'] must have a property 'email'

This constraint is a powerful tool. It doesn’t just validate data on insert; it also prevents updates that would remove the required property.

// Assume Alice exists with an email
MATCH (u:User {name: 'Alice'})
SET u.name = 'Alice Wonderland'; // This is OK, email is still present

// This will FAIL because it tries to remove the required email property
MATCH (u:User {name: 'Alice'})
REMOVE u.email;

The error here will be similar:

Neo.ClientError.Schema.ConstraintValidationFailed: Node(456) with labels ['User'] must have a property 'email'

So, how do you actually create this constraint? You use a CREATE CONSTRAINT statement. For our User.email example, it looks like this:

CREATE CONSTRAINT ON (u:User) ASSERT u.email IS NOT NULL;

This command tells Neo4j to enforce that any node labeled User must have a property named email, and its value cannot be null. Neo4j will then check all existing User nodes. If any User nodes are missing the email property, the constraint creation will fail, and Neo4j will report which nodes violate the rule. This is crucial because it stops you from introducing invalid data into an existing dataset.

The IS NOT NULL part is key. It’s the specific Neo4j syntax for enforcing the presence of a property. You can also combine this with uniqueness constraints. For example, to ensure every User has a unique email and that email is always present:

CREATE CONSTRAINT ON (u:User) ASSERT u.email IS UNIQUE;

When you create ASSERT u.email IS UNIQUE, Neo4j implicitly enforces IS NOT NULL. You don’t need to specify both. If a property must be unique, it logically cannot be null for multiple nodes (as null is considered a single value). Neo4j’s internal handling of unique constraints makes them inherently non-nullable.

To see all the constraints on your database, you can query the Neo4j schema information:

SHOW CONSTRAINTS;

This will give you output like:

id: 0, name: constraint_0, type: node_property_existence, labels: [User], properties: [email], errorType: NOT_NULL
id: 1, name: constraint_1, type: node_property_uniqueness, labels: [User], properties: [email], errorType: UNIQUE

If you need to remove a constraint, you first get its internal ID and then use DROP CONSTRAINT:

DROP CONSTRAINT constraint_1;

The most surprising thing about these constraints is how they interact with bulk imports. If you’re importing data using LOAD CSV and you have a CREATE or MERGE clause that doesn’t include the required property for a node, Neo4j will stop the import dead in its tracks with a ConstraintValidationFailed error. This means you can’t accidentally import malformed data into a system that relies on these schema guarantees. You must ensure your import script explicitly sets the property, even if it’s an empty string or a default value, to satisfy the IS NOT NULL requirement.

The next thing you’ll likely want to enforce is uniqueness, ensuring no two users have the same email address.

Want structured learning?

Take the full Neo4j course →