Neo4j

Port 7687 Graph database — Cypher query language. Bolt only (7474 not exposed).

Connection

NEO4J_URI=bolt://db.bizfylabs.com:7687
NEO4J_USER=myapp_user
NEO4J_PASSWORD=PASSWORD

Setup

npm install neo4j-driver

import neo4j from 'neo4j-driver';
const driver = neo4j.driver(
  process.env.NEO4J_URI,
  neo4j.auth.basic(process.env.NEO4J_USER, process.env.NEO4J_PASSWORD),
);
const session = driver.session();
// always close: await session.close()

Label Naming

Prefix labels per app: MyApp_User, MyApp_Product, MyApp_Order

Node CRUD

Create

// Single node
await session.run(
  'CREATE (u:MyApp_User {id: $id, email: $email, name: $name, createdAt: datetime()}) RETURN u',
  { id: 'u1', email: 'a@b.com', name: 'Alice' }
);

// MERGE (idempotent — create if not exists)
await session.run(
  'MERGE (u:MyApp_User {id: $id}) ON CREATE SET u.email = $email, u.name = $name RETURN u',
  { id: 'u1', email: 'a@b.com', name: 'Alice' }
);

Read

// By property
const result = await session.run(
  'MATCH (u:MyApp_User {id: $id}) RETURN u',
  { id: 'u1' }
);
const user = result.records[0].get('u').properties;

// List with filter + limit
await session.run(
  'MATCH (u:MyApp_User) WHERE u.status = $status RETURN u ORDER BY u.createdAt DESC LIMIT 20',
  { status: 'active' }
);

// Count
await session.run('MATCH (u:MyApp_User) RETURN count(u) AS total');

Update

await session.run(
  'MATCH (u:MyApp_User {id: $id}) SET u.name = $name, u.updatedAt = datetime() RETURN u',
  { id: 'u1', name: 'Alice Updated' }
);

Delete

// Delete node + all relationships
await session.run(
  'MATCH (u:MyApp_User {id: $id}) DETACH DELETE u',
  { id: 'u1' }
);

Relationship CRUD

Create

await session.run(
  `MATCH (a:MyApp_User {id: $from}), (b:MyApp_User {id: $to})
   CREATE (a)-[:FOLLOWS {since: date()}]->(b)`,
  { from: 'u1', to: 'u2' }
);

// Product purchase
await session.run(
  `MATCH (u:MyApp_User {id: $uid}), (p:MyApp_Product {id: $pid})
   CREATE (u)-[:PURCHASED {at: datetime(), qty: $qty}]->(p)`,
  { uid: 'u1', pid: 'p1', qty: 2 }
);

Read

// Friends
await session.run(
  'MATCH (u:MyApp_User {id: $id})-[:FOLLOWS]->(f) RETURN f',
  { id: 'u1' }
);

// Friends of friends
await session.run(
  `MATCH (u:MyApp_User {id: $id})-[:FOLLOWS]->(f)-[:FOLLOWS]->(fof)
   WHERE fof <> u RETURN DISTINCT fof LIMIT 20`,
  { id: 'u1' }
);

// Shortest path
await session.run(
  `MATCH path = shortestPath(
     (a:MyApp_User {id: $from})-[*]-(b:MyApp_User {id: $to})
   ) RETURN path`,
  { from: 'u1', to: 'u5' }
);

Update Relationship

await session.run(
  `MATCH (a:MyApp_User {id: $from})-[r:FOLLOWS]->(b:MyApp_User {id: $to})
   SET r.since = date()`,
  { from: 'u1', to: 'u2' }
);

Delete Relationship

await session.run(
  `MATCH (a:MyApp_User {id: $from})-[r:FOLLOWS]->(b:MyApp_User {id: $to})
   DELETE r`,
  { from: 'u1', to: 'u2' }
);

Indexes & Constraints

CREATE CONSTRAINT myapp_user_id IF NOT EXISTS
FOR (u:MyApp_User) REQUIRE u.id IS UNIQUE;

CREATE INDEX myapp_user_email IF NOT EXISTS
FOR (u:MyApp_User) ON (u.email);

Python CRUD

with driver.session() as session:
    session.run("CREATE (u:MyApp_User {id: $id}) RETURN u", id="u1")
    result = session.run("MATCH (u:MyApp_User {id: $id}) RETURN u", id="u1")
    session.run("MATCH (u:MyApp_User {id: $id}) SET u.name = $name", id="u1", name="Bob")
    session.run("MATCH (u:MyApp_User {id: $id}) DETACH DELETE u", id="u1")

Admin — Create User

Platform admin only.
docker exec -it bizfy-neo4j cypher-shell -u neo4j -p

// Read/write user
CREATE USER myapp_user SET PASSWORD 'strong-random-password' CHANGE NOT REQUIRED;
GRANT ROLE editor TO myapp_user;

// Read-only user
CREATE USER myapp_readonly SET PASSWORD 'strong-random-password' CHANGE NOT REQUIRED;
GRANT ROLE reader TO myapp_readonly;

// List users
SHOW USERS;

// Delete user
DROP USER myapp_user IF EXISTS;

Neo4j Roles

RoleAccess
readerRead-only Cypher
editorRead + write data
adminFull access (do not give to apps)

Send to Developer

NEO4J_URI=bolt://db.bizfylabs.com:7687
NEO4J_USER=myapp_user
NEO4J_PASSWORD=PASSWORD