Spatial Data

Learn how to expose NetTopologySuite spatial types as GeoJSON in Hot Chocolate v16.

Experimental: This feature is community-driven and not yet finalized. The core team has limited experience with spatial data and welcomes your feedback to guide next steps. While we try not to introduce breaking changes, we reserve the possibility to adjust the API in future releases.

Spatial data describes locations or shapes as objects. Many database providers support storing this type of data. APIs often use GeoJSON to send spatial data over the network.

The most common library for spatial data in .NET is NetTopologySuite. Entity Framework supports Spatial Data and uses NetTopologySuite as its data representation.

The HotChocolate.Spatial package integrates NetTopologySuite into Hot Chocolate. Your resolvers can return NetTopologySuite shapes, and they are transformed into GeoJSON.

Getting Started

Install the HotChocolate.Spatial package:

Bash
dotnet add package HotChocolate.Spatial
Warning
All HotChocolate.* packages need to have the same version.

Register the spatial types on the schema builder:

C#
builder
.AddGraphQL()
.AddSpatialTypes();

If you use data extensions to project data from a database, also install HotChocolate.Data.Spatial:

Bash
dotnet add package HotChocolate.Data.Spatial
Warning
All HotChocolate.* packages need to have the same version.

Register the data extensions:

C#
builder
.AddGraphQL()
.AddSpatialTypes()
.AddFiltering()
.AddProjections()
.AddSpatialFiltering()
.AddSpatialProjections();

All NetTopologySuite runtime types are now bound to the corresponding GeoJSON type:

C#
public class Pub
{
public int Id { get; set; }
public string Name { get; set; }
public Point Location { get; set; }
}
public class Query
{
public IQueryable<Pub> GetPubs(SomeDbContext someDbContext)
{
return someDbContext.Pubs;
}
}
SDL
type Pub {
id: Int!
name: String!
location: GeoJSONPointType!
}
type Query {
pubs: [Pub!]!
}
GraphQL
{
pubs {
id
location {
__typename
bbox
coordinates
crs
type
}
name
}
}
JSON
{
"data": {
"pubs": [
{
"id": 1,
"location": {
"__typename": "GeoJSONPointType",
"bbox": [12, 12, 12, 12],
"coordinates": [[12, 12]],
"crs": 4326,
"type": "Point"
},
"name": "The Winchester"
}
]
}
}

Spatial Types

Hot Chocolate supports GeoJSON input and output types, along with a GeoJSON scalar for generic inputs.

Output Types

NetTopologySuiteGraphQL
PointGeoJSONPointType
MultiPointGeoJSONMultiPointType
LineStringGeoJSONLineStringType
MultiLineStringGeoJSONMultiLineStringType
PolygonGeoJSONPolygonType
MultiPolygonGeoJSONMultiPolygonType
GeometryGeoJSONInterface

All GeoJSON output types implement:

SDL
interface GeoJSONInterface {
"The geometry type of the GeoJson object"
type: GeoJSONGeometryType!
"The minimum bounding box around the geometry object"
bbox: [Float]
"The coordinate reference system integer identifier"
crs: Int
}

Input Types

NetTopologySuiteGraphQL
PointGeoJSONPointInput
MultiPointGeoJSONMultiPointInput
LineStringGeoJSONLineStringInput
MultiLineStringGeoJSONMultiLineStringInput
PolygonGeoJSONPolygonInput
MultiPolygonGeoJSONMultiPolygonInput

Scalar

The Geometry scalar accepts any geometry type as input. This is useful when a resolver expects any Geometry type. Use this scalar with caution, as input and output types are more expressive.

SDL
scalar Geometry

Projections

Register the spatial projection handler with .AddSpatialProjections():

C#
builder
.AddGraphQL()
.AddProjections()
.AddSpatialTypes()
.AddSpatialProjections()

The projection middleware uses this handler to project spatial data directly to the database:

C#
[UseProjection]
public IQueryable<Pub> GetPubs(SomeDbContext someDbContext)
{
return someDbContext.Pubs;
}

Filtering

Entity Framework supports filtering on NetTopologySuite objects. HotChocolate.Spatial provides handlers for filtering spatial types on IQueryable. Register them with .AddSpatialFiltering():

C#
builder
.AddGraphQL()
.AddFiltering()
.AddSpatialTypes()
.AddSpatialFiltering()

After registration, UseFiltering() infers the possible filter types for all Geometry-based types.

C#
[UseFiltering]
public IQueryable<Pub> GetPubs(SomeDbContext someDbContext)
{
return someDbContext.Pubs;
}

Distance

The distance filter requires an input geometry. You can optionally buffer the geometry. All comparable filter operations are available.

GraphQL
{
pubs(
where: {
location: {
distance: { geometry: { type: Point, coordinates: [1, 1] }, lt: 120 }
}
}
) {
id
name
}
}

Contains

The contains filter is an implementation of Geometry.Contains. It requires an input geometry with an optional buffer.

GraphQL
{
counties(
where: {
area: { contains: { geometry: { type: Point, coordinates: [1, 1] } } }
}
) {
id
name
}
}

The negation is ncontains.

Touches

The touches filter is an implementation of Geometry.Touches.

GraphQL
{
counties(
where: {
area: {
touches: {
geometry: {
type: Polygon
coordinates: [[1, 1], ...]
}
}
}
}
) {
id
name
}
}

The negation is ntouches.

Intersects

The intersects filter is an implementation of Geometry.Intersects.

GraphQL
{
roads(
where: {
road: {
intersects: {
geometry: {
type: LineString
coordinates: [[1, 1], ...]
}
}
}
}
) {
id
name
}
}

The negation is nintersects.

Overlaps

The overlaps filter is an implementation of Geometry.Overlaps. The negation is noverlaps.

Within

The within filter is an implementation of Geometry.Within.

GraphQL
{
pubs(
where: {
location: {
within: { geometry: { type: Point, coordinates: [1, 1] }, buffer: 200 }
}
}
) {
id
name
}
}

The negation is nwithin.

Next Steps

Last updated on April 13, 2026 by Michael Staib