# Spatial Data - Hot Chocolate

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

Canonical source: https://chillicream.com/docs/hotchocolate/fetching-data/spatial-data

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](https://github.com/NetTopologySuite/NetTopologySuite). Entity Framework supports [Spatial Data](https://docs.microsoft.com/en-gb/ef/core/modeling/spatial) 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

| NetTopologySuite | GraphQL                    |
| ---------------- | -------------------------- |
| Point            | GeoJSONPointType           |
| MultiPoint       | GeoJSONMultiPointType      |
| LineString       | GeoJSONLineStringType      |
| MultiLineString  | GeoJSONMultiLineStringType |
| Polygon          | GeoJSONPolygonType         |
| MultiPolygon     | GeoJSONMultiPolygonType    |
| Geometry         | GeoJSONInterface           |

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

| NetTopologySuite | GraphQL                     |
| ---------------- | --------------------------- |
| Point            | GeoJSONPointInput           |
| MultiPoint       | GeoJSONMultiPointInput      |
| LineString       | GeoJSONLineStringInput      |
| MultiLineString  | GeoJSONMultiLineStringInput |
| Polygon          | GeoJSONPolygonInput         |
| MultiPolygon     | GeoJSONMultiPolygonInput    |

### 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

- [Filtering](https://chillicream.com/docs/hotchocolate/fetching-data/filtering) for general filtering concepts
- [Projections](https://chillicream.com/docs/hotchocolate/fetching-data/projections) for projection setup
- [Entity Framework integration](https://chillicream.com/docs/hotchocolate/fetching-data/integrations/entity-framework) for EF Core setup
[Edit this page on GitHub](https://github.com/ChilliCream/graphql-platform/edit/main/website/content/docs/hotchocolate/fetching-data/spatial-data.md)

Last updated on **June 30, 2026** by **Tobias Tengler**
