Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
World desk7 min

Building a Backend With Go and Neon PostgreSQL: Lessons for E-Commerce Orders and Inventory

A practical guide to connecting a Go e-commerce backend to Neon PostgreSQL, covering pooled versus direct connection strings, Go pool tuning, driver choice, and keeping orders and inventory consistent in one transaction.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

  1. 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.
  2. Copy the connection string. It is a standard PostgreSQL URL and includes sslmode=require. Keep the sslmode parameter; dropping it to get a local test to pass is the wrong trade.
  3. Store the string in an environment variable such as DATABASE_URL in your deployment configuration or secret store. Do not commit it to source control, and do not paste a live string into documentation or logs.
  4. 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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.

Go-side settings

  • SetMaxOpenConns caps 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.
  • SetMaxIdleConns controls how many idle connections are kept for reuse.
  • SetConnMaxLifetime limits how long a connection is reused before it is recycled, which helps when connections are cycled by infrastructure.
  • db.Stats() exposes pool counters. Watch WaitCount and WaitDuration: 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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
Heveboik Inventory & Sales Log Book for Small Business – Inventory Ledger Book, Inventory Notebook, Order Tracker for Purchases, Sales & Reorders, 5.8" x 8.5", Black
  • 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.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended order-placement sequence

  1. Begin one transaction with db.BeginTx(ctx, nil). Do not issue BEGIN or COMMIT as raw SQL through Exec; the Go guide advises using the transaction API.
  2. 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.
  3. Insert the order and its order lines using the same tx. Any sql.DB call made inside this block runs outside the transaction, so every write must go through tx.
  4. 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.

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.

Failure modes to test before launch

  • Requests hang under load. Check WaitCount and WaitDuration. 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 db calls made inside a transaction block where tx was 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=require retained.
  • Startup Ping that fails fast on a bad configuration.
  • Separate direct URL for migrations and any session-dependent tooling.
  • Explicit SetMaxOpenConns, SetMaxIdleConns and SetConnMaxLifetime values, reviewed against instance count and Neon limits.
  • Repository methods that take a context.Context and 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.