A Go service connects to Neon with a standard PostgreSQL connection string, so the connection itself is the easy part. The decisions that matter are which Neon endpoint the service uses, how the Go connection pool is sized, which driver the code talks through, and whether an order and its inventory change commit together. This guide covers those four decisions using the patterns Neon and the Go project document. It does not report benchmarks or production measurements, and it does not describe any one codebase.
Connecting a Go service to Neon
Neon accepts ordinary PostgreSQL connection URLs. Its current connection guide, last updated 5 October 2026, includes a Go example that uses the standard database/sql package with the lib/pq driver. The steps below follow that pattern.
As an Amazon Associate I earn from qualifying purchases.
- Open the Neon Console and select the branch, database and role the service will use. The connection string is generated for that combination, so a different role or branch produces a different string.
- Copy the connection string. It is a standard PostgreSQL URL and includes
sslmode=require. Keep thesslmodeparameter; dropping it to get a local test to pass is the wrong trade. - Store the string in an environment variable such as
DATABASE_URLin your deployment configuration or secret store. Do not commit it to source control, and do not paste a live string into documentation or logs. - Open the handle, verify the connection, and close it on shutdown.
package main
import (
"database/sql"
"log"
"os"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatalf("cannot reach Neon: %v", err)
}
}
sql.Open does not dial the database. It only validates its arguments and returns a handle, so the Ping call is what proves the URL, role and network path work. Run it at startup so a bad secret fails the deployment rather than the first customer request.
Pooled or direct: which Neon connection string to use
Neon provides two kinds of hostname. Pooled hostnames include -pooler; direct hostnames do not. Neon’s guidance is to use the pooled connection when the application opens many concurrent connections, and the direct connection for migrations or features that depend on session state.
#1 Best Overall
| Workload | Recommended Neon hostname | Reason |
|---|---|---|
| Web API serving many concurrent requests | Pooled (contains -pooler) |
Neon’s guidance targets applications that open many concurrent connections. |
| Schema migrations | Direct | Neon’s guidance recommends direct connections for migrations. |
| Code relying on session-level state | Direct | Neon’s guidance recommends direct connections for session-level features. |
| Small background job with one connection | Either; choose one and document it | The guide’s split is about concurrency and session behavior, not a rule for every service. |
Treat this as Neon’s documented guidance rather than a universal rule. The practical habit that prevents trouble is to name the URL each tool uses. If your migration runner reads DATABASE_URL, and that variable points at the pooled host, the migration runs through the pooled path even though you intended a direct one. Use a separate variable such as DATABASE_DIRECT_URL for migrations and state which one each process reads.
Sizing the Go connection pool
There are two pools in this design, and they should be sized together. The first is the Go pool inside sql.DB. The Go project describes sql.DB as a concurrency-safe handle that manages a pool: each operation takes a connection from the pool or opens one, and returns it afterward. The second is Neon’s pooled endpoint, which sits between your service and the database.
Rank #2
Go-side settings
SetMaxOpenConnscaps how many connections the Go process opens at once. Calls beyond the cap wait for a connection to be released. The Go documentation warns that this behaves like a semaphore, and that code acquiring resources in the wrong order can deadlock.SetMaxIdleConnscontrols how many idle connections are kept for reuse.SetConnMaxLifetimelimits how long a connection is reused before it is recycled, which helps when connections are cycled by infrastructure.db.Stats()exposes pool counters. WatchWaitCountandWaitDuration: rising values mean requests are queuing for a connection rather than talking to the database.
Choosing numbers
No cap is correct in the abstract. Start from the number of service instances, multiply by the open-connection cap you set on each, and check the total against the connection limits of your Neon plan and role. Then load-test the handler and adjust based on Stats() output. Set the Go cap too low and requests hang while waiting; set it too high and the service can overrun the database’s connection budget during traffic spikes.
Context deadlines help here. The Go project’s guide on accessing relational databases recommends passing a context.Context to database calls so that a cancelled request releases its connection rather than holding it until the query finishes. Use the ...Context variants such as ExecContext, QueryContext and BeginTx throughout the handler and repository layers.
Rank #3
- EASY TO USE - The inventory and sales log book are easy-to-use inventory books that help you track inventory, purchases, sales, balances, unit and total costs, and manage reorders - all in one place. Easy track your inventory for small businesses.
- MONITOR YOUR DATAS - Using a sales inventory book to store all your data, you can consult your records whenever needed. Optimize your business and generate the most benefit.
- UNIQUE DESIGN - We make sure you can tailor this inventory log book to your enterprise business needs to take full advantage of its capabilities. It will work for online, consignment, home or in-store businesses.
- HIGH QUALITY - This sales book for your business, sales book size of 5.8" x 8.5", just the perfectly size to fit in your backpack, purse or laptop case. Is used to high quality 100gsm pure white paper, elastic band and a back pocket for extra space.
- THE PERFECT GIFT - Use inventory and sales log book for your personal or samll business finances, give it to your friends, family as a gift for Birthday| Easter|Children's Day|Halloween|Thanksgiving|Christmas|Back to school and New Year's Day.
database/sql or pgx
Neon’s example uses database/sql with lib/pq. The pgx project supports two interfaces: a native PostgreSQL API, and an adapter that exposes pgx through database/sql. pgx’s own README suggests considering the native API for applications that target only PostgreSQL and have no dependency that requires database/sql.
| Option | Fits best when | Trade-off |
|---|---|---|
database/sql with lib/pq |
You want the approach shown in Neon’s Go example and standard-library portability. | You work through the generic database/sql interface and its driver conventions. |
| pgx native API | The service is PostgreSQL-only and you want PostgreSQL-specific features exposed directly. | Libraries that require database/sql need the adapter instead. |
pgx through database/sql |
You need pgx’s driver behavior but depend on tools that accept a *sql.DB. |
You get the generic interface back, so PostgreSQL-specific features are less direct. |
Neither choice is a speed decision by default. None of the sources cited here include benchmarks comparing the two, so choose on ecosystem fit, the PostgreSQL features you need, and what your team already maintains. Whichever you pick, keep all database calls behind a small repository layer so that switching later touches one package.
Rank #4
Keeping orders and inventory consistent
The main e-commerce risk is partial writes: stock decremented but no order created, or an order created against stock that was already sold. The Go project’s transaction guide addresses this directly. A transaction groups operations so they all succeed or none do, and its worked example checks inventory, updates it, inserts an order, and commits, with a deferred rollback that discards the work if any step fails.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →Recommended order-placement sequence
- Begin one transaction with
db.BeginTx(ctx, nil). Do not issueBEGINorCOMMITas raw SQL throughExec; the Go guide advises using the transaction API. - Decrement stock with a conditional update, and check how many rows changed. This avoids a read-then-write race between a separate check and the update.
- Insert the order and its order lines using the same
tx. Anysql.DBcall made inside this block runs outside the transaction, so every write must go throughtx. - Commit, then return the order ID. Put
defer tx.Rollback()immediately after a successful Begin so any early return discards the work; calling Rollback after Commit returns a harmless error that can be ignored.
var ErrOutOfStock = errors.New("insufficient stock")
func placeOrder(ctx context.Context, db *sql.DB, userID, productID int64, qty int) (int64, error) {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return 0, err
}
defer tx.Rollback()
res, err := tx.ExecContext(ctx,
`UPDATE inventory SET quantity = quantity - $1
WHERE product_id = $2 AND quantity >= $1`, qty, productID)
if err != nil {
return 0, err
}
n, err := res.RowsAffected()
if err != nil {
return 0, err
}
if n == 0 {
return 0, ErrOutOfStock
}
var orderID int64
err = tx.QueryRowContext(ctx,
`INSERT INTO orders (user_id, product_id, quantity, status)
VALUES ($1, $2, $3, 'placed') RETURNING id`,
userID, productID, qty).Scan(&orderID)
if err != nil {
return 0, err
}
if err := tx.Commit(); err != nil {
return 0, err
}
return orderID, nil
}
The schema is illustrative. Real stores usually have an order_items table and multiple products per order, in which case loop over the lines inside the same transaction. When a single order decrements several inventory rows, update them in a consistent order, such as ascending product ID, so two concurrent orders cannot each hold one row while waiting for the other.
Best Value
Keep external calls out of the transaction
Do not hold a database transaction open while calling a payment provider. The transaction holds row locks and a pool connection for as long as the external call takes, which can exhaust the pool under load. A safer sequence is to reserve stock and create a pending order in one short transaction, call the payment provider, then run a second short transaction that marks the order paid, or releases the stock if payment fails. The second step needs its own failure handling, such as a background job that releases abandoned reservations after a timeout.
Quick Recap
Failure modes to test before launch
- Requests hang under load. Check
WaitCountandWaitDuration. The Go pool cap may be below the concurrency you need, or handlers may hold connections across slow external calls. - Migrations fail or behave oddly. Confirm the migration tool reads the direct URL, not the pooled one, and that the URL matches what the tool expects.
- Orders exist without matching stock changes, or the reverse. Look for
dbcalls made inside a transaction block wheretxwas intended. - Two orders oversell the last unit. Confirm the stock update is conditional and that the row-count check is performed before the order insert.
- Cancelled requests keep running. Confirm the handler passes its request context down to the repository methods.
Practical setup checklist
- Connection string in an environment variable, with
sslmode=requireretained. - Startup
Pingthat fails fast on a bad configuration. - Separate direct URL for migrations and any session-dependent tooling.
- Explicit
SetMaxOpenConns,SetMaxIdleConnsandSetConnMaxLifetimevalues, reviewed against instance count and Neon limits. - Repository methods that take a
context.Contextand a shared executor so they run inside a transaction when needed. - Order placement as a single transaction with a conditional stock update, and no external payment call inside it.
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




