Skip to content

Add Neon integration#1136

Open
YandyZaldivar wants to merge 5 commits intoCommunityToolkit:mainfrom
YandyZaldivar:feature/neon-integration
Open

Add Neon integration#1136
YandyZaldivar wants to merge 5 commits intoCommunityToolkit:mainfrom
YandyZaldivar:feature/neon-integration

Conversation

@YandyZaldivar
Copy link

Closes #398

Adds Neon support to Aspire Community Toolkit with:

  • A hosting integration for modeling and wiring Neon resources in AppHost.
  • A client helper integration for consuming Neon connections in application projects.
  • A one-shot provisioner workflow supporting attach/provision behavior and branch lifecycle scenarios.

What’s included

New packages

  • CommunityToolkit.Aspire.Hosting.Neon
  • CommunityToolkit.Aspire.Neon
  • Provisioner project: CommunityToolkit.Aspire.Neon.Provisioner

Hosting integration capabilities

  • AddNeon(...) resource model with health integration.
  • Provisioner modes:
    • Attach for existing resources.
    • Provision for create-or-attach workflows.
  • Project and branch configuration:
    • project + branch targeting
    • ephemeral branches
    • branch restore
    • anonymized branch support / masking rules
  • Database resource wiring via AddDatabase(...).
  • Dashboard compute commands (Suspend/Resume) via provisioner command flow.
  • Startup ordering/health semantics for dependent resources (WaitFor(neon)).

Client integration capabilities

  • Convenience registration (AddNeonClient(...)) for consuming injected connection strings.

Examples and tests

  • Added Neon example app host.
  • Added hosting and client test coverage for public API and functional behavior.

Repo wiring updates

  • Solution/package/workflow updates required to include the new Neon projects.

Validation

  • Focused Neon hosting tests:
    • AddNeonTests
    • NeonFunctionalTests

PR Checklist

  • Created a feature/dev branch in your fork (vs. submitting directly from a commit on main)
  • Based off latest main branch of toolkit
  • PR doesn't include merge commits (always rebase on top of our main, if needed)
  • New integration
    • Docs are written
    • Added description of major feature to project description for NuGet package (4000 total character limit, so don't push entire description over that)
  • Tests for the changes have been added (for bug fixes / features) (if applicable)
  • Contains NO breaking changes
  • Every new API (including internal ones) has full XML docs
  • Code follows all style conventions

Other information

Copilot AI review requested due to automatic review settings February 23, 2026 00:02
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds comprehensive Neon Postgres integration to the Aspire Community Toolkit, enabling developers to provision and manage Neon database resources within .NET Aspire applications. The integration includes hosting capabilities with provisioner-based resource management, client helpers for consuming connections, and extensive support for Neon-specific features like branch management, ephemeral branches, branch restoration, and data anonymization.

Changes:

  • Added hosting integration (CommunityToolkit.Aspire.Hosting.Neon) with resource models, builder extensions, provisioner orchestration, and health checks
  • Added client integration (CommunityToolkit.Aspire.Neon) for simplified Npgsql registration
  • Added provisioner project (CommunityToolkit.Aspire.Neon.Provisioner) with Neon API client and execution logic for attach/provision modes
  • Added comprehensive test coverage including unit tests, functional tests, and live integration tests
  • Updated solution files, workflows, package references, and README

Reviewed changes

Copilot reviewed 45 out of 45 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/CommunityToolkit.Aspire.Neon.Tests/NeonClientPublicApiTests.cs Unit tests for client API parameter validation
tests/CommunityToolkit.Aspire.Hosting.Neon.Tests/NeonFunctionalTests.cs Comprehensive functional and integration tests for provisioner and hosting behavior
tests/CommunityToolkit.Aspire.Hosting.Neon.Tests/AddNeonTests.cs Unit tests for hosting API surface and configuration options
src/CommunityToolkit.Aspire.Neon/AspireNeonExtensions.cs Client registration methods for NpgsqlDataSource with health checks
src/CommunityToolkit.Aspire.Neon.Provisioner/Program.cs Provisioner entry point handling attach/provision/suspend/resume operations
src/CommunityToolkit.Aspire.Neon.Provisioner/Shared/NeonApiClient.cs Neon API client with comprehensive HTTP operations and retry logic
src/CommunityToolkit.Aspire.Hosting.Neon/NeonBuilderExtensions.cs Core AddNeon methods, command registration, and provisioner output processing
src/CommunityToolkit.Aspire.Hosting.Neon/NeonResourceBuilderExtensions.cs Fluent API for project, branch, organization, and connection configuration
src/CommunityToolkit.Aspire.Hosting.Neon/NeonProvisionerProjectTemplate.cs Embedded provisioner template materialization logic
examples/neon/CommunityToolkit.Aspire.Hosting.Neon.AppHost/Program.cs Example app host demonstrating Neon integration usage
README.md Updated with Neon integration badges and links
CommunityToolkit.Aspire.slnx Added Neon projects to solution structure
.github/workflows/tests.yaml Added Neon test projects to CI workflow

@YandyZaldivar
Copy link
Author

@dotnet-policy-service agree

Copy link
Contributor

@tommasodotNET tommasodotNET left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

overall looks good, but i have doubts about the way provisioning is handled. it looks like it doesn’t follow aspire conventions. i’m not sure how much we want to be tied to core azure integrations, @aaronpowell

@YandyZaldivar
Copy link
Author

overall looks good, but i have doubts about the way provisioning is handled. it looks like it doesn’t follow aspire conventions. i’m not sure how much we want to be tied to core azure integrations, @aaronpowell

@tommasodotNET thanks for the review. I’ve pushed a second commit addressing the provisioning concerns — would appreciate your thoughts on whether this aligns better with the direction you had in mind.

In this update I removed .AddProvisioner(mode) and introduced .AsExisting() to align more closely with the standard Aspire pattern for existing resources. I also removed configuration from the project resource constructor to better follow Aspire conventions.

Previously, the default behavior was always attach (AsExisting) semantics (since Neon resources are intended to live in the cloud, not locally). Now, provisioning intent is inferred from usage (e.g, WithProject vs AddProject), and calling AsExisting() explicitly forces attach semantics by disabling creation flags.

That said, Neon is not modeled or provisioned as an Azure resource in this integration. The goal is not to tie it to Azure semantics, but to follow Aspire’s public conventions where they make sense.

One nuance is that Neon (as modeled here) is always an external cloud resource — it never lives locally or necessarily inside the deployment environment. Provisioning (attach or create) always runs because the integration validates the project/branch/database and surfaces connection information in both run and publish modes.

I specifically tested this by deploying to a VPS via docker-compose (https://github.com/davidfowl/aspire-ssh-deploy), and Neon is still provisioned even though it does not live on that host. The deployment target (Azure, Kubernetes, Docker, etc.) is independent from where Neon physically resides. For example, a user can deploy their services to Azure while selecting NeonProjectOptions.RegionId = "aws-us-east-1".

So while the public API now approximates Aspire’s Azure-style AsExisting() convention, Neon itself is not an Azure-scoped resource — it is an external database service that can be used regardless of the infrastructure provider.

Also for clarity: the Neon Azure Native integration has been deprecated and reached end-of-life on January 31, 2026 (https://neon.com/docs/introduction/billing-azure-marketplace). This integration is not related to that model; it targets Neon directly via its API.

@tommasodotNET
Copy link
Contributor

@YandyZaldivar thanks for clarifying.

i get what you'r saying, which is why i didn't request changes but just posted my comments as such. not sure what the correction direction for this integration should be.

the fact that neon should always be provisioned and never run locally is not unheard of. if you microsoft foundry, that can only be deployed on azure by aspire or attached to a pre-deployed resource.

that being said, it looks like we are closer to what the average aspire experience is.
the one thing that (if i'm not mistaken) is still different is setting deployment options via ConfigureInfrastructure.

@aaronpowell
Copy link
Member

Looking at the provisioner part of it, it does make me rather apprehensive on the design, the fact that it does a dotnet run on the fly of stuff that's pulled from an embedded resource really doesn't seem right. What if dotnet isn't in $PATH? what if they don't have the right .NET SDK installed? Ultimately, why isn't it just part of the hosting integration?

@YandyZaldivar
Copy link
Author

Neon as an integration can be modeled in different ways. In fact, it could even make sense to support more than one integration approach for the same resource. That’s why I like to frame this PR as: "within the scope of this integration", and avoid generalizing Neon. I also considered alternatives such as using tooling like neon_local: https://github.com/neondatabase/neon_local, not having a publish mode provisioner, and a few others.

@tommasodotNET
I've updated the API to use ConfigureInfrastructure and removed constructs like .WithProjectOptions and .WithBranchOptions, along with a few other adjustments in the latest commit. Every design choice comes with trade-offs, but my goal is to simplify the initial experience and better align with existing patterns. I'm happy to keep iterating if you still see room for improvement.

@aaronpowell
The provisioner is intentionally modeled as a standard AddProject(...) resource so it participates fully in the Aspire lifecycle: dashboard visibility, logs, health status, start/stop controls, and clear signaling when provisioning has completed. Under the hood this means dotnet run, but that is the same mechanism Aspire uses for any project added to a distributed application.

Regarding the .NET SDK and dotnet in $PATH, since Aspire itself runs via the .NET SDK, this integration doesn't introduce any additional requirement beyond what is already needed to run the AppHost locally.

The provisioner project is embedded and materialized on disk because it's an internal implementation detail of the integration. The intent is to avoid requiring consumers to manage it as a ProjectReference, while still allowing Aspire to treat it as a first-class project resource.

In publish mode, the provisioner is emitted as other projects, so in production it behaves like any other containerized resource. The dotnet run path is strictly for local development and dashboard-driven suspend/resume operations.

Why not part of the hosting integration?

  • The provisioner needs to appear as a visible resource in the Aspire dashboard (logs, health status, lifecycle controls) and clearly indicate when provisioning has completed.
  • Neon API operations (project creation, branch provisioning, endpoint polling) can take an uncertain amount of time. Running them in a separate process avoids blocking and triggering errors in the AppHost startup pipeline.
  • Modeling it as a project resource ensures it works consistently across current and future deployment targets.
  • In publish mode, it is automatically containerized, requiring no additional configuration from the user.
  • Suspend/resume operations triggered from the dashboard invoke the provisioner in a targeted mode, and this is limited to local development scenarios.

So the intended flow is:

  • Development (dotnet run): the provisioner runs as a standard Aspire project resource.
  • Production (dotnet publish): the provisioner is emitted likely as a container and runs as a normal containerized resource.

Happy to discuss further a better design for the provisioner.

@aaronpowell
Copy link
Member

Shipping as a series of embedded resources that then have to be hydrated back to disk and then pushed into Aspire is a very inefficient model when you just want something to be modelled as a resource within Aspire.

The provisioner needs to appear as a visible resource in the Aspire dashboard (logs, health status, lifecycle controls) and clearly indicate when provisioning has completed.

Yes, so AddProvisioner would create a NeonProvisionerResource which inherits from something like Resource since you're manually managing the life cycle

Neon API operations (project creation, branch provisioning, endpoint polling) can take an uncertain amount of time. Running them in a separate process avoids blocking and triggering errors in the AppHost startup pipeline.

Resources don't block the main thread of Aspire. Take a look at the Ollama implementation, we have sub-resources that pull images and those are async operations that could take a long time, and it won't block Aspire, only resources that depend on it.

Modeling it as a project resource ensures it works consistently across current and future deployment targets.

Aspire has teased shipping non-.NET app hosts, so that's probably not a safe assumption to make. Modelling using resources ensures consistency, not dynamically extracting files from an assuembly.

@YandyZaldivar
Copy link
Author

I think there are two valid goals we are trying to satisfy here:

  1. The hosting integration should own the provisioning logic and model neon avoiding embedded project extraction.

  2. There should still be a way to deploy a physical provisioner process/container to target environments when restart-driven reconciliation or self-healing behavior is required.

To reconcile these we can:

  • Move the provisioning logic into a shared library.
  • Refactor the hosting integration to execute provisioning logic directly (no embedded project hydration by default).
  • Local development and dashboard provisioning would continue to work through the resource model.
  • On deploy, provisioning would execute as part of aspire deploy or CI/CD workflows.

For advanced scenarios, we could still allow an opt-in mode that emits a physical provisioner container. This would enable restart-driven reconciliation (e.g branch restore, use ephemeral branch, self-healing workflows) without making that the default behavior. For example:

var neon = builder.AddNeon(apiKey)...
neon.WithRuntimeProvisioner(); // Explicit call to trigger .AddProject(provisioner)

This would:

  • Align better with resource modeling.
  • Remove the embedded project extraction pattern by deafult.
  • Strengthen the hosting integration responsibility.
  • Preserve the ability to run provisioning logic as a standalone container when operational requirements justify it.

Let me know if this direction makes more sense or if you have other advice.

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd do this a different way if it were me.

Instead of generating a (basically static) .NET project. If you want this to be a resource in the Aspire app, why don't you create a docker image with this functionality? Then the Neon Hosting library would just .AddContainer("provisioner", "neon-provisioner:latest"), or similar. Then you wouldn't need to generate and build this project everywhere.

Then to reuse the logic in the Hosting package, have a library that is shared between the Hosting integration and the container.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but then who hosts the image, who maintains and keeps it sync?

Ideally, aspire supports .AddProject, .AddContainer or something similar accepting a nuget package instead of having to copy sources.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but then who hosts the image

https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry

who maintains and keeps it sync?

I assume you do? Are you going to be the maintainer of the Neon integration?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that in a polyglot apphost, if someone wants to use the Neon integration, now they need to install .NET on their machine, when they don't for other integrations because the code is already compiled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Neon integration

5 participants