GraphQL SDL generation
The driver's Neo4jSchemaGenerator exports your metadata as GraphQL SDL compatible with
@neo4j/graphql. Two things this is unusually good for:
- Instant APIs — a standard GraphQL schema over your existing models, with
@nodeand@relationshipdirectives already pointing at the right labels and edge types. - AI-ready schemas — agents and LLMs do markedly better with an SDL carrying semantic descriptions than with a bare table dump.
const sdl = orm.schema.getGraphSdl();
console.log(sdl);
From SDL to a running API
A complete GraphQL backend with NestJS takes this string the rest of
the way: @neo4j/graphql turns it into an executable schema, NestJS serves it, and the resulting
CRUD API reads and writes the same nodes your EntityManager does.
Comments become docstrings
comment is supported on entities and properties in both APIs, and lands as a triple-quoted
docstring:
export const ProductSchema = defineEntity({
name: 'Product',
comment: 'An item available for purchase.',
properties: (p) => ({
id: p.uuid().primary(),
price: p.float({ comment: 'Retail price in USD.' }),
}),
});
"""
An item available for purchase.
"""
type Product @node {
id: ID!
"""
Retail price in USD.
"""
price: Float!
}
What lands in the SDL
| Model | SDL |
|---|---|
| An entity | type X @node — with labels: [...] when it declares extra labels |
| A relation property | @relationship(type: "…", direction: IN | OUT) |
| A relationship entity | properties: "ActedIn" on the relationship directive |
| An interface entity | interface X, plus @declareRelationship on relations it declares |
| A fulltext search field | @fulltext(indexes: [...]) on every type the index covers |
| An auto-generated pivot, or a synthesized inverse side | Nothing — plumbing, not model |
The search field itself is never a field of the type: it stores nothing, and the directive is where
it belongs. queryName comes from the search field's options, and is derived from the type and the
scored fields when absent, so two indexes on one entity cannot collide.