[{"data":1,"prerenderedAt":1746},["ShallowReactive",2],{"navigation_docs_en":3,"-en-database-bun":342,"-en-database-bun-surround":1741},[4,45,61,96,132,166,196,217,241,265],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":44},"Getting Started","i-lucide-rocket","\u002Fen\u002Fgetting-started","en\u002F1.getting-started",[10,15,20,24,29,34,39],{"title":11,"path":12,"stem":13,"icon":14},"Introduction","\u002Fen\u002Fgetting-started\u002Fintroduction","en\u002F1.getting-started\u002F1.introduction","i-lucide-house",{"title":16,"path":17,"stem":18,"icon":19},"Coming from Laravel","\u002Fen\u002Fgetting-started\u002Fcoming-from-laravel","en\u002F1.getting-started\u002F2.coming-from-laravel","i-lucide-arrow-right-left",{"title":21,"path":22,"stem":23,"icon":19},"Coming from Symfony","\u002Fen\u002Fgetting-started\u002Fcoming-from-symfony","en\u002F1.getting-started\u002F3.coming-from-symfony",{"title":25,"path":26,"stem":27,"icon":28},"Installation","\u002Fen\u002Fgetting-started\u002Finstallation","en\u002F1.getting-started\u002F4.installation","i-lucide-download",{"title":30,"path":31,"stem":32,"icon":33},"Your first module","\u002Fen\u002Fgetting-started\u002Ffirst-module","en\u002F1.getting-started\u002F5.first-module","i-lucide-package-plus",{"title":35,"path":36,"stem":37,"icon":38},"Project structure","\u002Fen\u002Fgetting-started\u002Fproject-structure","en\u002F1.getting-started\u002F6.project-structure","i-lucide-folder-tree",{"title":40,"path":41,"stem":42,"icon":43},"The shop sample application","\u002Fen\u002Fgetting-started\u002Fsample-app","en\u002F1.getting-started\u002F7.sample-app","i-lucide-shopping-cart",false,{"title":46,"icon":47,"path":48,"stem":49,"children":50,"page":44},"Reference","i-lucide-book-open","\u002Fen\u002Freference","en\u002F10.reference",[51,56],{"title":52,"path":53,"stem":54,"icon":55},"Configuration","\u002Fen\u002Freference\u002Fconfiguration","en\u002F10.reference\u002F1.configuration","i-lucide-settings",{"title":57,"path":58,"stem":59,"icon":60},"External dependencies","\u002Fen\u002Freference\u002Fdependencies","en\u002F10.reference\u002F2.dependencies","i-lucide-package",{"title":62,"icon":63,"path":64,"stem":65,"children":66,"page":44},"Concepts","i-lucide-lightbulb","\u002Fen\u002Fconcepts","en\u002F2.concepts",[67,72,77,82,86,91],{"title":68,"path":69,"stem":70,"icon":71},"Architecture","\u002Fen\u002Fconcepts\u002Farchitecture","en\u002F2.concepts\u002F1.architecture","i-lucide-layers",{"title":73,"path":74,"stem":75,"icon":76},"Application lifecycle","\u002Fen\u002Fconcepts\u002Fapplication-lifecycle","en\u002F2.concepts\u002F2.application-lifecycle","i-lucide-power",{"title":78,"path":79,"stem":80,"icon":81},"Error model","\u002Fen\u002Fconcepts\u002Ferror-model","en\u002F2.concepts\u002F3.error-model","i-lucide-shield-alert",{"title":52,"path":83,"stem":84,"icon":85},"\u002Fen\u002Fconcepts\u002Fconfiguration","en\u002F2.concepts\u002F4.configuration","i-lucide-settings-2",{"title":87,"path":88,"stem":89,"icon":90},"Codegen pipeline","\u002Fen\u002Fconcepts\u002Fcodegen-pipeline","en\u002F2.concepts\u002F5.codegen-pipeline","i-lucide-file-json",{"title":92,"path":93,"stem":94,"icon":95},"Design patterns","\u002Fen\u002Fconcepts\u002Fdesign-patterns","en\u002F2.concepts\u002F6.design-patterns","i-lucide-puzzle",{"title":97,"icon":98,"path":99,"stem":100,"children":101,"page":44},"HTTP & Routing","i-lucide-globe","\u002Fen\u002Fhttp","en\u002F3.http",[102,107,112,117,122,127],{"title":103,"path":104,"stem":105,"icon":106},"The server core","\u002Fen\u002Fhttp\u002Fserver","en\u002F3.http\u002F1.server","i-lucide-server",{"title":108,"path":109,"stem":110,"icon":111},"The Fiber engine","\u002Fen\u002Fhttp\u002Ffiber","en\u002F3.http\u002F2.fiber","i-lucide-zap",{"title":113,"path":114,"stem":115,"icon":116},"The chi engine","\u002Fen\u002Fhttp\u002Fchi","en\u002F3.http\u002F3.chi","i-lucide-route",{"title":118,"path":119,"stem":120,"icon":121},"Error responses","\u002Fen\u002Fhttp\u002Ferror-responses","en\u002F3.http\u002F4.error-responses","i-lucide-octagon-alert",{"title":123,"path":124,"stem":125,"icon":126},"Validation","\u002Fen\u002Fhttp\u002Fvalidation","en\u002F3.http\u002F5.validation","i-lucide-badge-check",{"title":128,"path":129,"stem":130,"icon":131},"Pagination","\u002Fen\u002Fhttp\u002Fpagination","en\u002F3.http\u002F6.pagination","i-lucide-list-ordered",{"title":133,"icon":134,"path":135,"stem":136,"children":137,"page":44},"Database","i-lucide-database","\u002Fen\u002Fdatabase","en\u002F4.database",[138,142,147,151,156,161],{"title":139,"path":140,"stem":141,"icon":134},"Overview","\u002Fen\u002Fdatabase\u002Foverview","en\u002F4.database\u002F1.overview",{"title":143,"path":144,"stem":145,"icon":146},"pgx","\u002Fen\u002Fdatabase\u002Fpgx","en\u002F4.database\u002F2.pgx","i-lucide-plug",{"title":148,"path":149,"stem":150,"icon":71},"bun","\u002Fen\u002Fdatabase\u002Fbun","en\u002F4.database\u002F3.bun",{"title":152,"path":153,"stem":154,"icon":155},"Transactions","\u002Fen\u002Fdatabase\u002Ftransactions","en\u002F4.database\u002F4.transactions","i-lucide-git-merge",{"title":157,"path":158,"stem":159,"icon":160},"Migrations","\u002Fen\u002Fdatabase\u002Fmigrations","en\u002F4.database\u002F5.migrations","i-lucide-file-stack",{"title":162,"path":163,"stem":164,"icon":165},"Seeders","\u002Fen\u002Fdatabase\u002Fseeding","en\u002F4.database\u002F6.seeding","i-lucide-sprout",{"title":167,"icon":168,"path":169,"stem":170,"children":171,"page":44},"Async & Background","i-lucide-workflow","\u002Fen\u002Fasync","en\u002F5.async",[172,176,181,186,191],{"title":173,"path":174,"stem":175,"icon":131},"Queue","\u002Fen\u002Fasync\u002Fqueue","en\u002F5.async\u002F1.queue",{"title":177,"path":178,"stem":179,"icon":180},"Events","\u002Fen\u002Fasync\u002Fevents","en\u002F5.async\u002F2.events","i-lucide-radio",{"title":182,"path":183,"stem":184,"icon":185},"Outbox","\u002Fen\u002Fasync\u002Foutbox","en\u002F5.async\u002F3.outbox","i-lucide-inbox",{"title":187,"path":188,"stem":189,"icon":190},"Scheduling","\u002Fen\u002Fasync\u002Fscheduler","en\u002F5.async\u002F4.scheduler","i-lucide-calendar-clock",{"title":192,"path":193,"stem":194,"icon":195},"Worker","\u002Fen\u002Fasync\u002Fworker","en\u002F5.async\u002F5.worker","i-lucide-cog",{"title":197,"icon":198,"path":199,"stem":200,"children":201,"page":44},"Security","i-lucide-shield-check","\u002Fen\u002Fsecurity","en\u002F6.security",[202,207,212],{"title":203,"path":204,"stem":205,"icon":206},"Authentication","\u002Fen\u002Fsecurity\u002Fauthentication","en\u002F6.security\u002F1.authentication","i-lucide-key-round",{"title":208,"path":209,"stem":210,"icon":211},"RBAC","\u002Fen\u002Fsecurity\u002Frbac","en\u002F6.security\u002F2.rbac","i-lucide-users",{"title":213,"path":214,"stem":215,"icon":216},"Policies","\u002Fen\u002Fsecurity\u002Fpolicies","en\u002F6.security\u002F3.policies","i-lucide-gavel",{"title":218,"icon":219,"path":220,"stem":221,"children":222,"page":44},"Observability","i-lucide-activity","\u002Fen\u002Fobservability","en\u002F7.observability",[223,226,231,236],{"title":139,"path":224,"stem":225,"icon":219},"\u002Fen\u002Fobservability\u002Foverview","en\u002F7.observability\u002F1.overview",{"title":227,"path":228,"stem":229,"icon":230},"Logging","\u002Fen\u002Fobservability\u002Flogging","en\u002F7.observability\u002F2.logging","i-lucide-scroll-text",{"title":232,"path":233,"stem":234,"icon":235},"OpenTelemetry","\u002Fen\u002Fobservability\u002Fopentelemetry","en\u002F7.observability\u002F3.opentelemetry","i-lucide-radar",{"title":237,"path":238,"stem":239,"icon":240},"Sentry","\u002Fen\u002Fobservability\u002Fsentry","en\u002F7.observability\u002F4.sentry","i-lucide-bug",{"title":242,"icon":243,"path":244,"stem":245,"children":246,"page":44},"Storage & Mail","i-lucide-hard-drive","\u002Fen\u002Fstorage-mail","en\u002F8.storage-mail",[247,251,256,261],{"title":248,"path":249,"stem":250,"icon":243},"Storage","\u002Fen\u002Fstorage-mail\u002Fstorage","en\u002F8.storage-mail\u002F1.storage",{"title":252,"path":253,"stem":254,"icon":255},"Email","\u002Fen\u002Fstorage-mail\u002Fmail","en\u002F8.storage-mail\u002F2.mail","i-lucide-mail",{"title":257,"path":258,"stem":259,"icon":260},"Notifications","\u002Fen\u002Fstorage-mail\u002Fnotifications","en\u002F8.storage-mail\u002F3.notifications","i-lucide-bell",{"title":262,"path":263,"stem":264,"icon":180},"Realtime","\u002Fen\u002Fstorage-mail\u002Frealtime","en\u002F8.storage-mail\u002F4.realtime",{"title":266,"icon":267,"path":268,"stem":269,"children":270,"page":44},"CLI Reference","i-lucide-terminal","\u002Fen\u002Fcli","en\u002F9.cli",[271,274,279,284,289,294,299,303,308,313,318,323,328,333,337],{"title":139,"path":272,"stem":273,"icon":267},"\u002Fen\u002Fcli\u002Foverview","en\u002F9.cli\u002F1.overview",{"title":275,"path":276,"stem":277,"icon":278},"add db","\u002Fen\u002Fcli\u002Fadd-db","en\u002F9.cli\u002F10.add-db","i-lucide-database-zap",{"title":280,"path":281,"stem":282,"icon":283},"add compose \u002F add docker","\u002Fen\u002Fcli\u002Fadd-compose","en\u002F9.cli\u002F11.add-compose","i-lucide-container",{"title":285,"path":286,"stem":287,"icon":288},"add mail","\u002Fen\u002Fcli\u002Fadd-mail","en\u002F9.cli\u002F12.add-mail","i-lucide-mail-plus",{"title":290,"path":291,"stem":292,"icon":293},"add notification","\u002Fen\u002Fcli\u002Fadd-notification","en\u002F9.cli\u002F13.add-notification","i-lucide-bell-plus",{"title":295,"path":296,"stem":297,"icon":298},"add realtime","\u002Fen\u002Fcli\u002Fadd-realtime","en\u002F9.cli\u002F14.add-realtime","i-lucide-radio-tower",{"title":300,"path":301,"stem":302,"icon":165},"add seeder","\u002Fen\u002Fcli\u002Fadd-seeder","en\u002F9.cli\u002F15.add-seeder",{"title":304,"path":305,"stem":306,"icon":307},"new project","\u002Fen\u002Fcli\u002Fnew-project","en\u002F9.cli\u002F2.new-project","i-lucide-folder-plus",{"title":309,"path":310,"stem":311,"icon":312},"new module","\u002Fen\u002Fcli\u002Fnew-module","en\u002F9.cli\u002F3.new-module","i-lucide-blocks",{"title":314,"path":315,"stem":316,"icon":317},"add surface","\u002Fen\u002Fcli\u002Fadd-surface","en\u002F9.cli\u002F4.add-surface","i-lucide-layers-2",{"title":319,"path":320,"stem":321,"icon":322},"add core","\u002Fen\u002Fcli\u002Fadd-core","en\u002F9.cli\u002F5.add-core","i-lucide-box",{"title":324,"path":325,"stem":326,"icon":327},"add handler","\u002Fen\u002Fcli\u002Fadd-handler","en\u002F9.cli\u002F6.add-handler","i-lucide-webhook",{"title":329,"path":330,"stem":331,"icon":332},"new migration","\u002Fen\u002Fcli\u002Fnew-migration","en\u002F9.cli\u002F7.new-migration","i-lucide-file-plus",{"title":334,"path":335,"stem":336,"icon":195},"worker generators","\u002Fen\u002Fcli\u002Fworker-generators","en\u002F9.cli\u002F8.worker-generators",{"title":338,"path":339,"stem":340,"icon":341},"upgrade templates","\u002Fen\u002Fcli\u002Fupgrade-templates","en\u002F9.cli\u002F9.upgrade-templates","i-lucide-refresh-cw",{"id":343,"title":148,"body":344,"description":1734,"extension":1735,"links":1736,"meta":1737,"navigation":1738,"path":149,"seo":1739,"stem":150,"__hash__":1740},"docs_en\u002Fen\u002F4.database\u002F3.bun.md",{"type":345,"value":346,"toc":1724},"minimark",[347,380,409,414,421,443,462,466,490,636,679,683,718,735,739,767,774,1149,1158,1272,1276,1301,1562,1587,1594,1598,1618,1655,1659,1689,1693,1720],[348,349,350,354,355,361,362,367,368,371,372,379],"p",{},[351,352,353],"code",{},"dbx\u002Fbunx"," is the optional ",[356,357,148],"a",{"href":358,"rel":359},"https:\u002F\u002Fbun.uptrace.dev",[360],"nofollow"," adapter for the kit's transaction model. The default ",[356,363,364],{"href":144},[351,365,366],{},"dbx\u002Fpg"," carries a native ",[351,369,370],{},"pgx.Tx"," in the context and works with hand-written SQL; a project that wants bun for query building wires this package instead. Bun ",[373,374,375,376],"strong",{},"only enters the binary when something imports ",[351,377,378],{},"bunx",". The core kit stays bun-free.",[381,382,387],"pre",{"className":383,"code":384,"language":385,"meta":386,"style":386},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import \"github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx\u002Fbunx\"\n","go","",[351,388,389],{"__ignoreMap":386},[390,391,394,398,402,406],"span",{"class":392,"line":393},"line",1,[390,395,397],{"class":396},"s7zQu","import",[390,399,401],{"class":400},"sMK4o"," \"",[390,403,405],{"class":404},"sBMFI","github.com\u002Fgp-system\u002Fgpsystem\u002Fdbx\u002Fbunx",[390,407,408],{"class":400},"\"\n",[410,411,413],"h2",{"id":412},"what-bun-is-and-what-it-is-not","What bun is, and what it is not",[348,415,416,417,420],{},"Let's be honest about the positioning: bun is a ",[373,418,419],{},"query builder with struct mapping"," (an \"ORM-lite\"), not Eloquent. What you get:",[422,423,424,436],"ul",{},[425,426,427,428,431,432,435],"li",{},"struct-based models (",[351,429,430],{},"bun:\"...\""," tags), builder-style query writing (",[351,433,434],{},"NewSelect().Model(...).Where(...)","),",[425,437,438,439,442],{},"relation loading (",[351,440,441],{},"Relation(...)","), bulk insert\u002Fupdate, typed scanning.",[348,444,445,446,449,450,453,454,457,458,461],{},"What you do ",[373,447,448],{},"not"," get from Eloquent: no ActiveRecord (",[351,451,452],{},"$product->save()","), no model events (",[351,455,456],{},"creating","\u002F",[351,459,460],{},"saved","), no lazy loading, no accessors\u002Fmutators, no global scopes. A model is a plain struct; a query is an explicit builder call. This can feel spartan at first compared to a full ActiveRecord ORM. In exchange, every query lives where you wrote it, and exactly what you see is what runs.",[410,463,465],{"id":464},"wiring","Wiring",[348,467,468,469,472,473,475,476,478,479,478,482,485,486,489],{},"You pick it with the ",[351,470,471],{},"--db bun"," flag at project generation (see ",[356,474,304],{"href":305},"); the manifest records it, and every later generator (",[351,477,309],{},", ",[351,480,481],{},"add event",[351,483,484],{},"add worker",") emits bun-flavoured code. The generated ",[351,487,488],{},"main.go"," wires it like this:",[381,491,493],{"className":383,"code":492,"language":385,"meta":386,"style":386},"pool := pg.MustNewPool(ctx, cfg.DB)   \u002F\u002F pool construction is shared with the pgx stack\nbunDB := bunx.Open(pool)\n\ndeps := shop.Dependencies{\n    DB:         bunDB,                     \u002F\u002F *bun.DB, the repositories hold this\n    Transactor: bunx.NewTransactor(bunDB), \u002F\u002F instead of pg.NewTransactor(pool)\n}\n",[351,494,495,538,562,569,588,605,630],{"__ignoreMap":386},[390,496,497,501,504,507,510,514,517,520,523,526,528,531,534],{"class":392,"line":393},[390,498,500],{"class":499},"sTEyZ","pool ",[390,502,503],{"class":400},":=",[390,505,506],{"class":499}," pg",[390,508,509],{"class":400},".",[390,511,513],{"class":512},"s2Zo4","MustNewPool",[390,515,516],{"class":400},"(",[390,518,519],{"class":499},"ctx",[390,521,522],{"class":400},",",[390,524,525],{"class":499}," cfg",[390,527,509],{"class":400},[390,529,530],{"class":499},"DB",[390,532,533],{"class":400},")",[390,535,537],{"class":536},"sHwdD","   \u002F\u002F pool construction is shared with the pgx stack\n",[390,539,541,544,546,549,551,554,556,559],{"class":392,"line":540},2,[390,542,543],{"class":499},"bunDB ",[390,545,503],{"class":400},[390,547,548],{"class":499}," bunx",[390,550,509],{"class":400},[390,552,553],{"class":512},"Open",[390,555,516],{"class":400},[390,557,558],{"class":499},"pool",[390,560,561],{"class":400},")\n",[390,563,565],{"class":392,"line":564},3,[390,566,568],{"emptyLinePlaceholder":567},true,"\n",[390,570,572,575,577,580,582,585],{"class":392,"line":571},4,[390,573,574],{"class":499},"deps ",[390,576,503],{"class":400},[390,578,579],{"class":404}," shop",[390,581,509],{"class":400},[390,583,584],{"class":404},"Dependencies",[390,586,587],{"class":400},"{\n",[390,589,591,594,597,600,602],{"class":392,"line":590},5,[390,592,593],{"class":499},"    DB",[390,595,596],{"class":400},":",[390,598,599],{"class":499},"         bunDB",[390,601,522],{"class":400},[390,603,604],{"class":536},"                     \u002F\u002F *bun.DB, the repositories hold this\n",[390,606,608,611,613,615,617,620,622,625,627],{"class":392,"line":607},6,[390,609,610],{"class":499},"    Transactor",[390,612,596],{"class":400},[390,614,548],{"class":499},[390,616,509],{"class":400},[390,618,619],{"class":512},"NewTransactor",[390,621,516],{"class":400},[390,623,624],{"class":499},"bunDB",[390,626,435],{"class":400},[390,628,629],{"class":536}," \u002F\u002F instead of pg.NewTransactor(pool)\n",[390,631,633],{"class":392,"line":632},7,[390,634,635],{"class":400},"}\n",[348,637,638,641,642,645,646,649,650,653,654,660,661,663,664,667,668,671,672,674,675,678],{},[351,639,640],{},"bunx.Open"," builds a ",[351,643,644],{},"*bun.DB"," over the kit's existing ",[351,647,648],{},"pgxpool.Pool"," (with ",[351,651,652],{},"pgdialect","), so the ",[356,655,657],{"href":656},"\u002Fen\u002Fdatabase\u002Foverview#config",[351,658,659],{},"dbx.Config"," and the pool configuration are reused; closing the pool closes the underlying handles. ",[351,662,553],{}," also installs the ",[351,665,666],{},"bunotel"," query hook: queries running through the bun query builder emit OTel spans (statement + timing) just like the pgx path, and show up in the ",[356,669,670],{"href":233},"trace waterfall"," and in ",[356,673,237],{"href":238},". Raw SQL running through ",[351,676,677],{},"bunx.Conn"," bypasses this hook (see below).",[410,680,682],{"id":681},"the-transactor-contract-is-identical","The Transactor contract is identical",[348,684,685,688,689,695,696,698,699,702,703,706,707,709,710,713,714,717],{},[351,686,687],{},"bunx.NewTransactor"," returns the same ",[356,690,692],{"href":691},"\u002Fen\u002Fdatabase\u002Foverview#transactor",[351,693,694],{},"dbx.Transactor"," interface as ",[351,697,366],{},": commit on ",[351,700,701],{},"nil",", rollback on error or panic, a nested call joins the outer transaction (flat nesting, the outermost call commits). The only difference is the carried type: a ",[351,704,705],{},"bun.Tx"," travels in the context instead of a ",[351,708,370],{},", with the ",[351,711,712],{},"bunx.ContextWithTx"," \u002F ",[351,715,716],{},"bunx.TxFromContext"," pair.",[348,719,720,721,724,725,728,729,731,732,734],{},"Which means your ",[373,722,723],{},"service layer does not change"," when you switch implementations: the ",[351,726,727],{},"PlaceOrder"," service on the ",[356,730,152],{"href":153}," page is identical to the letter on pgx and on bun, only the ",[351,733,488],{}," wiring and the repositories' internals differ.",[410,736,738],{"id":737},"repositories","Repositories",[348,740,741,742,478,745,748,749,752,753,755,756,759,760,762,763,766],{},"Bun's query builder methods (",[351,743,744],{},"NewSelect",[351,746,747],{},"NewInsert",", …) take no context, so a repository resolves the query surface from the context at the top of each method. ",[351,750,751],{},"bunx.From"," returns the ",[351,754,705],{}," carried by the context (when ",[351,757,758],{},"WithinTransaction"," opened one), otherwise the root ",[351,761,644],{},", both are ",[351,764,765],{},"bun.IDB",", so the caller uses the result uniformly.",[348,768,769,770,773],{},"The ",[356,771,772],{"href":41},"shop","'s product repository with bun:",[381,775,777],{"className":383,"code":776,"language":385,"meta":386,"style":386},"package repository\n\ntype Product struct {\n    bun.BaseModel `bun:\"table:products\"`\n\n    ID         int64     `bun:\"id,pk,autoincrement\"`\n    Name       string    `bun:\"name,notnull\"`\n    PriceCents int64     `bun:\"price_cents,notnull\"`\n    Stock      int       `bun:\"stock,notnull\"`\n    CreatedAt  time.Time `bun:\"created_at,notnull\"`\n}\n\ntype ProductRepository struct {\n    db *bun.DB\n}\n\nfunc (r *ProductRepository) ListProducts(ctx context.Context, limit int) ([]Product, error) {\n    var products []Product\n    err := bunx.From(ctx, r.db).NewSelect().Model(&products).\n        OrderExpr(\"created_at DESC, id DESC\").\n        Limit(limit).\n        Scan(ctx)\n    return products, err\n}\n",[351,778,779,787,791,805,825,829,846,862,877,894,915,920,925,937,953,958,963,1023,1038,1087,1105,1118,1130,1144],{"__ignoreMap":386},[390,780,781,784],{"class":392,"line":393},[390,782,783],{"class":400},"package",[390,785,786],{"class":404}," repository\n",[390,788,789],{"class":392,"line":540},[390,790,568],{"emptyLinePlaceholder":567},[390,792,793,796,799,802],{"class":392,"line":564},[390,794,795],{"class":400},"type",[390,797,798],{"class":404}," Product",[390,800,801],{"class":400}," struct",[390,803,804],{"class":400}," {\n",[390,806,807,810,812,815,818,822],{"class":392,"line":571},[390,808,809],{"class":404},"    bun",[390,811,509],{"class":400},[390,813,814],{"class":404},"BaseModel",[390,816,817],{"class":400}," `",[390,819,821],{"class":820},"sfazB","bun:\"table:products\"",[390,823,824],{"class":400},"`\n",[390,826,827],{"class":392,"line":590},[390,828,568],{"emptyLinePlaceholder":567},[390,830,831,834,838,841,844],{"class":392,"line":607},[390,832,833],{"class":499},"    ID         ",[390,835,837],{"class":836},"spNyl","int64",[390,839,840],{"class":400},"     `",[390,842,843],{"class":820},"bun:\"id,pk,autoincrement\"",[390,845,824],{"class":400},[390,847,848,851,854,857,860],{"class":392,"line":632},[390,849,850],{"class":499},"    Name       ",[390,852,853],{"class":836},"string",[390,855,856],{"class":400},"    `",[390,858,859],{"class":820},"bun:\"name,notnull\"",[390,861,824],{"class":400},[390,863,865,868,870,872,875],{"class":392,"line":864},8,[390,866,867],{"class":499},"    PriceCents ",[390,869,837],{"class":836},[390,871,840],{"class":400},[390,873,874],{"class":820},"bun:\"price_cents,notnull\"",[390,876,824],{"class":400},[390,878,880,883,886,889,892],{"class":392,"line":879},9,[390,881,882],{"class":499},"    Stock      ",[390,884,885],{"class":836},"int",[390,887,888],{"class":400},"       `",[390,890,891],{"class":820},"bun:\"stock,notnull\"",[390,893,824],{"class":400},[390,895,897,900,903,905,908,910,913],{"class":392,"line":896},10,[390,898,899],{"class":499},"    CreatedAt  ",[390,901,902],{"class":404},"time",[390,904,509],{"class":400},[390,906,907],{"class":404},"Time",[390,909,817],{"class":400},[390,911,912],{"class":820},"bun:\"created_at,notnull\"",[390,914,824],{"class":400},[390,916,918],{"class":392,"line":917},11,[390,919,635],{"class":400},[390,921,923],{"class":392,"line":922},12,[390,924,568],{"emptyLinePlaceholder":567},[390,926,928,930,933,935],{"class":392,"line":927},13,[390,929,795],{"class":400},[390,931,932],{"class":404}," ProductRepository",[390,934,801],{"class":400},[390,936,804],{"class":400},[390,938,940,943,946,948,950],{"class":392,"line":939},14,[390,941,942],{"class":499},"    db ",[390,944,945],{"class":400},"*",[390,947,148],{"class":404},[390,949,509],{"class":400},[390,951,952],{"class":404},"DB\n",[390,954,956],{"class":392,"line":955},15,[390,957,635],{"class":400},[390,959,961],{"class":392,"line":960},16,[390,962,568],{"emptyLinePlaceholder":567},[390,964,966,969,972,976,978,981,983,986,988,990,993,995,998,1000,1003,1006,1008,1011,1014,1016,1019,1021],{"class":392,"line":965},17,[390,967,968],{"class":400},"func",[390,970,971],{"class":400}," (",[390,973,975],{"class":974},"sHdIc","r ",[390,977,945],{"class":400},[390,979,980],{"class":404},"ProductRepository",[390,982,533],{"class":400},[390,984,985],{"class":512}," ListProducts",[390,987,516],{"class":400},[390,989,519],{"class":974},[390,991,992],{"class":404}," context",[390,994,509],{"class":400},[390,996,997],{"class":404},"Context",[390,999,522],{"class":400},[390,1001,1002],{"class":974}," limit",[390,1004,1005],{"class":836}," int",[390,1007,533],{"class":400},[390,1009,1010],{"class":400}," ([]",[390,1012,1013],{"class":404},"Product",[390,1015,522],{"class":400},[390,1017,1018],{"class":836}," error",[390,1020,533],{"class":400},[390,1022,804],{"class":400},[390,1024,1026,1029,1032,1035],{"class":392,"line":1025},18,[390,1027,1028],{"class":400},"    var",[390,1030,1031],{"class":499}," products ",[390,1033,1034],{"class":400},"[]",[390,1036,1037],{"class":404},"Product\n",[390,1039,1041,1044,1046,1048,1050,1053,1055,1057,1059,1062,1064,1067,1070,1072,1075,1078,1081,1084],{"class":392,"line":1040},19,[390,1042,1043],{"class":499},"    err ",[390,1045,503],{"class":400},[390,1047,548],{"class":499},[390,1049,509],{"class":400},[390,1051,1052],{"class":512},"From",[390,1054,516],{"class":400},[390,1056,519],{"class":499},[390,1058,522],{"class":400},[390,1060,1061],{"class":499}," r",[390,1063,509],{"class":400},[390,1065,1066],{"class":499},"db",[390,1068,1069],{"class":400},").",[390,1071,744],{"class":512},[390,1073,1074],{"class":400},"().",[390,1076,1077],{"class":512},"Model",[390,1079,1080],{"class":400},"(&",[390,1082,1083],{"class":499},"products",[390,1085,1086],{"class":400},").\n",[390,1088,1090,1093,1095,1098,1101,1103],{"class":392,"line":1089},20,[390,1091,1092],{"class":512},"        OrderExpr",[390,1094,516],{"class":400},[390,1096,1097],{"class":400},"\"",[390,1099,1100],{"class":820},"created_at DESC, id DESC",[390,1102,1097],{"class":400},[390,1104,1086],{"class":400},[390,1106,1108,1111,1113,1116],{"class":392,"line":1107},21,[390,1109,1110],{"class":512},"        Limit",[390,1112,516],{"class":400},[390,1114,1115],{"class":499},"limit",[390,1117,1086],{"class":400},[390,1119,1121,1124,1126,1128],{"class":392,"line":1120},22,[390,1122,1123],{"class":512},"        Scan",[390,1125,516],{"class":400},[390,1127,519],{"class":499},[390,1129,561],{"class":400},[390,1131,1133,1136,1139,1141],{"class":392,"line":1132},23,[390,1134,1135],{"class":396},"    return",[390,1137,1138],{"class":499}," products",[390,1140,522],{"class":400},[390,1142,1143],{"class":499}," err\n",[390,1145,1147],{"class":392,"line":1146},24,[390,1148,635],{"class":400},[348,1150,1151,1152,1154,1155,1157],{},"The order insert, called from the ",[351,1153,727],{}," transaction, runs on the ",[351,1156,705],{}," automatically:",[381,1159,1161],{"className":383,"code":1160,"language":385,"meta":386,"style":386},"func (r *OrderRepository) Insert(ctx context.Context, o *Order) error {\n    _, err := bunx.From(ctx, r.db).NewInsert().Model(o).Exec(ctx)\n    return err\n}\n",[351,1162,1163,1208,1262,1268],{"__ignoreMap":386},[390,1164,1165,1167,1169,1171,1173,1176,1178,1181,1183,1185,1187,1189,1191,1193,1196,1199,1202,1204,1206],{"class":392,"line":393},[390,1166,968],{"class":400},[390,1168,971],{"class":400},[390,1170,975],{"class":974},[390,1172,945],{"class":400},[390,1174,1175],{"class":404},"OrderRepository",[390,1177,533],{"class":400},[390,1179,1180],{"class":512}," Insert",[390,1182,516],{"class":400},[390,1184,519],{"class":974},[390,1186,992],{"class":404},[390,1188,509],{"class":400},[390,1190,997],{"class":404},[390,1192,522],{"class":400},[390,1194,1195],{"class":974}," o",[390,1197,1198],{"class":400}," *",[390,1200,1201],{"class":404},"Order",[390,1203,533],{"class":400},[390,1205,1018],{"class":836},[390,1207,804],{"class":400},[390,1209,1210,1213,1215,1218,1220,1222,1224,1226,1228,1230,1232,1234,1236,1238,1240,1242,1244,1246,1248,1251,1253,1256,1258,1260],{"class":392,"line":540},[390,1211,1212],{"class":499},"    _",[390,1214,522],{"class":400},[390,1216,1217],{"class":499}," err ",[390,1219,503],{"class":400},[390,1221,548],{"class":499},[390,1223,509],{"class":400},[390,1225,1052],{"class":512},[390,1227,516],{"class":400},[390,1229,519],{"class":499},[390,1231,522],{"class":400},[390,1233,1061],{"class":499},[390,1235,509],{"class":400},[390,1237,1066],{"class":499},[390,1239,1069],{"class":400},[390,1241,747],{"class":512},[390,1243,1074],{"class":400},[390,1245,1077],{"class":512},[390,1247,516],{"class":400},[390,1249,1250],{"class":499},"o",[390,1252,1069],{"class":400},[390,1254,1255],{"class":512},"Exec",[390,1257,516],{"class":400},[390,1259,519],{"class":499},[390,1261,561],{"class":400},[390,1263,1264,1266],{"class":392,"line":564},[390,1265,1135],{"class":396},[390,1267,1143],{"class":499},[390,1269,1270],{"class":392,"line":571},[390,1271,635],{"class":400},[410,1273,1275],{"id":1274},"raw-sql-and-sqlc-the-conn-resolver","Raw SQL and sqlc: the Conn resolver",[348,1277,1278,1279,1281,1282,1285,1286,1289,1290,1293,1294,1296,1297,1300],{},"Because a ",[351,1280,705],{}," embeds a ",[351,1283,1284],{},"*sql.Tx",", raw SQL and sqlc (in ",[351,1287,1288],{},"database\u002Fsql"," mode) can join ",[373,1291,1292],{},"the same transaction"," through ",[351,1295,677],{},". The shop's stock decrement: an atomic ",[351,1298,1299],{},"UPDATE"," that would be awkward to express with the builder:",[381,1302,1304],{"className":383,"code":1303,"language":385,"meta":386,"style":386},"func (r *ProductRepository) DecrementStock(ctx context.Context, productID int64, qty int) error {\n    res, err := bunx.Conn(ctx, r.db).ExecContext(ctx,\n        `UPDATE products SET stock = stock - $2 WHERE id = $1 AND stock >= $2`,\n        productID, qty)\n    if err != nil {\n        return err\n    }\n    if n, _ := res.RowsAffected(); n == 0 {\n        return errs.New(\"insufficient stock\",\n            errs.Code(\"insufficient_stock\"), errs.Public(\"The product is out of stock.\"))\n    }\n    return nil\n}\n",[351,1305,1306,1354,1396,1409,1420,1435,1442,1447,1484,1507,1547,1551,1558],{"__ignoreMap":386},[390,1307,1308,1310,1312,1314,1316,1318,1320,1323,1325,1327,1329,1331,1333,1335,1338,1341,1343,1346,1348,1350,1352],{"class":392,"line":393},[390,1309,968],{"class":400},[390,1311,971],{"class":400},[390,1313,975],{"class":974},[390,1315,945],{"class":400},[390,1317,980],{"class":404},[390,1319,533],{"class":400},[390,1321,1322],{"class":512}," DecrementStock",[390,1324,516],{"class":400},[390,1326,519],{"class":974},[390,1328,992],{"class":404},[390,1330,509],{"class":400},[390,1332,997],{"class":404},[390,1334,522],{"class":400},[390,1336,1337],{"class":974}," productID",[390,1339,1340],{"class":836}," int64",[390,1342,522],{"class":400},[390,1344,1345],{"class":974}," qty",[390,1347,1005],{"class":836},[390,1349,533],{"class":400},[390,1351,1018],{"class":836},[390,1353,804],{"class":400},[390,1355,1356,1359,1361,1363,1365,1367,1369,1372,1374,1376,1378,1380,1382,1384,1386,1389,1391,1393],{"class":392,"line":540},[390,1357,1358],{"class":499},"    res",[390,1360,522],{"class":400},[390,1362,1217],{"class":499},[390,1364,503],{"class":400},[390,1366,548],{"class":499},[390,1368,509],{"class":400},[390,1370,1371],{"class":512},"Conn",[390,1373,516],{"class":400},[390,1375,519],{"class":499},[390,1377,522],{"class":400},[390,1379,1061],{"class":499},[390,1381,509],{"class":400},[390,1383,1066],{"class":499},[390,1385,1069],{"class":400},[390,1387,1388],{"class":512},"ExecContext",[390,1390,516],{"class":400},[390,1392,519],{"class":499},[390,1394,1395],{"class":400},",\n",[390,1397,1398,1401,1404,1407],{"class":392,"line":564},[390,1399,1400],{"class":400},"        `",[390,1402,1403],{"class":820},"UPDATE products SET stock = stock - $2 WHERE id = $1 AND stock >= $2",[390,1405,1406],{"class":400},"`",[390,1408,1395],{"class":400},[390,1410,1411,1414,1416,1418],{"class":392,"line":571},[390,1412,1413],{"class":499},"        productID",[390,1415,522],{"class":400},[390,1417,1345],{"class":499},[390,1419,561],{"class":400},[390,1421,1422,1425,1427,1430,1433],{"class":392,"line":590},[390,1423,1424],{"class":396},"    if",[390,1426,1217],{"class":499},[390,1428,1429],{"class":400},"!=",[390,1431,1432],{"class":400}," nil",[390,1434,804],{"class":400},[390,1436,1437,1440],{"class":392,"line":607},[390,1438,1439],{"class":396},"        return",[390,1441,1143],{"class":499},[390,1443,1444],{"class":392,"line":632},[390,1445,1446],{"class":400},"    }\n",[390,1448,1449,1451,1454,1456,1459,1461,1464,1466,1469,1472,1475,1478,1482],{"class":392,"line":864},[390,1450,1424],{"class":396},[390,1452,1453],{"class":499}," n",[390,1455,522],{"class":400},[390,1457,1458],{"class":499}," _ ",[390,1460,503],{"class":400},[390,1462,1463],{"class":499}," res",[390,1465,509],{"class":400},[390,1467,1468],{"class":512},"RowsAffected",[390,1470,1471],{"class":400},"();",[390,1473,1474],{"class":499}," n ",[390,1476,1477],{"class":400},"==",[390,1479,1481],{"class":1480},"sbssI"," 0",[390,1483,804],{"class":400},[390,1485,1486,1488,1491,1493,1496,1498,1500,1503,1505],{"class":392,"line":879},[390,1487,1439],{"class":396},[390,1489,1490],{"class":499}," errs",[390,1492,509],{"class":400},[390,1494,1495],{"class":512},"New",[390,1497,516],{"class":400},[390,1499,1097],{"class":400},[390,1501,1502],{"class":820},"insufficient stock",[390,1504,1097],{"class":400},[390,1506,1395],{"class":400},[390,1508,1509,1512,1514,1517,1519,1521,1524,1526,1528,1530,1532,1535,1537,1539,1542,1544],{"class":392,"line":896},[390,1510,1511],{"class":499},"            errs",[390,1513,509],{"class":400},[390,1515,1516],{"class":512},"Code",[390,1518,516],{"class":400},[390,1520,1097],{"class":400},[390,1522,1523],{"class":820},"insufficient_stock",[390,1525,1097],{"class":400},[390,1527,435],{"class":400},[390,1529,1490],{"class":499},[390,1531,509],{"class":400},[390,1533,1534],{"class":512},"Public",[390,1536,516],{"class":400},[390,1538,1097],{"class":400},[390,1540,1541],{"class":820},"The product is out of stock.",[390,1543,1097],{"class":400},[390,1545,1546],{"class":400},"))\n",[390,1548,1549],{"class":392,"line":917},[390,1550,1446],{"class":400},[390,1552,1553,1555],{"class":392,"line":922},[390,1554,1135],{"class":396},[390,1556,1557],{"class":400}," nil\n",[390,1559,1560],{"class":392,"line":927},[390,1561,635],{"class":400},[348,1563,1564,1566,1567,1570,1571,713,1573,1576,1577,1580,1581,1584,1585,509],{},[351,1565,677],{}," deliberately returns the ",[373,1568,1569],{},"raw"," ",[351,1572,1284],{},[351,1574,1575],{},"*sql.DB",", not the bun wrappers. The bun wrapper reformats every query through its own placeholder syntax (",[351,1578,1579],{},"?","), which would break native ",[351,1582,1583],{},"$N"," placeholders, so sqlc-generated postgres SQL runs unchanged too. The trade-off is that queries running this way skip bun's query hooks (otel\u002Flogging); code that wants those uses the bun builder via ",[351,1586,1052],{},[1588,1589,1590,1591,509],"note",{},"The \"one transaction, two query styles\" guarantee (bun builder + raw SQL, shared commit\u002Frollback) is pinned down by the kit's integration tests against a real PostgreSQL: ",[351,1592,1593],{},"go test -tags=integration .\u002Fdbx\u002Fbunx\u002F",[410,1595,1597],{"id":1596},"outbox-under-bun","Outbox under bun",[348,1599,769,1600,1603,1604,1607,1608,1611,1612,1614,1615,1617],{},[356,1601,1602],{"href":183},"outbox"," store has a bun adapter too, mirroring the same split: bun projects wire ",[351,1605,1606],{},"NewStore(bunDB)"," from ",[351,1609,1610],{},"github.com\u002Fgp-system\u002Fgpsystem\u002Fevents\u002Foutbox\u002Fbunx",", so the outbox insert joins the bun transaction opened by ",[351,1613,758],{},". The generator wires this automatically in ",[351,1616,471],{}," projects:",[381,1619,1621],{"className":383,"code":1620,"language":385,"meta":386,"style":386},"Dispatcher: outbox.NewDispatcher(outboxbunx.NewStore(bunDB)),\n",[351,1622,1623],{"__ignoreMap":386},[390,1624,1625,1628,1630,1633,1635,1638,1640,1643,1645,1648,1650,1652],{"class":392,"line":393},[390,1626,1627],{"class":499},"Dispatcher",[390,1629,596],{"class":400},[390,1631,1632],{"class":499}," outbox",[390,1634,509],{"class":400},[390,1636,1637],{"class":512},"NewDispatcher",[390,1639,516],{"class":400},[390,1641,1642],{"class":499},"outboxbunx",[390,1644,509],{"class":400},[390,1646,1647],{"class":512},"NewStore",[390,1649,516],{"class":400},[390,1651,624],{"class":499},[390,1653,1654],{"class":400},")),\n",[410,1656,1658],{"id":1657},"constraint","Constraint",[348,1660,1661,1662,1664,1665,1667,1668,1670,1671,1674,1675,1677,1678,1681,1682,1684,1685,1688],{},"Within a single project, ",[351,1663,378],{}," and the pgx-native executor (",[351,1666,366],{},"'s ",[351,1669,530],{},") ",[373,1672,1673],{},"cannot share one transaction",": the two abstractions check out connections through different paths and carry the transaction under different context keys. In a bun project, raw SQL goes through ",[351,1676,677],{},", never through ",[351,1679,1680],{},"pg.NewDB(pool)",". ",[356,1683,157],{"href":158}," are unaffected: the same ",[351,1686,1687],{},"cmd\u002Fmigrate"," binary runs under both stacks.",[410,1690,1692],{"id":1691},"patterns-used","Patterns used",[422,1694,1695,1708],{},[425,1696,1697,1700,1701,1704,1705,509],{},[373,1698,1699],{},"Unit of Work \u002F transaction in context",", the same pattern as the ",[356,1702,1703],{"href":144},"pgx stack",": ",[356,1706,92],{"href":1707},"\u002Fen\u002Fconcepts\u002Fdesign-patterns#unit-of-work--transaction-in-context",[425,1709,1710,971,1713,478,1716,1719],{},[373,1711,1712],{},"Resolver functions",[351,1714,1715],{},"From(ctx, db)",[351,1717,1718],{},"Conn(ctx, db)","): the same \"join the context's transaction if there is one\" logic, with a bun-specific return type.",[1721,1722,1723],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":386,"searchDepth":540,"depth":540,"links":1725},[1726,1727,1728,1729,1730,1731,1732,1733],{"id":412,"depth":540,"text":413},{"id":464,"depth":540,"text":465},{"id":681,"depth":540,"text":682},{"id":737,"depth":540,"text":738},{"id":1274,"depth":540,"text":1275},{"id":1596,"depth":540,"text":1597},{"id":1657,"depth":540,"text":1658},{"id":1691,"depth":540,"text":1692},"The optional bun adapter: query builder and struct models, with the Transactor contract unchanged.","md",null,{},{"icon":71},{"title":148,"description":1734},"mwXCb-6juChtY7n4RWFzoHsxWssKJysYm9w_Mjo6wDM",[1742,1744],{"title":143,"path":144,"stem":145,"description":1743,"icon":146,"children":-1},"The pgx-native database stack: the pool, the DBTX query surface and the context-aware DB executor.",{"title":152,"path":153,"stem":154,"description":1745,"icon":155,"children":-1},"Cross-repository transactions carried by the context, no plumbing.",1784668709396]