Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions content/docs/guides/dotnet-npgsql.md
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,43 @@ ID: 4, Title: Dune, Author: Frank Herbert, Year: 1965, In Stock: True

> You can see that the book '1984' has been successfully removed from the table.

### Using transactions

The examples above execute each operation independently. For production code where multiple operations must succeed or fail together, wrap them in a transaction using `BeginTransactionAsync`. If any operation fails, call `RollbackAsync` to undo all changes.

```csharp
await using var conn = new NpgsqlConnection(connectionString);
await conn.OpenAsync();

await using var transaction = await conn.BeginTransactionAsync();
try
{
await using (var cmd = new NpgsqlCommand("INSERT INTO books (title, author, publication_year) VALUES (@title, @author, @year)", conn, transaction))
{
cmd.Parameters.AddWithValue("title", "Brave New World");
cmd.Parameters.AddWithValue("author", "Aldous Huxley");
cmd.Parameters.AddWithValue("year", 1932);
await cmd.ExecuteNonQueryAsync();
}

await using (var cmd = new NpgsqlCommand("UPDATE books SET in_stock = @in_stock WHERE title = @title", conn, transaction))
{
cmd.Parameters.AddWithValue("in_stock", false);
cmd.Parameters.AddWithValue("title", "Brave New World");
await cmd.ExecuteNonQueryAsync();
}

await transaction.CommitAsync();
Console.WriteLine("Transaction committed successfully.");
}
catch (Exception e)
{
await transaction.RollbackAsync();
Console.WriteLine($"Transaction rolled back: {e.Message}");
throw; // Re-throw to avoid silently swallowing errors
}
```

## Run the application

To run the entire script, execute the following command from your project directory:
Expand Down Expand Up @@ -454,4 +491,15 @@ You can find the source code for the application described in this guide on GitH
- [.NET Documentation](https://learn.microsoft.com/en-us/dotnet/)
- [Connect an Entity Framework application to Neon](/docs/guides/dotnet-entity-framework)

<details>
<summary>**Notes for AI-assisted setup**</summary>

- Always use parameterized queries (`@param` placeholders) for INSERT, UPDATE, and DELETE operations. Never concatenate user input into SQL strings. This prevents SQL injection.
- Wrap related database operations in a transaction (`BeginTransactionAsync` / `CommitAsync` / `RollbackAsync`). Always call `RollbackAsync` in the catch block and re-throw the exception with `throw;` to avoid silently swallowing errors.
- Use `await using` for `NpgsqlConnection`, `NpgsqlCommand`, and `NpgsqlDataReader` to ensure resources are properly disposed.
- This application uses `appsettings.json` for configuration, not a `.env` file. Do not create a `.env` file — it is not read by the application.
- Do not hardcode credentials in `.cs` files or commit `appsettings.json` with real credentials to version control. In production, use environment variables or a secure secrets manager. For more information, see [Security overview](/docs/security/security-overview).

</details>

<NeedHelp/>
50 changes: 50 additions & 0 deletions content/docs/guides/elixir.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,58 @@ ID: 4, Title: Dune, Author: Frank Herbert, Year: 1965, In Stock: true

> You can see that the book '1984' has been successfully deleted from the `books` table.

### Using transactions

The examples above execute each operation independently. For production code where multiple operations must succeed or fail together, wrap them in a transaction using `Postgrex.transaction/2`. If any operation inside the function fails, the entire transaction is rolled back automatically.

```elixir title="transaction_example.exs"
defmodule TransactionExample do
def run do
config = Application.get_all_env(:neon_elixir_quickstart)
{:ok, pid} = Postgrex.start_link(config)
IO.puts("Connection established")

try do
Postgrex.transaction(pid, fn conn ->
# Insert
Postgrex.query!(conn, "INSERT INTO books (title, author, publication_year) VALUES ($1, $2, $3)", ["Brave New World", "Aldous Huxley", 1932])
IO.puts("Inserted book.")

# Update
Postgrex.query!(conn, "UPDATE books SET in_stock = $1 WHERE title = $2", [false, "Brave New World"])
IO.puts("Updated stock status.")

IO.puts("Transaction committed successfully.")
end)
rescue
e in Postgrex.Error ->
IO.puts("Transaction failed and was rolled back.")
IO.inspect(e)
end
end
end

TransactionExample.run()
```

Run with:

```bash
mix run transaction_example.exs
```

</Steps>

<details>
<summary>**Notes for AI-assisted setup**</summary>

- The `ssl: [cacerts: :public_key.cacerts_get()]` option in the Postgrex configuration is mandatory. The connection to Neon will fail without it.
- Use `Postgrex.transaction/2` to wrap related database operations. If any operation inside the transaction function fails, the entire transaction is rolled back automatically.
- Always use parameterized queries (`$1`, `$2`, etc.) for INSERT, UPDATE, and DELETE operations. Never concatenate user input into SQL strings.
- Do not hardcode credentials in `.ex` or `.exs` files. Store connection parameters in `config/config.exs` and keep that file out of version control. For more information, see [Security overview](/docs/security/security-overview).

</details>

## Next steps: Using an ORM or framework

While this guide demonstrates how to connect to Neon using raw SQL queries, for more advanced and maintainable data interactions in your Elixir applications, consider using an Object-Relational Mapping (ORM) framework. ORMs not only let you work with data as objects but also help manage schema changes through automated migrations keeping your database structure in sync with your application models.
Expand Down
12 changes: 12 additions & 0 deletions content/docs/guides/rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -685,4 +685,16 @@ You can find the source code for the applications described in this guide on Git
- [tokio-postgres crate documentation](https://docs.rs/tokio-postgres/latest/tokio_postgres/)
- [Tokio async runtime](https://tokio.rs/)

<details>
<summary>**Notes for AI-assisted setup**</summary>

Comment thread
bgrenon marked this conversation as resolved.
- Ask the user which Rust Postgres driver they want to use: `postgres` (synchronous) or `tokio-postgres` (asynchronous). Only after they choose, proceed with the relevant code and instructions for that driver.
- The TLS/SSL connector setup is mandatory. Neon requires a secure connection, and the `postgres-openssl` and `openssl` crates must be included. The connection will fail without the `SslConnector` / `MakeTlsConnector` setup shown in the examples.
- Mutability matters for transactions. In the synchronous `postgres` crate, both the `client` and the `transaction` must be declared as `mut`. In `tokio-postgres`, the `client` must be `mut` to call `.transaction()`, but the returned transaction object is not `mut`. Getting this wrong causes compile errors.
- Use `dotenvy` (not `dotenv`) for loading `.env` files. The `dotenv` crate is unmaintained; `dotenvy` is the actively maintained fork.
- Always use parameterized queries (`$1`, `$2`, etc.) for INSERT, UPDATE, and DELETE operations. Never concatenate user input into SQL strings.
- Do not hardcode credentials in `.rs` files. Use environment variables via `dotenvy` and `std::env::var`. For more information, see [Security overview](/docs/security/security-overview).

</details>

<NeedHelp/>