Skip to content

feat: prepare API for Render deployment (PostgreSQL, health check, CORS, Swagger, pagination) - #30

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
feature/render-deployment
Open

feat: prepare API for Render deployment (PostgreSQL, health check, CORS, Swagger, pagination)#30
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
feature/render-deployment

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Summary

Makes the API deployable to Render: adds PostgreSQL support, a render.yaml blueprint, an unauthenticated /health check, configurable CORS, Swagger, and pagination on GET /api/v1/readings.

Database provider selected at runtime

AppDbContext.OnConfiguring now picks the provider from DATABASE_URL:

var connectionString = Environment.GetEnvironmentVariable("DATABASE_URL");
if (!string.IsNullOrEmpty(connectionString))
    optionsBuilder.UseNpgsql(NormalizePostgresConnectionString(connectionString));
else
    optionsBuilder.UseSqlite($"DataSource={dbPath};Cache=Shared"); // local-dev fallback

Render exposes connection strings as URIs (postgres://user:pass@host:port/db), which Npgsql does not accept directly, so NormalizePostgresConnectionString converts a postgres(ql):// URI into the Npgsql key/value format (and passes through already-formatted strings). A new EF migration InitialPostgres was generated against the Npgsql provider.

Because EF migrations are provider-specific, startup now branches:

if (context.Database.IsNpgsql())
    context.Database.Migrate();      // production / PostgreSQL
else
    context.Database.EnsureCreated(); // SQLite local-dev fallback

(The repo previously had no committed migrations even though Program.cs called Migrate(), so the SQLite path effectively created no schema — EnsureCreated() fixes local dev too.)

/health endpoint

New HealthController returns 200 { "status": "healthy" }. ApiKeyAuthMiddleware short-circuits before key validation for /health so Render's health probe works without the API key:

if (context.Request.Path.StartsWithSegments("/health")) { await _next(context); return; }

CORS + Swagger (Startup.cs)

  • Startup now takes IConfiguration. CORS policy FrontendPolicy reads ALLOWED_ORIGINS (comma-separated); when empty it falls back to AllowAnyOrigin (dev-friendly). app.UseCors(...) runs before the API-key middleware and routing.
  • Swagger registered with an X-API-KEY ApiKey security scheme; the UI is exposed only outside Production.

Pagination on GET /api/v1/readings

Adds page (default 1) and pageSize (default 50, clamped to 200) and returns a typed envelope PagedResult<Reading> serialized as { data, page, pageSize, total } (ordered by CreatedAt desc). A concrete DTO is used instead of an anonymous type so the response is assertable from the test assembly; ReadingControllerTests were updated for the new contract and two pagination tests added.

Infra / config

  • render.yaml (Docker web service + free PostgreSQL, healthCheckPath: /health, DATABASE_URL from the DB, ApiKey/ALLOWED_ORIGINS as sync: false).
  • appsettings.Production.json with reduced log verbosity.
  • .dockerignore now excludes appsettings.Development.json, local secrets, and *.sqlite*.
  • README documents the Render deployment and required env vars.

Verification

dotnet build -c Release and dotnet test pass (91/91). Ran locally against the SQLite fallback: /health → 200 without key, readings → 401 without key, paginated envelope returned with key, CORS preflight returns headers, and /swagger/v1/swagger.json serves in Development.

Link to Devin session: https://app.devin.ai/sessions/829418a074784cbaa96bc62faf058bea
Requested by: @joaoferreira-dev

…RS, Swagger, pagination)

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Prepares the AirAware ASP.NET Core API for deployment on Render by adding PostgreSQL support (via DATABASE_URL), health checks, configurable CORS, Swagger, and paginated responses for readings.

Changes:

  • Add Render blueprint (render.yaml) and production configuration (appsettings.Production.json), plus Docker ignore hardening.
  • Add PostgreSQL runtime selection + initial Npgsql EF Core migrations; adjust startup to migrate on PostgreSQL and EnsureCreated() on SQLite.
  • Add /health, Swagger (non-Production), CORS policy, and paginate GET /api/v1/readings with a typed PagedResult<T> (tests updated).

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
render.yaml Render blueprint to provision the API service and PostgreSQL database.
README.md Documents Render deployment, env vars, and SQLite vs PostgreSQL behavior.
AirAware/ViewModels/PagedResult.cs Introduces a typed pagination envelope for API responses/tests.
AirAware/Startup.cs Adds CORS + Swagger configuration and updates middleware pipeline.
AirAware/Program.cs Applies migrations on PostgreSQL; uses EnsureCreated() for SQLite fallback.
AirAware/Migrations/AppDbContextModelSnapshot.cs EF Core snapshot for the new PostgreSQL migration set.
AirAware/Migrations/20260614040505_InitialPostgres.Designer.cs Generated migration designer for PostgreSQL schema.
AirAware/Migrations/20260614040505_InitialPostgres.cs Creates initial PostgreSQL tables + indexes.
AirAware/Middleware/ApiKeyAuthMiddleware.cs Exempts /health from API-key auth.
AirAware/Data/AppDbContext.cs Selects provider from DATABASE_URL and normalizes Render-style Postgres URIs.
AirAware/Controllers/ReadingController.cs Adds pagination parameters and returns PagedResult<Reading>.
AirAware/Controllers/HealthController.cs Adds unauthenticated health check endpoint.
AirAware/appsettings.Production.json Production log-level tuning and allowed hosts.
AirAware/AirAware.csproj Adds Npgsql EF provider and Swashbuckle dependencies.
AirAware.Tests/Controllers/ReadingControllerTests.cs Updates tests for the paginated response contract + adds pagination tests.
.dockerignore Excludes dev configs/secrets and SQLite files from Docker build context.
Files not reviewed (1)
  • AirAware/Migrations/20260614040505_InitialPostgres.Designer.cs: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread AirAware/Startup.cs
Comment on lines +29 to +31
var origins = Configuration.GetValue<string>("AllowedOrigins")?.Split(
',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
?? Array.Empty<string>();
Comment thread AirAware/Startup.cs
Comment on lines +78 to 82
app.UseCors(CorsPolicyName);

app.UseMiddleware<ApiKeyAuthMiddleware>();

app.UseRouting();
Comment thread AirAware/AirAware.csproj
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
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.

2 participants