[{"data":1,"prerenderedAt":3487},["ShallowReactive",2],{"post-relational-vs-nosql-databases":3,"rel-relational-vs-nosql-databases":368,"sib-relational-vs-nosql-databases":3407},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"layout":10,"date":11,"subtitle":12,"image":13,"optimized_image":13,"category":14,"tags":15,"author":20,"paginate":6,"body":21,"_type":362,"_id":363,"_source":364,"_file":365,"_stem":366,"_extension":367},"/posts/relational-vs-nosql-databases","posts",false,"","Relational vs. NoSQL Databases: Why PostgreSQL Is My Default","Lessons from using MySQL, PostgreSQL, and MongoDB, including schema evolution, data consistency, and why database-side event logic deserves caution.","post","2025-03-15T11:00:00.000Z","Choosing a data model by looking at the pain it creates","/assets/img/uploads/relational-vs-nosql-databases.jpg","code",[14,16,17,18,19],"database","postgresql","mongodb","architecture","jaimedearcos",{"type":22,"children":23,"toc":351},"root",[24,36,41,46,53,58,82,87,93,98,103,116,121,127,132,137,142,147,153,175,180,186,191,205,210,224,245,251,263,268,273,279,284,289,295],{"type":25,"tag":26,"props":27,"children":28},"element","blockquote",{},[29],{"type":25,"tag":30,"props":31,"children":32},"p",{},[33],{"type":34,"value":35},"text","My default is PostgreSQL, not because relational databases fit every problem, but because the cost of a well-defined model is usually lower than the cost of discovering that the data has no reliable shape.",{"type":25,"tag":30,"props":37,"children":38},{},[39],{"type":34,"value":40},"I have spent most of my career working with relational databases, mainly MySQL and PostgreSQL. More recently, I have used MongoDB more often. That experience has made me more comfortable with document databases, but it has not changed my default: when I start a project without a strong reason to choose otherwise, I reach for PostgreSQL.",{"type":25,"tag":30,"props":42,"children":43},{},[44],{"type":34,"value":45},"This is not a relational-versus-NoSQL benchmark. The more useful question is what kind of pain each model moves into your application, your migrations, and your production operations. Both can work well. Both can become awkward when the shape of the data and the way the product uses it stop matching.",{"type":25,"tag":47,"props":48,"children":50},"h2",{"id":49},"a-defined-schema-is-a-useful-constraint",[51],{"type":34,"value":52},"A defined schema is a useful constraint",{"type":25,"tag":30,"props":54,"children":55},{},[56],{"type":34,"value":57},"Relational modeling asks you to describe entities, relationships, constraints, and the shape of important values. That takes thought up front. When a requirement changes, the change may involve a migration, a backfill, and a deployment plan. For a busy table, even a simple schema change deserves care: locks, table size, compatibility between application versions, and rollback strategy all matter.",{"type":25,"tag":30,"props":59,"children":60},{},[61,63,69,71,80],{"type":34,"value":62},"That friction is real. It is also useful feedback. A schema makes assumptions visible and gives the database a chance to enforce some of them. A foreign key can prevent a reference to a row that does not exist. A ",{"type":25,"tag":14,"props":64,"children":66},{"className":65},[],[67],{"type":34,"value":68},"NOT NULL",{"type":34,"value":70}," constraint can stop incomplete records from entering the system. A unique constraint can protect a business rule even when two application requests race. PostgreSQL's ",{"type":25,"tag":72,"props":73,"children":77},"a",{"href":74,"rel":75},"https://www.postgresql.org/docs/current/ddl-constraints.html",[76],"nofollow",[78],{"type":34,"value":79},"constraint documentation",{"type":34,"value":81}," is a good reminder that integrity is not only an application concern.",{"type":25,"tag":30,"props":83,"children":84},{},[85],{"type":34,"value":86},"This is part of why I like relational databases for the same reason I tend to prefer statically typed languages over dynamically typed ones. The analogy is not exact, and Python and JavaScript are not untyped languages. Still, in both cases, making structure explicit earlier can help a team catch invalid assumptions closer to where they are introduced. It does not remove bugs, but it can make some classes of change easier to reason about.",{"type":25,"tag":47,"props":88,"children":90},{"id":89},"flexibility-does-not-remove-schema-decisions",[91],{"type":34,"value":92},"Flexibility does not remove schema decisions",{"type":25,"tag":30,"props":94,"children":95},{},[96],{"type":34,"value":97},"MongoDB gives a document a natural home when the data is usually read and written together. An order with a bounded list of line items, for example, can be represented as one document. This can make common access patterns straightforward and keep related data close together.",{"type":25,"tag":30,"props":99,"children":100},{},[101],{"type":34,"value":102},"The trade-off is that embedding is a decision about ownership and change. If an embedded object is shared by many documents, updating it can mean coordinating multiple copies. If a document grows without a useful bound, it may become hard to update or inefficient to retrieve. If a relationship is referenced instead, the application may need multiple queries or an aggregation pipeline to reconstruct what one SQL join would express directly.",{"type":25,"tag":30,"props":104,"children":105},{},[106,108,114],{"type":34,"value":107},"MongoDB is often called schemaless, but the data still has a shape. If the database does not enforce that shape, the application and the team must. Different code paths can write subtly different versions of a document. A field can be absent, ",{"type":25,"tag":14,"props":109,"children":111},{"className":110},[],[112],{"type":34,"value":113},"null",{"type":34,"value":115},", or present with a different type. An old document may still be in production after the code has moved on. Without validation and a migration policy, flexibility can turn into schema drift.",{"type":25,"tag":30,"props":117,"children":118},{},[119],{"type":34,"value":120},"That flexibility is valuable when records genuinely vary, when a bounded aggregate is the unit of work, or when the access patterns are well understood. It is less valuable when the product is still changing and nobody can confidently say which fields will be needed together six months from now. In that situation, avoiding a migration today may only defer the modeling decision until it is more expensive to make.",{"type":25,"tag":47,"props":122,"children":124},{"id":123},"the-difficult-part-is-often-the-next-query",[125],{"type":34,"value":126},"The difficult part is often the next query",{"type":25,"tag":30,"props":128,"children":129},{},[130],{"type":34,"value":131},"The first version of a data model tends to reflect the first screens or endpoints. The pain appears when new questions arrive: show all invoices for a customer across several years; find every order containing a product; compare activity across tenants; introduce a report that groups records by a field the original access pattern did not need.",{"type":25,"tag":30,"props":133,"children":134},{},[135],{"type":34,"value":136},"With a relational model, joins and ad hoc queries are often a strength. The schema can evolve to support new relationships, and the database can combine data without the application loading every record and stitching it together. But that convenience does not make every query cheap. Indexes, query plans, data volume, and write contention still need attention.",{"type":25,"tag":30,"props":138,"children":139},{},[140],{"type":34,"value":141},"With a document model, a carefully chosen aggregate can make the common path simple. The risk is coupling the storage shape too tightly to today's reads. A new access pattern can require duplicating data, changing the document boundary, adding an index with a real write and storage cost, or maintaining a separate read model. Denormalization can be the right choice, but it makes update ownership and consistency part of the design rather than something to postpone.",{"type":25,"tag":30,"props":143,"children":144},{},[145],{"type":34,"value":146},"When I evaluate a model, I try to list more than the first few queries. I ask which relationships must remain valid, which values change together, what is likely to be reported on later, and which copies of a fact need to be updated. Those questions reveal the likely maintenance cost better than a generic claim about one database being faster.",{"type":25,"tag":47,"props":148,"children":150},{"id":149},"postgresql-can-cover-more-than-relational-rows",[151],{"type":34,"value":152},"PostgreSQL can cover more than relational rows",{"type":25,"tag":30,"props":154,"children":155},{},[156,158,164,166,173],{"type":34,"value":157},"Choosing PostgreSQL does not mean every value must be split into normalized columns and tables. PostgreSQL's ",{"type":25,"tag":14,"props":159,"children":161},{"className":160},[],[162],{"type":34,"value":163},"jsonb",{"type":34,"value":165}," support can be useful for attributes that are genuinely variable while keeping core entities, relationships, and invariants in a relational model. The ",{"type":25,"tag":72,"props":167,"children":170},{"href":168,"rel":169},"https://www.postgresql.org/docs/current/datatype-json.html",[76],[171],{"type":34,"value":172},"JSON types documentation",{"type":34,"value":174}," covers indexing and operators as well as storage.",{"type":25,"tag":30,"props":176,"children":177},{},[178],{"type":34,"value":179},"I treat this as a pragmatic boundary, not a way to avoid modeling altogether. If a JSON field becomes central to filtering, reporting, authorization, or relationships, it may deserve a first-class column or table. Keeping the stable core explicit and the variable edge flexible can be a good compromise, provided the team knows which fields are allowed to drift and which are part of the contract.",{"type":25,"tag":47,"props":181,"children":183},{"id":182},"be-careful-where-change-logic-lives",[184],{"type":34,"value":185},"Be careful where change logic lives",{"type":25,"tag":30,"props":187,"children":188},{},[189],{"type":34,"value":190},"I am cautious about using database-side mechanisms to implement application workflows. In MongoDB, change streams let an application subscribe to persisted changes. In relational databases, triggers can execute database functions when rows are changed. They are different mechanisms, but either can become a hidden path for behavior if used to encode business decisions that are otherwise owned by the application.",{"type":25,"tag":30,"props":192,"children":193},{},[194,196,203],{"type":34,"value":195},"The pain is not simply that the code lives in an unusual language. It is that behavior becomes split across layers. A developer following an API request may see an application write and miss the additional effect triggered by a change stream consumer or a database trigger. Tests, deployment order, retries, permissions, and production debugging now need to account for logic in more than one place. A trigger can also run inside the transaction that modified the row, so its failure and latency affect that write; PostgreSQL documents this execution model in its ",{"type":25,"tag":72,"props":197,"children":200},{"href":198,"rel":199},"https://www.postgresql.org/docs/current/trigger-definition.html",[76],[201],{"type":34,"value":202},"trigger documentation",{"type":34,"value":204},".",{"type":25,"tag":30,"props":206,"children":207},{},[208],{"type":34,"value":209},"There are legitimate database-local uses. Constraints should remain in the database. A trigger may be appropriate for a narrow audit requirement or an invariant that must apply regardless of which client writes the table. The warning is about hiding application workflows there: for example, sending notifications, making decisions about a business process, or silently coordinating several systems. If we choose such a mechanism, it should be deliberate, visible, documented, and tested as part of the system.",{"type":25,"tag":30,"props":211,"children":212},{},[213,215,222],{"type":34,"value":214},"For propagating database changes into an event architecture, Kafka Connect and connectors such as Debezium have been more useful in my experience. They can capture row-level changes and publish them to Kafka without putting domain policy into the database. The ",{"type":25,"tag":72,"props":216,"children":219},{"href":217,"rel":218},"https://debezium.io/documentation/reference/stable/connectors/postgresql.html",[76],[220],{"type":34,"value":221},"Debezium PostgreSQL connector",{"type":34,"value":223},", for example, reads changes through PostgreSQL's logical decoding and streams records to Kafka topics.",{"type":25,"tag":30,"props":225,"children":226},{},[227,229,235,237,243],{"type":34,"value":228},"That stream is change data capture, not automatically a business event. A row update does not necessarily mean ",{"type":25,"tag":14,"props":230,"children":232},{"className":231},[],[233],{"type":34,"value":234},"OrderApproved",{"type":34,"value":236}," or ",{"type":25,"tag":14,"props":238,"children":240},{"className":239},[],[241],{"type":34,"value":242},"CustomerNotified",{"type":34,"value":244},"; consumers still need a clear contract and domain interpretation. I prefer keeping that meaning in application-owned code, while using connectors to move data reliably. CDC also adds operational responsibilities, including replication slots, WAL retention, monitoring, and recovery, so it is not free plumbing.",{"type":25,"tag":47,"props":246,"children":248},{"id":247},"my-default-and-the-exceptions",[249],{"type":34,"value":250},"My default and the exceptions",{"type":25,"tag":30,"props":252,"children":253},{},[254,256,261],{"type":34,"value":255},"I would start with PostgreSQL when the domain has meaningful relationships, data integrity matters across those relationships, requirements are still likely to change, or the product will need exploratory queries and reporting. Its explicit model gives me a dependable place to put important invariants, and ",{"type":25,"tag":14,"props":257,"children":259},{"className":258},[],[260],{"type":34,"value":163},{"type":34,"value":262}," leaves room for data that is naturally less structured.",{"type":25,"tag":30,"props":264,"children":265},{},[266],{"type":34,"value":267},"I would consider MongoDB when the domain naturally consists of bounded documents that are read and updated together, variation within those documents is a real property of the data, and the access patterns are understood well enough to choose embedding and references intentionally. I would also make document validation and evolution part of the design from the beginning.",{"type":25,"tag":30,"props":269,"children":270},{},[271],{"type":34,"value":272},"Neither choice eliminates modeling. Relational databases make more of the model explicit in tables, constraints, and migrations. Document databases can make some shapes easier to represent, while asking the application to take more responsibility for relationships and consistency. The right choice is the one whose likely changes and failure modes your team can explain and operate.",{"type":25,"tag":47,"props":274,"children":276},{"id":275},"conclusion",[277],{"type":34,"value":278},"Conclusion",{"type":25,"tag":30,"props":280,"children":281},{},[282],{"type":34,"value":283},"My preference for PostgreSQL comes from the kinds of problems I would rather handle deliberately: defining relationships, evolving a schema with migrations, and letting the database enforce invariants. MongoDB has been useful when a document matches the work the application actually performs, but its flexibility does not make modeling or evolution disappear.",{"type":25,"tag":30,"props":285,"children":286},{},[287],{"type":34,"value":288},"The database decision is not a referendum on SQL or NoSQL. It is a decision about where you want the complexity to live. Start with the shape and lifecycle of the data, make the expected pain visible, and choose the model your team can keep coherent as the product grows.",{"type":25,"tag":47,"props":290,"children":292},{"id":291},"further-reading",[293],{"type":34,"value":294},"Further Reading",{"type":25,"tag":296,"props":297,"children":298},"ul",{},[299,310,320,330,341],{"type":25,"tag":300,"props":301,"children":302},"li",{},[303,309],{"type":25,"tag":72,"props":304,"children":306},{"href":74,"rel":305},[76],[307],{"type":34,"value":308},"PostgreSQL: Constraints",{"type":34,"value":204},{"type":25,"tag":300,"props":311,"children":312},{},[313,319],{"type":25,"tag":72,"props":314,"children":316},{"href":198,"rel":315},[76],[317],{"type":34,"value":318},"PostgreSQL: Trigger Behavior",{"type":34,"value":204},{"type":25,"tag":300,"props":321,"children":322},{},[323,329],{"type":25,"tag":72,"props":324,"children":326},{"href":168,"rel":325},[76],[327],{"type":34,"value":328},"PostgreSQL: JSON Types",{"type":34,"value":204},{"type":25,"tag":300,"props":331,"children":332},{},[333,340],{"type":25,"tag":72,"props":334,"children":337},{"href":335,"rel":336},"https://www.mongodb.com/docs/manual/changeStreams/",[76],[338],{"type":34,"value":339},"MongoDB: Change Streams",{"type":34,"value":204},{"type":25,"tag":300,"props":342,"children":343},{},[344,350],{"type":25,"tag":72,"props":345,"children":347},{"href":217,"rel":346},[76],[348],{"type":34,"value":349},"Debezium: PostgreSQL Connector",{"type":34,"value":204},{"title":7,"searchDepth":352,"depth":352,"links":353},2,[354,355,356,357,358,359,360,361],{"id":49,"depth":352,"text":52},{"id":89,"depth":352,"text":92},{"id":123,"depth":352,"text":126},{"id":149,"depth":352,"text":152},{"id":182,"depth":352,"text":185},{"id":247,"depth":352,"text":250},{"id":275,"depth":352,"text":278},{"id":291,"depth":352,"text":294},"markdown","content:posts:relational-vs-nosql-databases.md","content","posts/relational-vs-nosql-databases.md","posts/relational-vs-nosql-databases","md",[369,1089,2506],{"_path":370,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":371,"description":372,"layout":10,"date":373,"subtitle":374,"image":375,"optimized_image":375,"category":14,"tags":376,"author":20,"paginate":6,"body":380,"_type":362,"_id":1086,"_source":364,"_file":1087,"_stem":1088,"_extension":367},"/posts/hidden-cost-event-driven-architectures","The Hidden Cost of Event-Driven Architectures","A practical look at the operational and design trade-offs behind event-driven architectures, from observability and schema evolution to retries, ordering, and ownership.","2025-02-15T11:00:00.000Z","What asynchronous systems trade for decoupling","/assets/img/uploads/hidden-cost-event-driven-architectures.jpg",[14,19,377,378,379],"distributed-systems","event-driven","microservices",{"type":22,"children":381,"toc":1073},[382,390,395,400,405,411,416,429,434,439,445,450,505,510,538,543,548,554,559,564,569,770,775,780,786,791,796,801,824,829,835,840,845,850,878,883,889,894,899,904,943,948,954,959,964,969,975,980,985,990,996,1001,1006,1011,1015,1020,1025,1029,1067],{"type":25,"tag":26,"props":383,"children":384},{},[385],{"type":25,"tag":30,"props":386,"children":387},{},[388],{"type":34,"value":389},"Event-driven architecture does not remove coupling. It changes where coupling lives, how it fails, and who has to understand it when production is under pressure.",{"type":25,"tag":30,"props":391,"children":392},{},[393],{"type":34,"value":394},"Event-driven architectures are appealing for good reasons. A service can publish a fact without knowing every system that will react to it. New consumers can be added without changing the producer. Work can happen asynchronously, which can improve responsiveness and absorb bursts of traffic.",{"type":25,"tag":30,"props":396,"children":397},{},[398],{"type":34,"value":399},"That is the visible part of the trade. The less visible part appears once events become an important path for the business: a customer was charged, inventory changed, an account was activated, a shipment was delayed. At that point, the system is no longer a collection of independent services connected by a broker. It is a distributed workflow whose behavior is spread across topics, consumers, storage, retries, dashboards, and operational knowledge.",{"type":25,"tag":30,"props":401,"children":402},{},[403],{"type":34,"value":404},"The cost is not a reason to avoid events. It is a reason to adopt them deliberately.",{"type":25,"tag":47,"props":406,"children":408},{"id":407},"the-coupling-did-not-disappear",[409],{"type":34,"value":410},"The coupling did not disappear",{"type":25,"tag":30,"props":412,"children":413},{},[414],{"type":34,"value":415},"With a synchronous API, coupling is obvious. The caller knows the endpoint, the request shape, the response contract, and the fact that it must wait for an answer. This can be inconvenient, but it is easy to draw and relatively easy to trace.",{"type":25,"tag":30,"props":417,"children":418},{},[419,421,427],{"type":34,"value":420},"Events remove direct knowledge between the producer and consumer. A payment service can emit ",{"type":25,"tag":14,"props":422,"children":424},{"className":423},[],[425],{"type":34,"value":426},"PaymentCompleted",{"type":34,"value":428}," without calling an invoicing service. That is useful decoupling.",{"type":25,"tag":30,"props":430,"children":431},{},[432],{"type":34,"value":433},"However, the services are still coupled through a shared meaning. The invoice consumer must understand what a completed payment means, which fields are guaranteed, whether the event can arrive twice, and what happens if it arrives after another related event. The producer may not know the consumer exists, but both systems are now dependent on the same contract.",{"type":25,"tag":30,"props":435,"children":436},{},[437],{"type":34,"value":438},"This is semantic coupling. It is usually healthier than a web of direct calls, but it must be designed and owned. A topic name is not an architecture boundary by itself.",{"type":25,"tag":47,"props":440,"children":442},{"id":441},"a-simple-event-becomes-a-distributed-workflow",[443],{"type":34,"value":444},"A simple event becomes a distributed workflow",{"type":25,"tag":30,"props":446,"children":447},{},[448],{"type":34,"value":449},"Consider an order placement flow:",{"type":25,"tag":451,"props":452,"children":455},"pre",{"className":453,"code":454,"language":34,"meta":7,"style":7},"language-text shiki shiki-themes github-dark github-light","Order service -> OrderPlaced\nInventory service -> StockReserved\nPayment service -> PaymentAuthorized\nFulfillment service -> ShipmentRequested\nNotification service -> CustomerNotified\n",[456],{"type":25,"tag":14,"props":457,"children":458},{"__ignoreMap":7},[459,470,478,487,496],{"type":25,"tag":460,"props":461,"children":464},"span",{"class":462,"line":463},"line",1,[465],{"type":25,"tag":460,"props":466,"children":467},{},[468],{"type":34,"value":469},"Order service -> OrderPlaced\n",{"type":25,"tag":460,"props":471,"children":472},{"class":462,"line":352},[473],{"type":25,"tag":460,"props":474,"children":475},{},[476],{"type":34,"value":477},"Inventory service -> StockReserved\n",{"type":25,"tag":460,"props":479,"children":481},{"class":462,"line":480},3,[482],{"type":25,"tag":460,"props":483,"children":484},{},[485],{"type":34,"value":486},"Payment service -> PaymentAuthorized\n",{"type":25,"tag":460,"props":488,"children":490},{"class":462,"line":489},4,[491],{"type":25,"tag":460,"props":492,"children":493},{},[494],{"type":34,"value":495},"Fulfillment service -> ShipmentRequested\n",{"type":25,"tag":460,"props":497,"children":499},{"class":462,"line":498},5,[500],{"type":25,"tag":460,"props":501,"children":502},{},[503],{"type":34,"value":504},"Notification service -> CustomerNotified\n",{"type":25,"tag":30,"props":506,"children":507},{},[508],{"type":34,"value":509},"The diagram looks pleasantly modular. In reality, every arrow creates questions:",{"type":25,"tag":296,"props":511,"children":512},{},[513,518,523,528,533],{"type":25,"tag":300,"props":514,"children":515},{},[516],{"type":34,"value":517},"What happens when stock is reserved but payment is declined?",{"type":25,"tag":300,"props":519,"children":520},{},[521],{"type":34,"value":522},"Can a shipment request be processed before the payment authorization is visible?",{"type":25,"tag":300,"props":524,"children":525},{},[526],{"type":34,"value":527},"How long may the customer wait before the order status becomes accurate?",{"type":25,"tag":300,"props":529,"children":530},{},[531],{"type":34,"value":532},"Which service compensates an earlier action when a later one fails?",{"type":25,"tag":300,"props":534,"children":535},{},[536],{"type":34,"value":537},"Where does support look when an order is stuck between two steps?",{"type":25,"tag":30,"props":539,"children":540},{},[541],{"type":34,"value":542},"The workflow has become asynchronous and distributed. There is no single transaction manager, no single stack trace, and often no single database that can answer what happened. The architecture has moved coordination out of a request handler and into the behavior of many independent processes.",{"type":25,"tag":30,"props":544,"children":545},{},[546],{"type":34,"value":547},"That can be exactly the right decision. It is also real complexity, not an implementation detail to be hidden behind a message broker.",{"type":25,"tag":47,"props":549,"children":551},{"id":550},"delivery-guarantees-are-an-application-concern",[552],{"type":34,"value":553},"Delivery guarantees are an application concern",{"type":25,"tag":30,"props":555,"children":556},{},[557],{"type":34,"value":558},"Teams often describe a broker as providing \"exactly once\" processing. In practice, that statement is rarely enough to make a business action exactly once.",{"type":25,"tag":30,"props":560,"children":561},{},[562],{"type":34,"value":563},"A consumer can receive a message, write to its database, and crash before acknowledging it. The broker will deliver the message again. This is the correct behavior for at-least-once delivery, but the consumer must be prepared for it.",{"type":25,"tag":30,"props":565,"children":566},{},[567],{"type":34,"value":568},"An idempotent consumer treats repeated delivery as harmless. For example, it can store the event identifier alongside the resulting state change and reject a duplicate in the same database transaction:",{"type":25,"tag":451,"props":570,"children":574},{"className":571,"code":572,"language":573,"meta":7,"style":7},"language-java shiki shiki-themes github-dark github-light","@Transactional\npublic void handle(PaymentCompleted event) {\n    if (processedEvents.existsById(event.id())) {\n        return;\n    }\n\n    invoiceRepository.markPaid(event.orderId(), event.completedAt());\n    processedEvents.save(new ProcessedEvent(event.id()));\n}\n","java",[575],{"type":25,"tag":14,"props":576,"children":577},{"__ignoreMap":7},[578,593,617,650,663,671,681,719,761],{"type":25,"tag":460,"props":579,"children":580},{"class":462,"line":463},[581,587],{"type":25,"tag":460,"props":582,"children":584},{"style":583},"--shiki-default:#E1E4E8;--shiki-light:#24292E",[585],{"type":34,"value":586},"@",{"type":25,"tag":460,"props":588,"children":590},{"style":589},"--shiki-default:#F97583;--shiki-light:#D73A49",[591],{"type":34,"value":592},"Transactional\n",{"type":25,"tag":460,"props":594,"children":595},{"class":462,"line":352},[596,601,606,612],{"type":25,"tag":460,"props":597,"children":598},{"style":589},[599],{"type":34,"value":600},"public",{"type":25,"tag":460,"props":602,"children":603},{"style":589},[604],{"type":34,"value":605}," void",{"type":25,"tag":460,"props":607,"children":609},{"style":608},"--shiki-default:#B392F0;--shiki-light:#6F42C1",[610],{"type":34,"value":611}," handle",{"type":25,"tag":460,"props":613,"children":614},{"style":583},[615],{"type":34,"value":616},"(PaymentCompleted event) {\n",{"type":25,"tag":460,"props":618,"children":619},{"class":462,"line":480},[620,625,630,635,640,645],{"type":25,"tag":460,"props":621,"children":622},{"style":589},[623],{"type":34,"value":624},"    if",{"type":25,"tag":460,"props":626,"children":627},{"style":583},[628],{"type":34,"value":629}," (processedEvents.",{"type":25,"tag":460,"props":631,"children":632},{"style":608},[633],{"type":34,"value":634},"existsById",{"type":25,"tag":460,"props":636,"children":637},{"style":583},[638],{"type":34,"value":639},"(event.",{"type":25,"tag":460,"props":641,"children":642},{"style":608},[643],{"type":34,"value":644},"id",{"type":25,"tag":460,"props":646,"children":647},{"style":583},[648],{"type":34,"value":649},"())) {\n",{"type":25,"tag":460,"props":651,"children":652},{"class":462,"line":489},[653,658],{"type":25,"tag":460,"props":654,"children":655},{"style":589},[656],{"type":34,"value":657},"        return",{"type":25,"tag":460,"props":659,"children":660},{"style":583},[661],{"type":34,"value":662},";\n",{"type":25,"tag":460,"props":664,"children":665},{"class":462,"line":498},[666],{"type":25,"tag":460,"props":667,"children":668},{"style":583},[669],{"type":34,"value":670},"    }\n",{"type":25,"tag":460,"props":672,"children":674},{"class":462,"line":673},6,[675],{"type":25,"tag":460,"props":676,"children":678},{"emptyLinePlaceholder":677},true,[679],{"type":34,"value":680},"\n",{"type":25,"tag":460,"props":682,"children":684},{"class":462,"line":683},7,[685,690,695,699,704,709,714],{"type":25,"tag":460,"props":686,"children":687},{"style":583},[688],{"type":34,"value":689},"    invoiceRepository.",{"type":25,"tag":460,"props":691,"children":692},{"style":608},[693],{"type":34,"value":694},"markPaid",{"type":25,"tag":460,"props":696,"children":697},{"style":583},[698],{"type":34,"value":639},{"type":25,"tag":460,"props":700,"children":701},{"style":608},[702],{"type":34,"value":703},"orderId",{"type":25,"tag":460,"props":705,"children":706},{"style":583},[707],{"type":34,"value":708},"(), event.",{"type":25,"tag":460,"props":710,"children":711},{"style":608},[712],{"type":34,"value":713},"completedAt",{"type":25,"tag":460,"props":715,"children":716},{"style":583},[717],{"type":34,"value":718},"());\n",{"type":25,"tag":460,"props":720,"children":722},{"class":462,"line":721},8,[723,728,733,738,743,748,752,756],{"type":25,"tag":460,"props":724,"children":725},{"style":583},[726],{"type":34,"value":727},"    processedEvents.",{"type":25,"tag":460,"props":729,"children":730},{"style":608},[731],{"type":34,"value":732},"save",{"type":25,"tag":460,"props":734,"children":735},{"style":583},[736],{"type":34,"value":737},"(",{"type":25,"tag":460,"props":739,"children":740},{"style":589},[741],{"type":34,"value":742},"new",{"type":25,"tag":460,"props":744,"children":745},{"style":608},[746],{"type":34,"value":747}," ProcessedEvent",{"type":25,"tag":460,"props":749,"children":750},{"style":583},[751],{"type":34,"value":639},{"type":25,"tag":460,"props":753,"children":754},{"style":608},[755],{"type":34,"value":644},{"type":25,"tag":460,"props":757,"children":758},{"style":583},[759],{"type":34,"value":760},"()));\n",{"type":25,"tag":460,"props":762,"children":764},{"class":462,"line":763},9,[765],{"type":25,"tag":460,"props":766,"children":767},{"style":583},[768],{"type":34,"value":769},"}\n",{"type":25,"tag":30,"props":771,"children":772},{},[773],{"type":34,"value":774},"This example is intentionally small. In a real system, you also need to decide retention for processed identifiers, how the event key is chosen, and how an external side effect such as sending an email is made safe to repeat. A database transaction cannot automatically make an email provider, payment gateway, or third-party API idempotent.",{"type":25,"tag":30,"props":776,"children":777},{},[778],{"type":34,"value":779},"The important point is that reliable delivery changes application design. It is not something infrastructure can fully solve on its own.",{"type":25,"tag":47,"props":781,"children":783},{"id":782},"ordering-is-usually-narrower-than-expected",[784],{"type":34,"value":785},"Ordering is usually narrower than expected",{"type":25,"tag":30,"props":787,"children":788},{},[789],{"type":34,"value":790},"People often assume that events will be processed in the order in which they occurred. That may be true only within one partition, for one key, and while a consumer group is behaving normally. It is not a global promise.",{"type":25,"tag":30,"props":792,"children":793},{},[794],{"type":34,"value":795},"An account update and an account closure can be published seconds apart and still be observed in a different order by downstream systems. Retries can make an old event arrive after a newer one. A replay can introduce historical facts into a live stream.",{"type":25,"tag":30,"props":797,"children":798},{},[799],{"type":34,"value":800},"Consumers should therefore define the ordering they require rather than relying on the order they happen to observe. Common strategies include:",{"type":25,"tag":296,"props":802,"children":803},{},[804,809,814,819],{"type":25,"tag":300,"props":805,"children":806},{},[807],{"type":34,"value":808},"Partitioning by aggregate identifier when operations for one aggregate must be sequential.",{"type":25,"tag":300,"props":810,"children":811},{},[812],{"type":34,"value":813},"Including a version or sequence number in the event and ignoring stale updates.",{"type":25,"tag":300,"props":815,"children":816},{},[817],{"type":34,"value":818},"Making transitions explicit, so invalid state changes can be detected rather than silently accepted.",{"type":25,"tag":300,"props":820,"children":821},{},[822],{"type":34,"value":823},"Using a synchronous interaction when an immediate, authoritative answer is genuinely required.",{"type":25,"tag":30,"props":825,"children":826},{},[827],{"type":34,"value":828},"There is a useful design question here: does this operation need an ordered event stream, or does it need a transaction? Those are not interchangeable needs.",{"type":25,"tag":47,"props":830,"children":832},{"id":831},"schema-evolution-is-a-long-term-contract",[833],{"type":34,"value":834},"Schema evolution is a long-term contract",{"type":25,"tag":30,"props":836,"children":837},{},[838],{"type":34,"value":839},"An event is often treated as a private implementation detail because it is emitted by one service. Once another service consumes it, the event becomes a public contract, even if it never leaves the company.",{"type":25,"tag":30,"props":841,"children":842},{},[843],{"type":34,"value":844},"Adding a field is usually easy. Renaming a field, changing a value's meaning, changing a default, or splitting one event into two can break consumers in ways that are hard to spot before deployment. The producer may be healthy while a less frequently used consumer quietly falls behind.",{"type":25,"tag":30,"props":846,"children":847},{},[848],{"type":34,"value":849},"Schema management needs an explicit policy. At a minimum:",{"type":25,"tag":296,"props":851,"children":852},{},[853,858,863,868,873],{"type":25,"tag":300,"props":854,"children":855},{},[856],{"type":34,"value":857},"Define which changes are backward compatible.",{"type":25,"tag":300,"props":859,"children":860},{},[861],{"type":34,"value":862},"Version intentionally when meaning changes, not only when serialization fails.",{"type":25,"tag":300,"props":864,"children":865},{},[866],{"type":34,"value":867},"Keep events focused on facts that happened, rather than leaking a producer's internal data model.",{"type":25,"tag":300,"props":869,"children":870},{},[871],{"type":34,"value":872},"Test producer and consumer compatibility as part of delivery.",{"type":25,"tag":300,"props":874,"children":875},{},[876],{"type":34,"value":877},"Give consumers time and a migration path when a contract must change.",{"type":25,"tag":30,"props":879,"children":880},{},[881],{"type":34,"value":882},"The cost grows with the number of consumers. That is not an argument for hiding events; it is an argument for treating them with the same care as an API.",{"type":25,"tag":47,"props":884,"children":886},{"id":885},"observability-becomes-part-of-the-product",[887],{"type":34,"value":888},"Observability becomes part of the product",{"type":25,"tag":30,"props":890,"children":891},{},[892],{"type":34,"value":893},"In a synchronous request, a trace can often show a failure from one endpoint to another. In an event-driven workflow, the result may appear minutes later in a different process after several retries. Without good observability, engineers and support teams are left correlating timestamps across logs and guessing.",{"type":25,"tag":30,"props":895,"children":896},{},[897],{"type":34,"value":898},"Every business event should carry enough context to be traced without making it a dumping ground for internal state. A correlation identifier, causation identifier, aggregate identifier, event identifier, timestamp, and schema version are a strong starting point.",{"type":25,"tag":30,"props":900,"children":901},{},[902],{"type":34,"value":903},"The technical metadata matters, but the operational view matters just as much. A useful system can answer questions such as:",{"type":25,"tag":296,"props":905,"children":906},{},[907,928,933,938],{"type":25,"tag":300,"props":908,"children":909},{},[910,912,918,920,926],{"type":34,"value":911},"How many ",{"type":25,"tag":14,"props":913,"children":915},{"className":914},[],[916],{"type":34,"value":917},"OrderPlaced",{"type":34,"value":919}," events have not produced a ",{"type":25,"tag":14,"props":921,"children":923},{"className":922},[],[924],{"type":34,"value":925},"ShipmentRequested",{"type":34,"value":927}," event within the expected time?",{"type":25,"tag":300,"props":929,"children":930},{},[931],{"type":34,"value":932},"Which consumer is retrying and why?",{"type":25,"tag":300,"props":934,"children":935},{},[936],{"type":34,"value":937},"Can a failed message be replayed safely after a fix?",{"type":25,"tag":300,"props":939,"children":940},{},[941],{"type":34,"value":942},"Which customer-facing state should support trust while the workflow is still in progress?",{"type":25,"tag":30,"props":944,"children":945},{},[946],{"type":34,"value":947},"If the answer depends on manually inspecting a broker console, the architecture is still incomplete.",{"type":25,"tag":47,"props":949,"children":951},{"id":950},"retries-need-a-destination",[952],{"type":34,"value":953},"Retries need a destination",{"type":25,"tag":30,"props":955,"children":956},{},[957],{"type":34,"value":958},"Retries are necessary because networks fail, dependencies become unavailable, and deployments interrupt work. An unbounded retry, though, turns a temporary problem into invisible queue growth or a permanent loop.",{"type":25,"tag":30,"props":960,"children":961},{},[962],{"type":34,"value":963},"A consumer should distinguish between failures that may succeed later and failures that require intervention. Transient failures can use bounded retries with backoff. Invalid payloads, missing invariants, and incompatible schemas usually need to be sent to a dead-letter path with enough context to diagnose and recover them.",{"type":25,"tag":30,"props":965,"children":966},{},[967],{"type":34,"value":968},"Recovery is a product decision as well as an engineering decision. Replay can be dangerous if handlers are not idempotent or if an event triggers external actions. A dead-letter queue is useful only when someone owns the process for inspecting, correcting, replaying, or deliberately discarding its messages.",{"type":25,"tag":47,"props":970,"children":972},{"id":971},"ownership-must-be-visible",[973],{"type":34,"value":974},"Ownership must be visible",{"type":25,"tag":30,"props":976,"children":977},{},[978],{"type":34,"value":979},"The cost of asynchronous systems is often paid by the team on call rather than the team that introduced the topic. Ownership should therefore be explicit.",{"type":25,"tag":30,"props":981,"children":982},{},[983],{"type":34,"value":984},"For each event, make it clear who owns the contract, how consumers discover changes, who maintains the schema, what service owns the business state, and who investigates a workflow that stops progressing. Shared infrastructure teams can provide a reliable platform, but they cannot decide the business meaning of an event or repair every broken consumer relationship.",{"type":25,"tag":30,"props":986,"children":987},{},[988],{"type":34,"value":989},"This is also where event catalogs, runbooks, dashboards, and service-level objectives earn their place. They are not bureaucracy around the architecture. They are the tools that make an asynchronous system understandable when its normal flow has already diverged.",{"type":25,"tag":47,"props":991,"children":993},{"id":992},"when-events-are-worth-the-cost",[994],{"type":34,"value":995},"When events are worth the cost",{"type":25,"tag":30,"props":997,"children":998},{},[999],{"type":34,"value":1000},"Events are a strong fit when a fact should trigger independent reactions, when work can complete asynchronously, when the system benefits from absorbing bursts, or when the domain naturally produces a useful history of changes.",{"type":25,"tag":30,"props":1002,"children":1003},{},[1004],{"type":34,"value":1005},"They are a weaker fit when a caller needs an immediate decision, a small system only has one downstream action, or the team cannot yet support contract ownership and operational visibility. A direct call with a clear timeout and a well-defined failure response can be simpler and more reliable than a premature event pipeline.",{"type":25,"tag":30,"props":1007,"children":1008},{},[1009],{"type":34,"value":1010},"The best systems often use both. Synchronous communication establishes an immediate command or query boundary; events distribute facts that other parts of the system can process independently.",{"type":25,"tag":47,"props":1012,"children":1013},{"id":275},[1014],{"type":34,"value":278},{"type":25,"tag":30,"props":1016,"children":1017},{},[1018],{"type":34,"value":1019},"Event-driven architecture buys flexibility by moving coordination into contracts, consumers, and operations. The hidden cost is not the broker. It is the discipline required to make asynchronous workflows observable, recoverable, evolvable, and owned.",{"type":25,"tag":30,"props":1021,"children":1022},{},[1023],{"type":34,"value":1024},"When that discipline is present, events can make a system more resilient and easier to extend. When it is absent, they can turn straightforward business flows into a set of delayed surprises. Design the operational model at the same time as the event model, and the trade becomes a deliberate one.",{"type":25,"tag":47,"props":1026,"children":1027},{"id":291},[1028],{"type":34,"value":294},{"type":25,"tag":296,"props":1030,"children":1031},{},[1032,1044,1056],{"type":25,"tag":300,"props":1033,"children":1034},{},[1035,1042],{"type":25,"tag":72,"props":1036,"children":1039},{"href":1037,"rel":1038},"https://dataintensive.net/",[76],[1040],{"type":34,"value":1041},"Designing Data-Intensive Applications",{"type":34,"value":1043},", Martin Kleppmann.",{"type":25,"tag":300,"props":1045,"children":1046},{},[1047,1054],{"type":25,"tag":72,"props":1048,"children":1051},{"href":1049,"rel":1050},"https://microservices.io/patterns/data/transactional-outbox.html",[76],[1052],{"type":34,"value":1053},"The Transactional Outbox Pattern",{"type":34,"value":1055},", Chris Richardson.",{"type":25,"tag":300,"props":1057,"children":1058},{},[1059,1066],{"type":25,"tag":72,"props":1060,"children":1063},{"href":1061,"rel":1062},"https://kafka.apache.org/documentation/#design",[76],[1064],{"type":34,"value":1065},"Apache Kafka documentation: Design",{"type":34,"value":204},{"type":25,"tag":1068,"props":1069,"children":1070},"style",{},[1071],{"type":34,"value":1072},"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 .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);}",{"title":7,"searchDepth":352,"depth":352,"links":1074},[1075,1076,1077,1078,1079,1080,1081,1082,1083,1084,1085],{"id":407,"depth":352,"text":410},{"id":441,"depth":352,"text":444},{"id":550,"depth":352,"text":553},{"id":782,"depth":352,"text":785},{"id":831,"depth":352,"text":834},{"id":885,"depth":352,"text":888},{"id":950,"depth":352,"text":953},{"id":971,"depth":352,"text":974},{"id":992,"depth":352,"text":995},{"id":275,"depth":352,"text":278},{"id":291,"depth":352,"text":294},"content:posts:hidden-cost-event-driven-architectures.md","posts/hidden-cost-event-driven-architectures.md","posts/hidden-cost-event-driven-architectures",{"_path":1090,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1091,"description":1092,"date":1093,"layout":10,"subtitle":1094,"image":1095,"optimized_image":1095,"category":14,"tags":1096,"author":20,"paginate":6,"body":1102,"_type":362,"_id":2503,"_source":364,"_file":2504,"_stem":2505,"_extension":367},"/posts/gpt-sql-integration","Implementa un analizador de consultas en lenguaje natural para bases de datos SQL con GPT","Aprende a implementar un sistema que convierte consultas en lenguaje natural en sentencias SQL usando OpenAI GPT, con un tutorial paso a paso y ejemplos prácticos.","2025-01-15T11:00:00.000Z","Transforma preguntas naturales en consultas SQL automatizadas","/assets/img/uploads/gpt-sql-integration.png",[14,1097,1098,1099,1100,1101],"gpt","openai","sql","ai","nlp",{"type":22,"children":1103,"toc":2492},[1104,1112,1118,1136,1142,1147,1190,1203,1219,1225,1230,1444,1450,1455,1791,1797,1802,2153,2159,2164,2223,2229,2234,2413,2419,2437,2443,2448,2455,2488],{"type":25,"tag":26,"props":1105,"children":1106},{},[1107],{"type":25,"tag":30,"props":1108,"children":1109},{},[1110],{"type":34,"value":1111},"Convertir preguntas naturales en consultas SQL de forma automática simplifica la interacción con bases de datos, permitiendo que usuarios sin conocimientos técnicos extraigan datos relevantes. En este tutorial, aprenderás a implementar esta solución utilizando la API de OpenAI GPT.",{"type":25,"tag":47,"props":1113,"children":1115},{"id":1114},"requisitos-previos",[1116],{"type":34,"value":1117},"Requisitos Previos",{"type":25,"tag":296,"props":1119,"children":1120},{},[1121,1126,1131],{"type":25,"tag":300,"props":1122,"children":1123},{},[1124],{"type":34,"value":1125},"Cuenta y clave API de OpenAI",{"type":25,"tag":300,"props":1127,"children":1128},{},[1129],{"type":34,"value":1130},"Python instalado en tu entorno",{"type":25,"tag":300,"props":1132,"children":1133},{},[1134],{"type":34,"value":1135},"Base de datos SQL (ej.: PostgreSQL, MySQL)",{"type":25,"tag":47,"props":1137,"children":1139},{"id":1138},"paso-1-configuración-del-entorno",[1140],{"type":34,"value":1141},"Paso 1: Configuración del entorno",{"type":25,"tag":30,"props":1143,"children":1144},{},[1145],{"type":34,"value":1146},"Primero, instala las dependencias necesarias:",{"type":25,"tag":451,"props":1148,"children":1152},{"className":1149,"code":1150,"language":1151,"meta":7,"style":7},"language-bash shiki shiki-themes github-dark github-light","pip install openai python-dotenv sqlalchemy psycopg2-binary\n","bash",[1153],{"type":25,"tag":14,"props":1154,"children":1155},{"__ignoreMap":7},[1156],{"type":25,"tag":460,"props":1157,"children":1158},{"class":462,"line":463},[1159,1164,1170,1175,1180,1185],{"type":25,"tag":460,"props":1160,"children":1161},{"style":608},[1162],{"type":34,"value":1163},"pip",{"type":25,"tag":460,"props":1165,"children":1167},{"style":1166},"--shiki-default:#9ECBFF;--shiki-light:#032F62",[1168],{"type":34,"value":1169}," install",{"type":25,"tag":460,"props":1171,"children":1172},{"style":1166},[1173],{"type":34,"value":1174}," openai",{"type":25,"tag":460,"props":1176,"children":1177},{"style":1166},[1178],{"type":34,"value":1179}," python-dotenv",{"type":25,"tag":460,"props":1181,"children":1182},{"style":1166},[1183],{"type":34,"value":1184}," sqlalchemy",{"type":25,"tag":460,"props":1186,"children":1187},{"style":1166},[1188],{"type":34,"value":1189}," psycopg2-binary\n",{"type":25,"tag":30,"props":1191,"children":1192},{},[1193,1195,1201],{"type":34,"value":1194},"Guarda tu clave API en un archivo ",{"type":25,"tag":14,"props":1196,"children":1198},{"className":1197},[],[1199],{"type":34,"value":1200},".env",{"type":34,"value":1202},":",{"type":25,"tag":451,"props":1204,"children":1208},{"className":1205,"code":1206,"language":1207,"meta":7,"style":7},"language-env shiki shiki-themes github-dark github-light","OPENAI_API_KEY='tu-api-key'\n","env",[1209],{"type":25,"tag":14,"props":1210,"children":1211},{"__ignoreMap":7},[1212],{"type":25,"tag":460,"props":1213,"children":1214},{"class":462,"line":463},[1215],{"type":25,"tag":460,"props":1216,"children":1217},{},[1218],{"type":34,"value":1206},{"type":25,"tag":47,"props":1220,"children":1222},{"id":1221},"paso-2-conexión-a-la-base-de-datos",[1223],{"type":34,"value":1224},"Paso 2: Conexión a la base de datos",{"type":25,"tag":30,"props":1226,"children":1227},{},[1228],{"type":34,"value":1229},"Utiliza SQLAlchemy para conectarte a tu base de datos:",{"type":25,"tag":451,"props":1231,"children":1235},{"className":1232,"code":1233,"language":1234,"meta":7,"style":7},"language-python shiki shiki-themes github-dark github-light","from sqlalchemy import create_engine\nfrom sqlalchemy.orm import sessionmaker\nimport os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\nDATABASE_URL = os.getenv(\"DATABASE_URL\")\nengine = create_engine(DATABASE_URL)\nSession = sessionmaker(bind=engine)\nsession = Session()\n","python",[1236],{"type":25,"tag":14,"props":1237,"children":1238},{"__ignoreMap":7},[1239,1262,1283,1295,1316,1323,1331,1338,1367,1393,1426],{"type":25,"tag":460,"props":1240,"children":1241},{"class":462,"line":463},[1242,1247,1252,1257],{"type":25,"tag":460,"props":1243,"children":1244},{"style":589},[1245],{"type":34,"value":1246},"from",{"type":25,"tag":460,"props":1248,"children":1249},{"style":583},[1250],{"type":34,"value":1251}," sqlalchemy ",{"type":25,"tag":460,"props":1253,"children":1254},{"style":589},[1255],{"type":34,"value":1256},"import",{"type":25,"tag":460,"props":1258,"children":1259},{"style":583},[1260],{"type":34,"value":1261}," create_engine\n",{"type":25,"tag":460,"props":1263,"children":1264},{"class":462,"line":352},[1265,1269,1274,1278],{"type":25,"tag":460,"props":1266,"children":1267},{"style":589},[1268],{"type":34,"value":1246},{"type":25,"tag":460,"props":1270,"children":1271},{"style":583},[1272],{"type":34,"value":1273}," sqlalchemy.orm ",{"type":25,"tag":460,"props":1275,"children":1276},{"style":589},[1277],{"type":34,"value":1256},{"type":25,"tag":460,"props":1279,"children":1280},{"style":583},[1281],{"type":34,"value":1282}," sessionmaker\n",{"type":25,"tag":460,"props":1284,"children":1285},{"class":462,"line":480},[1286,1290],{"type":25,"tag":460,"props":1287,"children":1288},{"style":589},[1289],{"type":34,"value":1256},{"type":25,"tag":460,"props":1291,"children":1292},{"style":583},[1293],{"type":34,"value":1294}," os\n",{"type":25,"tag":460,"props":1296,"children":1297},{"class":462,"line":489},[1298,1302,1307,1311],{"type":25,"tag":460,"props":1299,"children":1300},{"style":589},[1301],{"type":34,"value":1246},{"type":25,"tag":460,"props":1303,"children":1304},{"style":583},[1305],{"type":34,"value":1306}," dotenv ",{"type":25,"tag":460,"props":1308,"children":1309},{"style":589},[1310],{"type":34,"value":1256},{"type":25,"tag":460,"props":1312,"children":1313},{"style":583},[1314],{"type":34,"value":1315}," load_dotenv\n",{"type":25,"tag":460,"props":1317,"children":1318},{"class":462,"line":498},[1319],{"type":25,"tag":460,"props":1320,"children":1321},{"emptyLinePlaceholder":677},[1322],{"type":34,"value":680},{"type":25,"tag":460,"props":1324,"children":1325},{"class":462,"line":673},[1326],{"type":25,"tag":460,"props":1327,"children":1328},{"style":583},[1329],{"type":34,"value":1330},"load_dotenv()\n",{"type":25,"tag":460,"props":1332,"children":1333},{"class":462,"line":683},[1334],{"type":25,"tag":460,"props":1335,"children":1336},{"emptyLinePlaceholder":677},[1337],{"type":34,"value":680},{"type":25,"tag":460,"props":1339,"children":1340},{"class":462,"line":721},[1341,1347,1352,1357,1362],{"type":25,"tag":460,"props":1342,"children":1344},{"style":1343},"--shiki-default:#79B8FF;--shiki-light:#005CC5",[1345],{"type":34,"value":1346},"DATABASE_URL",{"type":25,"tag":460,"props":1348,"children":1349},{"style":589},[1350],{"type":34,"value":1351}," =",{"type":25,"tag":460,"props":1353,"children":1354},{"style":583},[1355],{"type":34,"value":1356}," os.getenv(",{"type":25,"tag":460,"props":1358,"children":1359},{"style":1166},[1360],{"type":34,"value":1361},"\"DATABASE_URL\"",{"type":25,"tag":460,"props":1363,"children":1364},{"style":583},[1365],{"type":34,"value":1366},")\n",{"type":25,"tag":460,"props":1368,"children":1369},{"class":462,"line":763},[1370,1375,1380,1385,1389],{"type":25,"tag":460,"props":1371,"children":1372},{"style":583},[1373],{"type":34,"value":1374},"engine ",{"type":25,"tag":460,"props":1376,"children":1377},{"style":589},[1378],{"type":34,"value":1379},"=",{"type":25,"tag":460,"props":1381,"children":1382},{"style":583},[1383],{"type":34,"value":1384}," create_engine(",{"type":25,"tag":460,"props":1386,"children":1387},{"style":1343},[1388],{"type":34,"value":1346},{"type":25,"tag":460,"props":1390,"children":1391},{"style":583},[1392],{"type":34,"value":1366},{"type":25,"tag":460,"props":1394,"children":1396},{"class":462,"line":1395},10,[1397,1402,1406,1411,1417,1421],{"type":25,"tag":460,"props":1398,"children":1399},{"style":583},[1400],{"type":34,"value":1401},"Session ",{"type":25,"tag":460,"props":1403,"children":1404},{"style":589},[1405],{"type":34,"value":1379},{"type":25,"tag":460,"props":1407,"children":1408},{"style":583},[1409],{"type":34,"value":1410}," sessionmaker(",{"type":25,"tag":460,"props":1412,"children":1414},{"style":1413},"--shiki-default:#FFAB70;--shiki-light:#E36209",[1415],{"type":34,"value":1416},"bind",{"type":25,"tag":460,"props":1418,"children":1419},{"style":589},[1420],{"type":34,"value":1379},{"type":25,"tag":460,"props":1422,"children":1423},{"style":583},[1424],{"type":34,"value":1425},"engine)\n",{"type":25,"tag":460,"props":1427,"children":1429},{"class":462,"line":1428},11,[1430,1435,1439],{"type":25,"tag":460,"props":1431,"children":1432},{"style":583},[1433],{"type":34,"value":1434},"session ",{"type":25,"tag":460,"props":1436,"children":1437},{"style":589},[1438],{"type":34,"value":1379},{"type":25,"tag":460,"props":1440,"children":1441},{"style":583},[1442],{"type":34,"value":1443}," Session()\n",{"type":25,"tag":47,"props":1445,"children":1447},{"id":1446},"paso-3-obtener-esquema-de-la-base-de-datos",[1448],{"type":34,"value":1449},"Paso 3: Obtener esquema de la base de datos",{"type":25,"tag":30,"props":1451,"children":1452},{},[1453],{"type":34,"value":1454},"Genera una descripción del esquema que será utilizada por GPT:",{"type":25,"tag":451,"props":1456,"children":1458},{"className":1232,"code":1457,"language":1234,"meta":7,"style":7},"from sqlalchemy import inspect\n\ndef get_database_schema(engine):\n    inspector = inspect(engine)\n    schema_description = \"\"\n    for table_name in inspector.get_table_names():\n        schema_description += f\"Table: {table_name}\\n\"\n        for column in inspector.get_columns(table_name):\n            schema_description += f\"- {column['name']} ({column['type']})\\n\"\n        schema_description += \"\\n\"\n    return schema_description\n\nschema_description = get_database_schema(engine)\nprint(schema_description)\n",[1459],{"type":25,"tag":14,"props":1460,"children":1461},{"__ignoreMap":7},[1462,1482,1489,1507,1524,1541,1564,1607,1629,1714,1738,1751,1759,1777],{"type":25,"tag":460,"props":1463,"children":1464},{"class":462,"line":463},[1465,1469,1473,1477],{"type":25,"tag":460,"props":1466,"children":1467},{"style":589},[1468],{"type":34,"value":1246},{"type":25,"tag":460,"props":1470,"children":1471},{"style":583},[1472],{"type":34,"value":1251},{"type":25,"tag":460,"props":1474,"children":1475},{"style":589},[1476],{"type":34,"value":1256},{"type":25,"tag":460,"props":1478,"children":1479},{"style":583},[1480],{"type":34,"value":1481}," inspect\n",{"type":25,"tag":460,"props":1483,"children":1484},{"class":462,"line":352},[1485],{"type":25,"tag":460,"props":1486,"children":1487},{"emptyLinePlaceholder":677},[1488],{"type":34,"value":680},{"type":25,"tag":460,"props":1490,"children":1491},{"class":462,"line":480},[1492,1497,1502],{"type":25,"tag":460,"props":1493,"children":1494},{"style":589},[1495],{"type":34,"value":1496},"def",{"type":25,"tag":460,"props":1498,"children":1499},{"style":608},[1500],{"type":34,"value":1501}," get_database_schema",{"type":25,"tag":460,"props":1503,"children":1504},{"style":583},[1505],{"type":34,"value":1506},"(engine):\n",{"type":25,"tag":460,"props":1508,"children":1509},{"class":462,"line":489},[1510,1515,1519],{"type":25,"tag":460,"props":1511,"children":1512},{"style":583},[1513],{"type":34,"value":1514},"    inspector ",{"type":25,"tag":460,"props":1516,"children":1517},{"style":589},[1518],{"type":34,"value":1379},{"type":25,"tag":460,"props":1520,"children":1521},{"style":583},[1522],{"type":34,"value":1523}," inspect(engine)\n",{"type":25,"tag":460,"props":1525,"children":1526},{"class":462,"line":498},[1527,1532,1536],{"type":25,"tag":460,"props":1528,"children":1529},{"style":583},[1530],{"type":34,"value":1531},"    schema_description ",{"type":25,"tag":460,"props":1533,"children":1534},{"style":589},[1535],{"type":34,"value":1379},{"type":25,"tag":460,"props":1537,"children":1538},{"style":1166},[1539],{"type":34,"value":1540}," \"\"\n",{"type":25,"tag":460,"props":1542,"children":1543},{"class":462,"line":673},[1544,1549,1554,1559],{"type":25,"tag":460,"props":1545,"children":1546},{"style":589},[1547],{"type":34,"value":1548},"    for",{"type":25,"tag":460,"props":1550,"children":1551},{"style":583},[1552],{"type":34,"value":1553}," table_name ",{"type":25,"tag":460,"props":1555,"children":1556},{"style":589},[1557],{"type":34,"value":1558},"in",{"type":25,"tag":460,"props":1560,"children":1561},{"style":583},[1562],{"type":34,"value":1563}," inspector.get_table_names():\n",{"type":25,"tag":460,"props":1565,"children":1566},{"class":462,"line":683},[1567,1572,1577,1582,1587,1592,1597,1602],{"type":25,"tag":460,"props":1568,"children":1569},{"style":583},[1570],{"type":34,"value":1571},"        schema_description ",{"type":25,"tag":460,"props":1573,"children":1574},{"style":589},[1575],{"type":34,"value":1576},"+=",{"type":25,"tag":460,"props":1578,"children":1579},{"style":589},[1580],{"type":34,"value":1581}," f",{"type":25,"tag":460,"props":1583,"children":1584},{"style":1166},[1585],{"type":34,"value":1586},"\"Table: ",{"type":25,"tag":460,"props":1588,"children":1589},{"style":1343},[1590],{"type":34,"value":1591},"{",{"type":25,"tag":460,"props":1593,"children":1594},{"style":583},[1595],{"type":34,"value":1596},"table_name",{"type":25,"tag":460,"props":1598,"children":1599},{"style":1343},[1600],{"type":34,"value":1601},"}\\n",{"type":25,"tag":460,"props":1603,"children":1604},{"style":1166},[1605],{"type":34,"value":1606},"\"\n",{"type":25,"tag":460,"props":1608,"children":1609},{"class":462,"line":721},[1610,1615,1620,1624],{"type":25,"tag":460,"props":1611,"children":1612},{"style":589},[1613],{"type":34,"value":1614},"        for",{"type":25,"tag":460,"props":1616,"children":1617},{"style":583},[1618],{"type":34,"value":1619}," column ",{"type":25,"tag":460,"props":1621,"children":1622},{"style":589},[1623],{"type":34,"value":1558},{"type":25,"tag":460,"props":1625,"children":1626},{"style":583},[1627],{"type":34,"value":1628}," inspector.get_columns(table_name):\n",{"type":25,"tag":460,"props":1630,"children":1631},{"class":462,"line":763},[1632,1637,1641,1645,1650,1654,1659,1664,1669,1674,1679,1683,1687,1692,1696,1700,1705,1710],{"type":25,"tag":460,"props":1633,"children":1634},{"style":583},[1635],{"type":34,"value":1636},"            schema_description ",{"type":25,"tag":460,"props":1638,"children":1639},{"style":589},[1640],{"type":34,"value":1576},{"type":25,"tag":460,"props":1642,"children":1643},{"style":589},[1644],{"type":34,"value":1581},{"type":25,"tag":460,"props":1646,"children":1647},{"style":1166},[1648],{"type":34,"value":1649},"\"- ",{"type":25,"tag":460,"props":1651,"children":1652},{"style":1343},[1653],{"type":34,"value":1591},{"type":25,"tag":460,"props":1655,"children":1656},{"style":583},[1657],{"type":34,"value":1658},"column[",{"type":25,"tag":460,"props":1660,"children":1661},{"style":1166},[1662],{"type":34,"value":1663},"'name'",{"type":25,"tag":460,"props":1665,"children":1666},{"style":583},[1667],{"type":34,"value":1668},"]",{"type":25,"tag":460,"props":1670,"children":1671},{"style":1343},[1672],{"type":34,"value":1673},"}",{"type":25,"tag":460,"props":1675,"children":1676},{"style":1166},[1677],{"type":34,"value":1678}," (",{"type":25,"tag":460,"props":1680,"children":1681},{"style":1343},[1682],{"type":34,"value":1591},{"type":25,"tag":460,"props":1684,"children":1685},{"style":583},[1686],{"type":34,"value":1658},{"type":25,"tag":460,"props":1688,"children":1689},{"style":1166},[1690],{"type":34,"value":1691},"'type'",{"type":25,"tag":460,"props":1693,"children":1694},{"style":583},[1695],{"type":34,"value":1668},{"type":25,"tag":460,"props":1697,"children":1698},{"style":1343},[1699],{"type":34,"value":1673},{"type":25,"tag":460,"props":1701,"children":1702},{"style":1166},[1703],{"type":34,"value":1704},")",{"type":25,"tag":460,"props":1706,"children":1707},{"style":1343},[1708],{"type":34,"value":1709},"\\n",{"type":25,"tag":460,"props":1711,"children":1712},{"style":1166},[1713],{"type":34,"value":1606},{"type":25,"tag":460,"props":1715,"children":1716},{"class":462,"line":1395},[1717,1721,1725,1730,1734],{"type":25,"tag":460,"props":1718,"children":1719},{"style":583},[1720],{"type":34,"value":1571},{"type":25,"tag":460,"props":1722,"children":1723},{"style":589},[1724],{"type":34,"value":1576},{"type":25,"tag":460,"props":1726,"children":1727},{"style":1166},[1728],{"type":34,"value":1729}," \"",{"type":25,"tag":460,"props":1731,"children":1732},{"style":1343},[1733],{"type":34,"value":1709},{"type":25,"tag":460,"props":1735,"children":1736},{"style":1166},[1737],{"type":34,"value":1606},{"type":25,"tag":460,"props":1739,"children":1740},{"class":462,"line":1428},[1741,1746],{"type":25,"tag":460,"props":1742,"children":1743},{"style":589},[1744],{"type":34,"value":1745},"    return",{"type":25,"tag":460,"props":1747,"children":1748},{"style":583},[1749],{"type":34,"value":1750}," schema_description\n",{"type":25,"tag":460,"props":1752,"children":1754},{"class":462,"line":1753},12,[1755],{"type":25,"tag":460,"props":1756,"children":1757},{"emptyLinePlaceholder":677},[1758],{"type":34,"value":680},{"type":25,"tag":460,"props":1760,"children":1762},{"class":462,"line":1761},13,[1763,1768,1772],{"type":25,"tag":460,"props":1764,"children":1765},{"style":583},[1766],{"type":34,"value":1767},"schema_description ",{"type":25,"tag":460,"props":1769,"children":1770},{"style":589},[1771],{"type":34,"value":1379},{"type":25,"tag":460,"props":1773,"children":1774},{"style":583},[1775],{"type":34,"value":1776}," get_database_schema(engine)\n",{"type":25,"tag":460,"props":1778,"children":1780},{"class":462,"line":1779},14,[1781,1786],{"type":25,"tag":460,"props":1782,"children":1783},{"style":1343},[1784],{"type":34,"value":1785},"print",{"type":25,"tag":460,"props":1787,"children":1788},{"style":583},[1789],{"type":34,"value":1790},"(schema_description)\n",{"type":25,"tag":47,"props":1792,"children":1794},{"id":1793},"paso-4-preparar-la-consulta-con-gpt",[1795],{"type":34,"value":1796},"Paso 4: Preparar la consulta con GPT",{"type":25,"tag":30,"props":1798,"children":1799},{},[1800],{"type":34,"value":1801},"Utiliza la API de OpenAI para transformar preguntas en consultas SQL, incluyendo el esquema de la base de datos:",{"type":25,"tag":451,"props":1803,"children":1805},{"className":1232,"code":1804,"language":1234,"meta":7,"style":7},"import openai\nimport os\n\nopenai.api_key = os.getenv(\"OPENAI_API_KEY\")\n\ndef generate_sql(question, schema):\n    response = openai.ChatCompletion.create(\n        model=\"gpt-4\",\n        messages=[\n            {\"role\": \"system\", \"content\": f\"Convierte preguntas en lenguaje natural a consultas SQL. Esquema de la base de datos:\\n{schema}\"},\n            {\"role\": \"user\", \"content\": question}\n        ]\n    )\n    sql_query = response.choices[0].message.content.strip()\n    return sql_query\n\nquery = generate_sql(\"¿Cuántos usuarios se registraron el último mes?\", schema_description)\nprint(query)\n",[1806],{"type":25,"tag":14,"props":1807,"children":1808},{"__ignoreMap":7},[1809,1821,1832,1839,1864,1871,1888,1905,1927,1944,2015,2048,2056,2064,2091,2104,2112,2140],{"type":25,"tag":460,"props":1810,"children":1811},{"class":462,"line":463},[1812,1816],{"type":25,"tag":460,"props":1813,"children":1814},{"style":589},[1815],{"type":34,"value":1256},{"type":25,"tag":460,"props":1817,"children":1818},{"style":583},[1819],{"type":34,"value":1820}," openai\n",{"type":25,"tag":460,"props":1822,"children":1823},{"class":462,"line":352},[1824,1828],{"type":25,"tag":460,"props":1825,"children":1826},{"style":589},[1827],{"type":34,"value":1256},{"type":25,"tag":460,"props":1829,"children":1830},{"style":583},[1831],{"type":34,"value":1294},{"type":25,"tag":460,"props":1833,"children":1834},{"class":462,"line":480},[1835],{"type":25,"tag":460,"props":1836,"children":1837},{"emptyLinePlaceholder":677},[1838],{"type":34,"value":680},{"type":25,"tag":460,"props":1840,"children":1841},{"class":462,"line":489},[1842,1847,1851,1855,1860],{"type":25,"tag":460,"props":1843,"children":1844},{"style":583},[1845],{"type":34,"value":1846},"openai.api_key ",{"type":25,"tag":460,"props":1848,"children":1849},{"style":589},[1850],{"type":34,"value":1379},{"type":25,"tag":460,"props":1852,"children":1853},{"style":583},[1854],{"type":34,"value":1356},{"type":25,"tag":460,"props":1856,"children":1857},{"style":1166},[1858],{"type":34,"value":1859},"\"OPENAI_API_KEY\"",{"type":25,"tag":460,"props":1861,"children":1862},{"style":583},[1863],{"type":34,"value":1366},{"type":25,"tag":460,"props":1865,"children":1866},{"class":462,"line":498},[1867],{"type":25,"tag":460,"props":1868,"children":1869},{"emptyLinePlaceholder":677},[1870],{"type":34,"value":680},{"type":25,"tag":460,"props":1872,"children":1873},{"class":462,"line":673},[1874,1878,1883],{"type":25,"tag":460,"props":1875,"children":1876},{"style":589},[1877],{"type":34,"value":1496},{"type":25,"tag":460,"props":1879,"children":1880},{"style":608},[1881],{"type":34,"value":1882}," generate_sql",{"type":25,"tag":460,"props":1884,"children":1885},{"style":583},[1886],{"type":34,"value":1887},"(question, schema):\n",{"type":25,"tag":460,"props":1889,"children":1890},{"class":462,"line":683},[1891,1896,1900],{"type":25,"tag":460,"props":1892,"children":1893},{"style":583},[1894],{"type":34,"value":1895},"    response ",{"type":25,"tag":460,"props":1897,"children":1898},{"style":589},[1899],{"type":34,"value":1379},{"type":25,"tag":460,"props":1901,"children":1902},{"style":583},[1903],{"type":34,"value":1904}," openai.ChatCompletion.create(\n",{"type":25,"tag":460,"props":1906,"children":1907},{"class":462,"line":721},[1908,1913,1917,1922],{"type":25,"tag":460,"props":1909,"children":1910},{"style":1413},[1911],{"type":34,"value":1912},"        model",{"type":25,"tag":460,"props":1914,"children":1915},{"style":589},[1916],{"type":34,"value":1379},{"type":25,"tag":460,"props":1918,"children":1919},{"style":1166},[1920],{"type":34,"value":1921},"\"gpt-4\"",{"type":25,"tag":460,"props":1923,"children":1924},{"style":583},[1925],{"type":34,"value":1926},",\n",{"type":25,"tag":460,"props":1928,"children":1929},{"class":462,"line":763},[1930,1935,1939],{"type":25,"tag":460,"props":1931,"children":1932},{"style":1413},[1933],{"type":34,"value":1934},"        messages",{"type":25,"tag":460,"props":1936,"children":1937},{"style":589},[1938],{"type":34,"value":1379},{"type":25,"tag":460,"props":1940,"children":1941},{"style":583},[1942],{"type":34,"value":1943},"[\n",{"type":25,"tag":460,"props":1945,"children":1946},{"class":462,"line":1395},[1947,1952,1957,1962,1967,1972,1977,1981,1986,1991,1996,2001,2005,2010],{"type":25,"tag":460,"props":1948,"children":1949},{"style":583},[1950],{"type":34,"value":1951},"            {",{"type":25,"tag":460,"props":1953,"children":1954},{"style":1166},[1955],{"type":34,"value":1956},"\"role\"",{"type":25,"tag":460,"props":1958,"children":1959},{"style":583},[1960],{"type":34,"value":1961},": ",{"type":25,"tag":460,"props":1963,"children":1964},{"style":1166},[1965],{"type":34,"value":1966},"\"system\"",{"type":25,"tag":460,"props":1968,"children":1969},{"style":583},[1970],{"type":34,"value":1971},", ",{"type":25,"tag":460,"props":1973,"children":1974},{"style":1166},[1975],{"type":34,"value":1976},"\"content\"",{"type":25,"tag":460,"props":1978,"children":1979},{"style":583},[1980],{"type":34,"value":1961},{"type":25,"tag":460,"props":1982,"children":1983},{"style":589},[1984],{"type":34,"value":1985},"f",{"type":25,"tag":460,"props":1987,"children":1988},{"style":1166},[1989],{"type":34,"value":1990},"\"Convierte preguntas en lenguaje natural a consultas SQL. Esquema de la base de datos:",{"type":25,"tag":460,"props":1992,"children":1993},{"style":1343},[1994],{"type":34,"value":1995},"\\n{",{"type":25,"tag":460,"props":1997,"children":1998},{"style":583},[1999],{"type":34,"value":2000},"schema",{"type":25,"tag":460,"props":2002,"children":2003},{"style":1343},[2004],{"type":34,"value":1673},{"type":25,"tag":460,"props":2006,"children":2007},{"style":1166},[2008],{"type":34,"value":2009},"\"",{"type":25,"tag":460,"props":2011,"children":2012},{"style":583},[2013],{"type":34,"value":2014},"},\n",{"type":25,"tag":460,"props":2016,"children":2017},{"class":462,"line":1428},[2018,2022,2026,2030,2035,2039,2043],{"type":25,"tag":460,"props":2019,"children":2020},{"style":583},[2021],{"type":34,"value":1951},{"type":25,"tag":460,"props":2023,"children":2024},{"style":1166},[2025],{"type":34,"value":1956},{"type":25,"tag":460,"props":2027,"children":2028},{"style":583},[2029],{"type":34,"value":1961},{"type":25,"tag":460,"props":2031,"children":2032},{"style":1166},[2033],{"type":34,"value":2034},"\"user\"",{"type":25,"tag":460,"props":2036,"children":2037},{"style":583},[2038],{"type":34,"value":1971},{"type":25,"tag":460,"props":2040,"children":2041},{"style":1166},[2042],{"type":34,"value":1976},{"type":25,"tag":460,"props":2044,"children":2045},{"style":583},[2046],{"type":34,"value":2047},": question}\n",{"type":25,"tag":460,"props":2049,"children":2050},{"class":462,"line":1753},[2051],{"type":25,"tag":460,"props":2052,"children":2053},{"style":583},[2054],{"type":34,"value":2055},"        ]\n",{"type":25,"tag":460,"props":2057,"children":2058},{"class":462,"line":1761},[2059],{"type":25,"tag":460,"props":2060,"children":2061},{"style":583},[2062],{"type":34,"value":2063},"    )\n",{"type":25,"tag":460,"props":2065,"children":2066},{"class":462,"line":1779},[2067,2072,2076,2081,2086],{"type":25,"tag":460,"props":2068,"children":2069},{"style":583},[2070],{"type":34,"value":2071},"    sql_query ",{"type":25,"tag":460,"props":2073,"children":2074},{"style":589},[2075],{"type":34,"value":1379},{"type":25,"tag":460,"props":2077,"children":2078},{"style":583},[2079],{"type":34,"value":2080}," response.choices[",{"type":25,"tag":460,"props":2082,"children":2083},{"style":1343},[2084],{"type":34,"value":2085},"0",{"type":25,"tag":460,"props":2087,"children":2088},{"style":583},[2089],{"type":34,"value":2090},"].message.content.strip()\n",{"type":25,"tag":460,"props":2092,"children":2094},{"class":462,"line":2093},15,[2095,2099],{"type":25,"tag":460,"props":2096,"children":2097},{"style":589},[2098],{"type":34,"value":1745},{"type":25,"tag":460,"props":2100,"children":2101},{"style":583},[2102],{"type":34,"value":2103}," sql_query\n",{"type":25,"tag":460,"props":2105,"children":2107},{"class":462,"line":2106},16,[2108],{"type":25,"tag":460,"props":2109,"children":2110},{"emptyLinePlaceholder":677},[2111],{"type":34,"value":680},{"type":25,"tag":460,"props":2113,"children":2115},{"class":462,"line":2114},17,[2116,2121,2125,2130,2135],{"type":25,"tag":460,"props":2117,"children":2118},{"style":583},[2119],{"type":34,"value":2120},"query ",{"type":25,"tag":460,"props":2122,"children":2123},{"style":589},[2124],{"type":34,"value":1379},{"type":25,"tag":460,"props":2126,"children":2127},{"style":583},[2128],{"type":34,"value":2129}," generate_sql(",{"type":25,"tag":460,"props":2131,"children":2132},{"style":1166},[2133],{"type":34,"value":2134},"\"¿Cuántos usuarios se registraron el último mes?\"",{"type":25,"tag":460,"props":2136,"children":2137},{"style":583},[2138],{"type":34,"value":2139},", schema_description)\n",{"type":25,"tag":460,"props":2141,"children":2143},{"class":462,"line":2142},18,[2144,2148],{"type":25,"tag":460,"props":2145,"children":2146},{"style":1343},[2147],{"type":34,"value":1785},{"type":25,"tag":460,"props":2149,"children":2150},{"style":583},[2151],{"type":34,"value":2152},"(query)\n",{"type":25,"tag":47,"props":2154,"children":2156},{"id":2155},"paso-5-ejecutar-consultas-generadas",[2157],{"type":34,"value":2158},"Paso 5: Ejecutar consultas generadas",{"type":25,"tag":30,"props":2160,"children":2161},{},[2162],{"type":34,"value":2163},"Ejecuta la consulta generada y obtiene resultados:",{"type":25,"tag":451,"props":2165,"children":2167},{"className":1232,"code":2166,"language":1234,"meta":7,"style":7},"result = session.execute(query)\nfor row in result:\n    print(row)\n",[2168],{"type":25,"tag":14,"props":2169,"children":2170},{"__ignoreMap":7},[2171,2188,2210],{"type":25,"tag":460,"props":2172,"children":2173},{"class":462,"line":463},[2174,2179,2183],{"type":25,"tag":460,"props":2175,"children":2176},{"style":583},[2177],{"type":34,"value":2178},"result ",{"type":25,"tag":460,"props":2180,"children":2181},{"style":589},[2182],{"type":34,"value":1379},{"type":25,"tag":460,"props":2184,"children":2185},{"style":583},[2186],{"type":34,"value":2187}," session.execute(query)\n",{"type":25,"tag":460,"props":2189,"children":2190},{"class":462,"line":352},[2191,2196,2201,2205],{"type":25,"tag":460,"props":2192,"children":2193},{"style":589},[2194],{"type":34,"value":2195},"for",{"type":25,"tag":460,"props":2197,"children":2198},{"style":583},[2199],{"type":34,"value":2200}," row ",{"type":25,"tag":460,"props":2202,"children":2203},{"style":589},[2204],{"type":34,"value":1558},{"type":25,"tag":460,"props":2206,"children":2207},{"style":583},[2208],{"type":34,"value":2209}," result:\n",{"type":25,"tag":460,"props":2211,"children":2212},{"class":462,"line":480},[2213,2218],{"type":25,"tag":460,"props":2214,"children":2215},{"style":1343},[2216],{"type":34,"value":2217},"    print",{"type":25,"tag":460,"props":2219,"children":2220},{"style":583},[2221],{"type":34,"value":2222},"(row)\n",{"type":25,"tag":47,"props":2224,"children":2226},{"id":2225},"paso-6-manejo-de-errores-y-seguridad",[2227],{"type":34,"value":2228},"Paso 6: Manejo de errores y seguridad",{"type":25,"tag":30,"props":2230,"children":2231},{},[2232],{"type":34,"value":2233},"Para evitar problemas con consultas inseguras o errores, siempre valida las consultas generadas por GPT antes de ejecutarlas directamente:",{"type":25,"tag":451,"props":2235,"children":2237},{"className":1232,"code":2236,"language":1234,"meta":7,"style":7},"def execute_safe_query(sql_query):\n    try:\n        result = session.execute(sql_query)\n        return result.fetchall()\n    except Exception as e:\n        print(f\"Error al ejecutar consulta: {e}\")\n        return None\n\nresults = execute_safe_query(query)\nprint(results)\n",[2238],{"type":25,"tag":14,"props":2239,"children":2240},{"__ignoreMap":7},[2241,2258,2271,2288,2300,2323,2365,2377,2384,2401],{"type":25,"tag":460,"props":2242,"children":2243},{"class":462,"line":463},[2244,2248,2253],{"type":25,"tag":460,"props":2245,"children":2246},{"style":589},[2247],{"type":34,"value":1496},{"type":25,"tag":460,"props":2249,"children":2250},{"style":608},[2251],{"type":34,"value":2252}," execute_safe_query",{"type":25,"tag":460,"props":2254,"children":2255},{"style":583},[2256],{"type":34,"value":2257},"(sql_query):\n",{"type":25,"tag":460,"props":2259,"children":2260},{"class":462,"line":352},[2261,2266],{"type":25,"tag":460,"props":2262,"children":2263},{"style":589},[2264],{"type":34,"value":2265},"    try",{"type":25,"tag":460,"props":2267,"children":2268},{"style":583},[2269],{"type":34,"value":2270},":\n",{"type":25,"tag":460,"props":2272,"children":2273},{"class":462,"line":480},[2274,2279,2283],{"type":25,"tag":460,"props":2275,"children":2276},{"style":583},[2277],{"type":34,"value":2278},"        result ",{"type":25,"tag":460,"props":2280,"children":2281},{"style":589},[2282],{"type":34,"value":1379},{"type":25,"tag":460,"props":2284,"children":2285},{"style":583},[2286],{"type":34,"value":2287}," session.execute(sql_query)\n",{"type":25,"tag":460,"props":2289,"children":2290},{"class":462,"line":489},[2291,2295],{"type":25,"tag":460,"props":2292,"children":2293},{"style":589},[2294],{"type":34,"value":657},{"type":25,"tag":460,"props":2296,"children":2297},{"style":583},[2298],{"type":34,"value":2299}," result.fetchall()\n",{"type":25,"tag":460,"props":2301,"children":2302},{"class":462,"line":498},[2303,2308,2313,2318],{"type":25,"tag":460,"props":2304,"children":2305},{"style":589},[2306],{"type":34,"value":2307},"    except",{"type":25,"tag":460,"props":2309,"children":2310},{"style":1343},[2311],{"type":34,"value":2312}," Exception",{"type":25,"tag":460,"props":2314,"children":2315},{"style":589},[2316],{"type":34,"value":2317}," as",{"type":25,"tag":460,"props":2319,"children":2320},{"style":583},[2321],{"type":34,"value":2322}," e:\n",{"type":25,"tag":460,"props":2324,"children":2325},{"class":462,"line":673},[2326,2331,2335,2339,2344,2348,2353,2357,2361],{"type":25,"tag":460,"props":2327,"children":2328},{"style":1343},[2329],{"type":34,"value":2330},"        print",{"type":25,"tag":460,"props":2332,"children":2333},{"style":583},[2334],{"type":34,"value":737},{"type":25,"tag":460,"props":2336,"children":2337},{"style":589},[2338],{"type":34,"value":1985},{"type":25,"tag":460,"props":2340,"children":2341},{"style":1166},[2342],{"type":34,"value":2343},"\"Error al ejecutar consulta: ",{"type":25,"tag":460,"props":2345,"children":2346},{"style":1343},[2347],{"type":34,"value":1591},{"type":25,"tag":460,"props":2349,"children":2350},{"style":583},[2351],{"type":34,"value":2352},"e",{"type":25,"tag":460,"props":2354,"children":2355},{"style":1343},[2356],{"type":34,"value":1673},{"type":25,"tag":460,"props":2358,"children":2359},{"style":1166},[2360],{"type":34,"value":2009},{"type":25,"tag":460,"props":2362,"children":2363},{"style":583},[2364],{"type":34,"value":1366},{"type":25,"tag":460,"props":2366,"children":2367},{"class":462,"line":683},[2368,2372],{"type":25,"tag":460,"props":2369,"children":2370},{"style":589},[2371],{"type":34,"value":657},{"type":25,"tag":460,"props":2373,"children":2374},{"style":1343},[2375],{"type":34,"value":2376}," None\n",{"type":25,"tag":460,"props":2378,"children":2379},{"class":462,"line":721},[2380],{"type":25,"tag":460,"props":2381,"children":2382},{"emptyLinePlaceholder":677},[2383],{"type":34,"value":680},{"type":25,"tag":460,"props":2385,"children":2386},{"class":462,"line":763},[2387,2392,2396],{"type":25,"tag":460,"props":2388,"children":2389},{"style":583},[2390],{"type":34,"value":2391},"results ",{"type":25,"tag":460,"props":2393,"children":2394},{"style":589},[2395],{"type":34,"value":1379},{"type":25,"tag":460,"props":2397,"children":2398},{"style":583},[2399],{"type":34,"value":2400}," execute_safe_query(query)\n",{"type":25,"tag":460,"props":2402,"children":2403},{"class":462,"line":1395},[2404,2408],{"type":25,"tag":460,"props":2405,"children":2406},{"style":1343},[2407],{"type":34,"value":1785},{"type":25,"tag":460,"props":2409,"children":2410},{"style":583},[2411],{"type":34,"value":2412},"(results)\n",{"type":25,"tag":47,"props":2414,"children":2416},{"id":2415},"buenas-prácticas",[2417],{"type":34,"value":2418},"Buenas prácticas",{"type":25,"tag":296,"props":2420,"children":2421},{},[2422,2427,2432],{"type":25,"tag":300,"props":2423,"children":2424},{},[2425],{"type":34,"value":2426},"Siempre revisa o limita el acceso de los usuarios a consultas críticas o sensibles.",{"type":25,"tag":300,"props":2428,"children":2429},{},[2430],{"type":34,"value":2431},"Implementa validaciones adicionales para asegurar la integridad y seguridad de tu base de datos.",{"type":25,"tag":300,"props":2433,"children":2434},{},[2435],{"type":34,"value":2436},"Utiliza tokens y modelos específicos de OpenAI optimizados para tareas SQL.",{"type":25,"tag":47,"props":2438,"children":2440},{"id":2439},"conclusión",[2441],{"type":34,"value":2442},"Conclusión",{"type":25,"tag":30,"props":2444,"children":2445},{},[2446],{"type":34,"value":2447},"Utilizar GPT para transformar consultas en lenguaje natural a SQL facilita enormemente la accesibilidad a los datos. Implementando este sistema, podrás ofrecer una experiencia de usuario más amigable y eficiente.",{"type":25,"tag":2449,"props":2450,"children":2452},"h1",{"id":2451},"recursos-adicionales",[2453],{"type":34,"value":2454},"Recursos adicionales",{"type":25,"tag":296,"props":2456,"children":2457},{},[2458,2468,2478],{"type":25,"tag":300,"props":2459,"children":2460},{},[2461],{"type":25,"tag":72,"props":2462,"children":2465},{"href":2463,"rel":2464},"https://platform.openai.com/docs/api-reference",[76],[2466],{"type":34,"value":2467},"Documentación API OpenAI",{"type":25,"tag":300,"props":2469,"children":2470},{},[2471],{"type":25,"tag":72,"props":2472,"children":2475},{"href":2473,"rel":2474},"https://docs.sqlalchemy.org/en/20/",[76],[2476],{"type":34,"value":2477},"SQLAlchemy ORM Documentation",{"type":25,"tag":300,"props":2479,"children":2480},{},[2481],{"type":25,"tag":72,"props":2482,"children":2485},{"href":2483,"rel":2484},"https://pypi.org/project/python-dotenv/",[76],[2486],{"type":34,"value":2487},"Python Dotenv",{"type":25,"tag":1068,"props":2489,"children":2490},{},[2491],{"type":34,"value":1072},{"title":7,"searchDepth":352,"depth":352,"links":2493},[2494,2495,2496,2497,2498,2499,2500,2501,2502],{"id":1114,"depth":352,"text":1117},{"id":1138,"depth":352,"text":1141},{"id":1221,"depth":352,"text":1224},{"id":1446,"depth":352,"text":1449},{"id":1793,"depth":352,"text":1796},{"id":2155,"depth":352,"text":2158},{"id":2225,"depth":352,"text":2228},{"id":2415,"depth":352,"text":2418},{"id":2439,"depth":352,"text":2442},"content:posts:gpt-sql-integration.md","posts/gpt-sql-integration.md","posts/gpt-sql-integration",{"_path":2507,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2508,"description":2509,"date":2510,"layout":10,"subtitle":2511,"image":2512,"optimized_image":2512,"category":14,"tags":2513,"author":20,"paginate":6,"body":2517,"_type":362,"_id":3404,"_source":364,"_file":3405,"_stem":3406,"_extension":367},"/posts/java-security-best-practices","Java Security Best Practices: Preventing Vulnerabilities and Threats","Explore essential security strategies for Java applications, addressing common vulnerabilities identified by OWASP Top 10 with practical examples.","2024-08-15T11:00:00.000Z","Practical Strategies to Safeguard Your Java Applications","/assets/img/uploads/java-security-best-practices.png",[14,573,2514,2515,2516],"security","owasp","cybersecurity",{"type":22,"children":2518,"toc":3392},[2519,2527,2533,2538,2592,2597,2603,2608,2730,2735,2741,2746,2847,2853,2858,2981,2986,2992,2997,3022,3027,3033,3038,3054,3059,3065,3070,3152,3157,3163,3168,3288,3293,3299,3304,3337,3342,3346,3351,3355,3388],{"type":25,"tag":26,"props":2520,"children":2521},{},[2522],{"type":25,"tag":30,"props":2523,"children":2524},{},[2525],{"type":34,"value":2526},"Security is critical in software development. Java applications, like all software, face numerous potential vulnerabilities. In this article, we will explore essential security best practices, referencing the OWASP Top 10, and providing practical Java examples to help you protect your applications effectively.",{"type":25,"tag":47,"props":2528,"children":2530},{"id":2529},"understanding-owasp-top-10",[2531],{"type":34,"value":2532},"Understanding OWASP Top 10",{"type":25,"tag":30,"props":2534,"children":2535},{},[2536],{"type":34,"value":2537},"The OWASP Top 10 outlines the most critical web application security risks:",{"type":25,"tag":2539,"props":2540,"children":2541},"ol",{},[2542,2547,2552,2557,2562,2567,2572,2577,2582,2587],{"type":25,"tag":300,"props":2543,"children":2544},{},[2545],{"type":34,"value":2546},"Injection",{"type":25,"tag":300,"props":2548,"children":2549},{},[2550],{"type":34,"value":2551},"Broken Authentication",{"type":25,"tag":300,"props":2553,"children":2554},{},[2555],{"type":34,"value":2556},"Sensitive Data Exposure",{"type":25,"tag":300,"props":2558,"children":2559},{},[2560],{"type":34,"value":2561},"XML External Entities (XXE)",{"type":25,"tag":300,"props":2563,"children":2564},{},[2565],{"type":34,"value":2566},"Broken Access Control",{"type":25,"tag":300,"props":2568,"children":2569},{},[2570],{"type":34,"value":2571},"Security Misconfiguration",{"type":25,"tag":300,"props":2573,"children":2574},{},[2575],{"type":34,"value":2576},"Cross-Site Scripting (XSS)",{"type":25,"tag":300,"props":2578,"children":2579},{},[2580],{"type":34,"value":2581},"Insecure Deserialization",{"type":25,"tag":300,"props":2583,"children":2584},{},[2585],{"type":34,"value":2586},"Using Components with Known Vulnerabilities",{"type":25,"tag":300,"props":2588,"children":2589},{},[2590],{"type":34,"value":2591},"Insufficient Logging and Monitoring",{"type":25,"tag":30,"props":2593,"children":2594},{},[2595],{"type":34,"value":2596},"Let's delve into practical strategies and Java implementations for some of these threats.",{"type":25,"tag":47,"props":2598,"children":2600},{"id":2599},"_1-injection-prevention",[2601],{"type":34,"value":2602},"1. Injection Prevention",{"type":25,"tag":30,"props":2604,"children":2605},{},[2606],{"type":34,"value":2607},"Use Prepared Statements and parameterized queries:",{"type":25,"tag":451,"props":2609,"children":2611},{"className":571,"code":2610,"language":573,"meta":7,"style":7},"PreparedStatement stmt = connection.prepareStatement(\"SELECT * FROM users WHERE username=? AND password=?\");\nstmt.setString(1, username);\nstmt.setString(2, password);\nResultSet rs = stmt.executeQuery();\n",[2612],{"type":25,"tag":14,"props":2613,"children":2614},{"__ignoreMap":7},[2615,2651,2678,2703],{"type":25,"tag":460,"props":2616,"children":2617},{"class":462,"line":463},[2618,2623,2627,2632,2637,2641,2646],{"type":25,"tag":460,"props":2619,"children":2620},{"style":583},[2621],{"type":34,"value":2622},"PreparedStatement stmt ",{"type":25,"tag":460,"props":2624,"children":2625},{"style":589},[2626],{"type":34,"value":1379},{"type":25,"tag":460,"props":2628,"children":2629},{"style":583},[2630],{"type":34,"value":2631}," connection.",{"type":25,"tag":460,"props":2633,"children":2634},{"style":608},[2635],{"type":34,"value":2636},"prepareStatement",{"type":25,"tag":460,"props":2638,"children":2639},{"style":583},[2640],{"type":34,"value":737},{"type":25,"tag":460,"props":2642,"children":2643},{"style":1166},[2644],{"type":34,"value":2645},"\"SELECT * FROM users WHERE username=? AND password=?\"",{"type":25,"tag":460,"props":2647,"children":2648},{"style":583},[2649],{"type":34,"value":2650},");\n",{"type":25,"tag":460,"props":2652,"children":2653},{"class":462,"line":352},[2654,2659,2664,2668,2673],{"type":25,"tag":460,"props":2655,"children":2656},{"style":583},[2657],{"type":34,"value":2658},"stmt.",{"type":25,"tag":460,"props":2660,"children":2661},{"style":608},[2662],{"type":34,"value":2663},"setString",{"type":25,"tag":460,"props":2665,"children":2666},{"style":583},[2667],{"type":34,"value":737},{"type":25,"tag":460,"props":2669,"children":2670},{"style":1343},[2671],{"type":34,"value":2672},"1",{"type":25,"tag":460,"props":2674,"children":2675},{"style":583},[2676],{"type":34,"value":2677},", username);\n",{"type":25,"tag":460,"props":2679,"children":2680},{"class":462,"line":480},[2681,2685,2689,2693,2698],{"type":25,"tag":460,"props":2682,"children":2683},{"style":583},[2684],{"type":34,"value":2658},{"type":25,"tag":460,"props":2686,"children":2687},{"style":608},[2688],{"type":34,"value":2663},{"type":25,"tag":460,"props":2690,"children":2691},{"style":583},[2692],{"type":34,"value":737},{"type":25,"tag":460,"props":2694,"children":2695},{"style":1343},[2696],{"type":34,"value":2697},"2",{"type":25,"tag":460,"props":2699,"children":2700},{"style":583},[2701],{"type":34,"value":2702},", password);\n",{"type":25,"tag":460,"props":2704,"children":2705},{"class":462,"line":489},[2706,2711,2715,2720,2725],{"type":25,"tag":460,"props":2707,"children":2708},{"style":583},[2709],{"type":34,"value":2710},"ResultSet rs ",{"type":25,"tag":460,"props":2712,"children":2713},{"style":589},[2714],{"type":34,"value":1379},{"type":25,"tag":460,"props":2716,"children":2717},{"style":583},[2718],{"type":34,"value":2719}," stmt.",{"type":25,"tag":460,"props":2721,"children":2722},{"style":608},[2723],{"type":34,"value":2724},"executeQuery",{"type":25,"tag":460,"props":2726,"children":2727},{"style":583},[2728],{"type":34,"value":2729},"();\n",{"type":25,"tag":30,"props":2731,"children":2732},{},[2733],{"type":34,"value":2734},"This approach prevents SQL injection by ensuring that input data is treated as data, not executable code.",{"type":25,"tag":47,"props":2736,"children":2738},{"id":2737},"_2-secure-authentication",[2739],{"type":34,"value":2740},"2. Secure Authentication",{"type":25,"tag":30,"props":2742,"children":2743},{},[2744],{"type":34,"value":2745},"Implement robust authentication using secure password storage (bcrypt or Argon2):",{"type":25,"tag":451,"props":2747,"children":2749},{"className":571,"code":2748,"language":573,"meta":7,"style":7},"import org.springframework.security.crypto.bcrypt.BCrypt;\n\nString hashedPassword = BCrypt.hashpw(password, BCrypt.gensalt());\nif (BCrypt.checkpw(password, hashedPassword)) {\n    // authentication successful\n}\n",[2750],{"type":25,"tag":14,"props":2751,"children":2752},{"__ignoreMap":7},[2753,2765,2772,2808,2831,2840],{"type":25,"tag":460,"props":2754,"children":2755},{"class":462,"line":463},[2756,2760],{"type":25,"tag":460,"props":2757,"children":2758},{"style":589},[2759],{"type":34,"value":1256},{"type":25,"tag":460,"props":2761,"children":2762},{"style":583},[2763],{"type":34,"value":2764}," org.springframework.security.crypto.bcrypt.BCrypt;\n",{"type":25,"tag":460,"props":2766,"children":2767},{"class":462,"line":352},[2768],{"type":25,"tag":460,"props":2769,"children":2770},{"emptyLinePlaceholder":677},[2771],{"type":34,"value":680},{"type":25,"tag":460,"props":2773,"children":2774},{"class":462,"line":480},[2775,2780,2784,2789,2794,2799,2804],{"type":25,"tag":460,"props":2776,"children":2777},{"style":583},[2778],{"type":34,"value":2779},"String hashedPassword ",{"type":25,"tag":460,"props":2781,"children":2782},{"style":589},[2783],{"type":34,"value":1379},{"type":25,"tag":460,"props":2785,"children":2786},{"style":583},[2787],{"type":34,"value":2788}," BCrypt.",{"type":25,"tag":460,"props":2790,"children":2791},{"style":608},[2792],{"type":34,"value":2793},"hashpw",{"type":25,"tag":460,"props":2795,"children":2796},{"style":583},[2797],{"type":34,"value":2798},"(password, BCrypt.",{"type":25,"tag":460,"props":2800,"children":2801},{"style":608},[2802],{"type":34,"value":2803},"gensalt",{"type":25,"tag":460,"props":2805,"children":2806},{"style":583},[2807],{"type":34,"value":718},{"type":25,"tag":460,"props":2809,"children":2810},{"class":462,"line":489},[2811,2816,2821,2826],{"type":25,"tag":460,"props":2812,"children":2813},{"style":589},[2814],{"type":34,"value":2815},"if",{"type":25,"tag":460,"props":2817,"children":2818},{"style":583},[2819],{"type":34,"value":2820}," (BCrypt.",{"type":25,"tag":460,"props":2822,"children":2823},{"style":608},[2824],{"type":34,"value":2825},"checkpw",{"type":25,"tag":460,"props":2827,"children":2828},{"style":583},[2829],{"type":34,"value":2830},"(password, hashedPassword)) {\n",{"type":25,"tag":460,"props":2832,"children":2833},{"class":462,"line":498},[2834],{"type":25,"tag":460,"props":2835,"children":2837},{"style":2836},"--shiki-default:#6A737D;--shiki-light:#6A737D",[2838],{"type":34,"value":2839},"    // authentication successful\n",{"type":25,"tag":460,"props":2841,"children":2842},{"class":462,"line":673},[2843],{"type":25,"tag":460,"props":2844,"children":2845},{"style":583},[2846],{"type":34,"value":769},{"type":25,"tag":47,"props":2848,"children":2850},{"id":2849},"_3-sensitive-data-exposure",[2851],{"type":34,"value":2852},"3. Sensitive Data Exposure",{"type":25,"tag":30,"props":2854,"children":2855},{},[2856],{"type":34,"value":2857},"Use encryption for sensitive data storage:",{"type":25,"tag":451,"props":2859,"children":2861},{"className":571,"code":2860,"language":573,"meta":7,"style":7},"Cipher cipher = Cipher.getInstance(\"AES/GCM/NoPadding\");\nSecretKey key = generateSecretKey();\ncipher.init(Cipher.ENCRYPT_MODE, key);\nbyte[] encryptedData = cipher.doFinal(plainData.getBytes(StandardCharsets.UTF_8));\n",[2862],{"type":25,"tag":14,"props":2863,"children":2864},{"__ignoreMap":7},[2865,2900,2921,2939],{"type":25,"tag":460,"props":2866,"children":2867},{"class":462,"line":463},[2868,2873,2877,2882,2887,2891,2896],{"type":25,"tag":460,"props":2869,"children":2870},{"style":583},[2871],{"type":34,"value":2872},"Cipher cipher ",{"type":25,"tag":460,"props":2874,"children":2875},{"style":589},[2876],{"type":34,"value":1379},{"type":25,"tag":460,"props":2878,"children":2879},{"style":583},[2880],{"type":34,"value":2881}," Cipher.",{"type":25,"tag":460,"props":2883,"children":2884},{"style":608},[2885],{"type":34,"value":2886},"getInstance",{"type":25,"tag":460,"props":2888,"children":2889},{"style":583},[2890],{"type":34,"value":737},{"type":25,"tag":460,"props":2892,"children":2893},{"style":1166},[2894],{"type":34,"value":2895},"\"AES/GCM/NoPadding\"",{"type":25,"tag":460,"props":2897,"children":2898},{"style":583},[2899],{"type":34,"value":2650},{"type":25,"tag":460,"props":2901,"children":2902},{"class":462,"line":352},[2903,2908,2912,2917],{"type":25,"tag":460,"props":2904,"children":2905},{"style":583},[2906],{"type":34,"value":2907},"SecretKey key ",{"type":25,"tag":460,"props":2909,"children":2910},{"style":589},[2911],{"type":34,"value":1379},{"type":25,"tag":460,"props":2913,"children":2914},{"style":608},[2915],{"type":34,"value":2916}," generateSecretKey",{"type":25,"tag":460,"props":2918,"children":2919},{"style":583},[2920],{"type":34,"value":2729},{"type":25,"tag":460,"props":2922,"children":2923},{"class":462,"line":480},[2924,2929,2934],{"type":25,"tag":460,"props":2925,"children":2926},{"style":583},[2927],{"type":34,"value":2928},"cipher.",{"type":25,"tag":460,"props":2930,"children":2931},{"style":608},[2932],{"type":34,"value":2933},"init",{"type":25,"tag":460,"props":2935,"children":2936},{"style":583},[2937],{"type":34,"value":2938},"(Cipher.ENCRYPT_MODE, key);\n",{"type":25,"tag":460,"props":2940,"children":2941},{"class":462,"line":489},[2942,2947,2952,2956,2961,2966,2971,2976],{"type":25,"tag":460,"props":2943,"children":2944},{"style":589},[2945],{"type":34,"value":2946},"byte",{"type":25,"tag":460,"props":2948,"children":2949},{"style":583},[2950],{"type":34,"value":2951},"[] encryptedData ",{"type":25,"tag":460,"props":2953,"children":2954},{"style":589},[2955],{"type":34,"value":1379},{"type":25,"tag":460,"props":2957,"children":2958},{"style":583},[2959],{"type":34,"value":2960}," cipher.",{"type":25,"tag":460,"props":2962,"children":2963},{"style":608},[2964],{"type":34,"value":2965},"doFinal",{"type":25,"tag":460,"props":2967,"children":2968},{"style":583},[2969],{"type":34,"value":2970},"(plainData.",{"type":25,"tag":460,"props":2972,"children":2973},{"style":608},[2974],{"type":34,"value":2975},"getBytes",{"type":25,"tag":460,"props":2977,"children":2978},{"style":583},[2979],{"type":34,"value":2980},"(StandardCharsets.UTF_8));\n",{"type":25,"tag":30,"props":2982,"children":2983},{},[2984],{"type":34,"value":2985},"Always encrypt data at rest and in transit (use HTTPS).",{"type":25,"tag":47,"props":2987,"children":2989},{"id":2988},"_4-security-misconfiguration",[2990],{"type":34,"value":2991},"4. Security Misconfiguration",{"type":25,"tag":30,"props":2993,"children":2994},{},[2995],{"type":34,"value":2996},"Adopt secure defaults, disable debugging, and avoid detailed error messages:",{"type":25,"tag":451,"props":2998,"children":3002},{"className":2999,"code":3000,"language":3001,"meta":7,"style":7},"language-properties shiki shiki-themes github-dark github-light","server.error.include-stacktrace=never\nserver.error.include-message=never\n","properties",[3003],{"type":25,"tag":14,"props":3004,"children":3005},{"__ignoreMap":7},[3006,3014],{"type":25,"tag":460,"props":3007,"children":3008},{"class":462,"line":463},[3009],{"type":25,"tag":460,"props":3010,"children":3011},{},[3012],{"type":34,"value":3013},"server.error.include-stacktrace=never\n",{"type":25,"tag":460,"props":3015,"children":3016},{"class":462,"line":352},[3017],{"type":25,"tag":460,"props":3018,"children":3019},{},[3020],{"type":34,"value":3021},"server.error.include-message=never\n",{"type":25,"tag":30,"props":3023,"children":3024},{},[3025],{"type":34,"value":3026},"Review and secure your Java application configuration regularly.",{"type":25,"tag":47,"props":3028,"children":3030},{"id":3029},"_5-cross-site-scripting-xss",[3031],{"type":34,"value":3032},"5. Cross-Site Scripting (XSS)",{"type":25,"tag":30,"props":3034,"children":3035},{},[3036],{"type":34,"value":3037},"Utilize proper encoding or frameworks that prevent XSS by default, such as Thymeleaf:",{"type":25,"tag":451,"props":3039,"children":3043},{"className":3040,"code":3041,"language":3042,"meta":7,"style":7},"language-html shiki shiki-themes github-dark github-light","\u003Cp th:text=\"${userInput}\">\u003C/p>\n","html",[3044],{"type":25,"tag":14,"props":3045,"children":3046},{"__ignoreMap":7},[3047],{"type":25,"tag":460,"props":3048,"children":3049},{"class":462,"line":463},[3050],{"type":25,"tag":460,"props":3051,"children":3052},{},[3053],{"type":34,"value":3041},{"type":25,"tag":30,"props":3055,"children":3056},{},[3057],{"type":34,"value":3058},"Avoid directly inserting user inputs into HTML without proper sanitization or encoding.",{"type":25,"tag":47,"props":3060,"children":3062},{"id":3061},"_6-insecure-deserialization",[3063],{"type":34,"value":3064},"6. Insecure Deserialization",{"type":25,"tag":30,"props":3066,"children":3067},{},[3068],{"type":34,"value":3069},"Limit deserialization to trusted sources only:",{"type":25,"tag":451,"props":3071,"children":3073},{"className":571,"code":3072,"language":573,"meta":7,"style":7},"ObjectInputStream ois = new ObjectInputStream(new FileInputStream(\"trusted_data.ser\"));\nMyObject obj = (MyObject) ois.readObject();\n",[3074],{"type":25,"tag":14,"props":3075,"children":3076},{"__ignoreMap":7},[3077,3126],{"type":25,"tag":460,"props":3078,"children":3079},{"class":462,"line":463},[3080,3085,3089,3094,3099,3103,3107,3112,3116,3121],{"type":25,"tag":460,"props":3081,"children":3082},{"style":583},[3083],{"type":34,"value":3084},"ObjectInputStream ois ",{"type":25,"tag":460,"props":3086,"children":3087},{"style":589},[3088],{"type":34,"value":1379},{"type":25,"tag":460,"props":3090,"children":3091},{"style":589},[3092],{"type":34,"value":3093}," new",{"type":25,"tag":460,"props":3095,"children":3096},{"style":608},[3097],{"type":34,"value":3098}," ObjectInputStream",{"type":25,"tag":460,"props":3100,"children":3101},{"style":583},[3102],{"type":34,"value":737},{"type":25,"tag":460,"props":3104,"children":3105},{"style":589},[3106],{"type":34,"value":742},{"type":25,"tag":460,"props":3108,"children":3109},{"style":608},[3110],{"type":34,"value":3111}," FileInputStream",{"type":25,"tag":460,"props":3113,"children":3114},{"style":583},[3115],{"type":34,"value":737},{"type":25,"tag":460,"props":3117,"children":3118},{"style":1166},[3119],{"type":34,"value":3120},"\"trusted_data.ser\"",{"type":25,"tag":460,"props":3122,"children":3123},{"style":583},[3124],{"type":34,"value":3125},"));\n",{"type":25,"tag":460,"props":3127,"children":3128},{"class":462,"line":352},[3129,3134,3138,3143,3148],{"type":25,"tag":460,"props":3130,"children":3131},{"style":583},[3132],{"type":34,"value":3133},"MyObject obj ",{"type":25,"tag":460,"props":3135,"children":3136},{"style":589},[3137],{"type":34,"value":1379},{"type":25,"tag":460,"props":3139,"children":3140},{"style":583},[3141],{"type":34,"value":3142}," (MyObject) ois.",{"type":25,"tag":460,"props":3144,"children":3145},{"style":608},[3146],{"type":34,"value":3147},"readObject",{"type":25,"tag":460,"props":3149,"children":3150},{"style":583},[3151],{"type":34,"value":2729},{"type":25,"tag":30,"props":3153,"children":3154},{},[3155],{"type":34,"value":3156},"Always validate deserialized data.",{"type":25,"tag":47,"props":3158,"children":3160},{"id":3159},"_7-component-vulnerabilities",[3161],{"type":34,"value":3162},"7. Component Vulnerabilities",{"type":25,"tag":30,"props":3164,"children":3165},{},[3166],{"type":34,"value":3167},"Regularly update libraries and dependencies, and use tools like OWASP Dependency-Check to identify vulnerabilities:",{"type":25,"tag":451,"props":3169,"children":3173},{"className":3170,"code":3171,"language":3172,"meta":7,"style":7},"language-xml shiki shiki-themes github-dark github-light","\u003Cplugin>\n    \u003CgroupId>org.owasp\u003C/groupId>\n    \u003CartifactId>dependency-check-maven\u003C/artifactId>\n    \u003Cversion>9.0.9\u003C/version>\n\u003C/plugin>\n","xml",[3174],{"type":25,"tag":14,"props":3175,"children":3176},{"__ignoreMap":7},[3177,3196,3222,3247,3272],{"type":25,"tag":460,"props":3178,"children":3179},{"class":462,"line":463},[3180,3185,3191],{"type":25,"tag":460,"props":3181,"children":3182},{"style":583},[3183],{"type":34,"value":3184},"\u003C",{"type":25,"tag":460,"props":3186,"children":3188},{"style":3187},"--shiki-default:#85E89D;--shiki-light:#22863A",[3189],{"type":34,"value":3190},"plugin",{"type":25,"tag":460,"props":3192,"children":3193},{"style":583},[3194],{"type":34,"value":3195},">\n",{"type":25,"tag":460,"props":3197,"children":3198},{"class":462,"line":352},[3199,3204,3209,3214,3218],{"type":25,"tag":460,"props":3200,"children":3201},{"style":583},[3202],{"type":34,"value":3203},"    \u003C",{"type":25,"tag":460,"props":3205,"children":3206},{"style":3187},[3207],{"type":34,"value":3208},"groupId",{"type":25,"tag":460,"props":3210,"children":3211},{"style":583},[3212],{"type":34,"value":3213},">org.owasp\u003C/",{"type":25,"tag":460,"props":3215,"children":3216},{"style":3187},[3217],{"type":34,"value":3208},{"type":25,"tag":460,"props":3219,"children":3220},{"style":583},[3221],{"type":34,"value":3195},{"type":25,"tag":460,"props":3223,"children":3224},{"class":462,"line":480},[3225,3229,3234,3239,3243],{"type":25,"tag":460,"props":3226,"children":3227},{"style":583},[3228],{"type":34,"value":3203},{"type":25,"tag":460,"props":3230,"children":3231},{"style":3187},[3232],{"type":34,"value":3233},"artifactId",{"type":25,"tag":460,"props":3235,"children":3236},{"style":583},[3237],{"type":34,"value":3238},">dependency-check-maven\u003C/",{"type":25,"tag":460,"props":3240,"children":3241},{"style":3187},[3242],{"type":34,"value":3233},{"type":25,"tag":460,"props":3244,"children":3245},{"style":583},[3246],{"type":34,"value":3195},{"type":25,"tag":460,"props":3248,"children":3249},{"class":462,"line":489},[3250,3254,3259,3264,3268],{"type":25,"tag":460,"props":3251,"children":3252},{"style":583},[3253],{"type":34,"value":3203},{"type":25,"tag":460,"props":3255,"children":3256},{"style":3187},[3257],{"type":34,"value":3258},"version",{"type":25,"tag":460,"props":3260,"children":3261},{"style":583},[3262],{"type":34,"value":3263},">9.0.9\u003C/",{"type":25,"tag":460,"props":3265,"children":3266},{"style":3187},[3267],{"type":34,"value":3258},{"type":25,"tag":460,"props":3269,"children":3270},{"style":583},[3271],{"type":34,"value":3195},{"type":25,"tag":460,"props":3273,"children":3274},{"class":462,"line":498},[3275,3280,3284],{"type":25,"tag":460,"props":3276,"children":3277},{"style":583},[3278],{"type":34,"value":3279},"\u003C/",{"type":25,"tag":460,"props":3281,"children":3282},{"style":3187},[3283],{"type":34,"value":3190},{"type":25,"tag":460,"props":3285,"children":3286},{"style":583},[3287],{"type":34,"value":3195},{"type":25,"tag":30,"props":3289,"children":3290},{},[3291],{"type":34,"value":3292},"Execute dependency checks regularly in your CI/CD pipeline.",{"type":25,"tag":47,"props":3294,"children":3296},{"id":3295},"_8-logging-and-monitoring",[3297],{"type":34,"value":3298},"8. Logging and Monitoring",{"type":25,"tag":30,"props":3300,"children":3301},{},[3302],{"type":34,"value":3303},"Implement comprehensive logging and monitoring to detect and respond to security incidents:",{"type":25,"tag":451,"props":3305,"children":3307},{"className":571,"code":3306,"language":573,"meta":7,"style":7},"logger.warn(\"Failed login attempt for user: {}\", username);\n",[3308],{"type":25,"tag":14,"props":3309,"children":3310},{"__ignoreMap":7},[3311],{"type":25,"tag":460,"props":3312,"children":3313},{"class":462,"line":463},[3314,3319,3324,3328,3333],{"type":25,"tag":460,"props":3315,"children":3316},{"style":583},[3317],{"type":34,"value":3318},"logger.",{"type":25,"tag":460,"props":3320,"children":3321},{"style":608},[3322],{"type":34,"value":3323},"warn",{"type":25,"tag":460,"props":3325,"children":3326},{"style":583},[3327],{"type":34,"value":737},{"type":25,"tag":460,"props":3329,"children":3330},{"style":1166},[3331],{"type":34,"value":3332},"\"Failed login attempt for user: {}\"",{"type":25,"tag":460,"props":3334,"children":3335},{"style":583},[3336],{"type":34,"value":2677},{"type":25,"tag":30,"props":3338,"children":3339},{},[3340],{"type":34,"value":3341},"Aggregate logs centrally, and set up alerts for suspicious activities.",{"type":25,"tag":47,"props":3343,"children":3344},{"id":275},[3345],{"type":34,"value":278},{"type":25,"tag":30,"props":3347,"children":3348},{},[3349],{"type":34,"value":3350},"Adopting these best practices can greatly enhance the security posture of your Java applications. Stay proactive by regularly reviewing security measures, updating dependencies, and monitoring your systems. Security is a continuous process, not a one-time setup.",{"type":25,"tag":2449,"props":3352,"children":3353},{"id":291},[3354],{"type":34,"value":294},{"type":25,"tag":296,"props":3356,"children":3357},{},[3358,3368,3378],{"type":25,"tag":300,"props":3359,"children":3360},{},[3361],{"type":25,"tag":72,"props":3362,"children":3365},{"href":3363,"rel":3364},"https://owasp.org/www-project-top-ten/",[76],[3366],{"type":34,"value":3367},"OWASP Top 10",{"type":25,"tag":300,"props":3369,"children":3370},{},[3371],{"type":25,"tag":72,"props":3372,"children":3375},{"href":3373,"rel":3374},"https://spring.io/projects/spring-security",[76],[3376],{"type":34,"value":3377},"Spring Security",{"type":25,"tag":300,"props":3379,"children":3380},{},[3381],{"type":25,"tag":72,"props":3382,"children":3385},{"href":3383,"rel":3384},"https://owasp.org/www-project-dependency-check/",[76],[3386],{"type":34,"value":3387},"OWASP Dependency Check",{"type":25,"tag":1068,"props":3389,"children":3390},{},[3391],{"type":34,"value":1072},{"title":7,"searchDepth":352,"depth":352,"links":3393},[3394,3395,3396,3397,3398,3399,3400,3401,3402,3403],{"id":2529,"depth":352,"text":2532},{"id":2599,"depth":352,"text":2602},{"id":2737,"depth":352,"text":2740},{"id":2849,"depth":352,"text":2852},{"id":2988,"depth":352,"text":2991},{"id":3029,"depth":352,"text":3032},{"id":3061,"depth":352,"text":3064},{"id":3159,"depth":352,"text":3162},{"id":3295,"depth":352,"text":3298},{"id":275,"depth":352,"text":278},"content:posts:java-security-best-practices.md","posts/java-security-best-practices.md","posts/java-security-best-practices",[3408,3409,3410,3411,3412,3416,3420,3424,3428,3432,3436,3440,3444,3448,3452,3456,3460,3464,3468,3472,3475,3479,3483],{"_path":4,"title":8,"date":11},{"_path":370,"title":371,"date":373},{"_path":1090,"title":1091,"date":1093},{"_path":2507,"title":2508,"date":2510},{"_path":3413,"title":3414,"date":3415},"/posts/circuit-breaking-resilience","Circuit Breaking in Distributed Systems","2024-07-15T11:00:00.000Z",{"_path":3417,"title":3418,"date":3419},"/posts/maven-and-github-actions","Automating Java Builds with Maven and GitHub Actions CI/CD","2024-06-15T11:00:00.000Z",{"_path":3421,"title":3422,"date":3423},"/posts/pattern-matching-sealed-classes-java21","A Guide to Pattern Matching and Sealed Classes in Java 21","2024-05-15T11:00:00.000Z",{"_path":3425,"title":3426,"date":3427},"/posts/async-java-completable-vs-reactive","Asynchronous Programming in Java: CompletableFuture vs Reactive Streams","2024-04-15T11:00:00.000Z",{"_path":3429,"title":3430,"date":3431},"/posts/mastering-concurrency","Mastering Concurrency in Java: Threads, Executors, and Virtual Threads","2024-03-15T11:00:00.000Z",{"_path":3433,"title":3434,"date":3435},"/posts/microservices-mistakes","Common Mistakes When Developing Microservices","2024-02-15T11:00:00.000Z",{"_path":3437,"title":3438,"date":3439},"/posts/tdd-testing","Test-Driven Development (TDD) in Java with JUnit and Mockito","2024-01-15T11:00:00.000Z",{"_path":3441,"title":3442,"date":3443},"/posts/single-table-design","Single Table Design","2023-12-27T11:00:00.000Z",{"_path":3445,"title":3446,"date":3447},"/posts/java-17","Exploring Java 17's New Features","2023-10-08T11:00:00.000Z",{"_path":3449,"title":3450,"date":3451},"/posts/hexagonal-architecture","Introduction to Hexagonal Architecture","2023-10-02 18:00:00",{"_path":3453,"title":3454,"date":3455},"/posts/terraform","Unleashing the Power of Infrastructure as Code (IaC)","2023-09-24 18:00:00",{"_path":3457,"title":3458,"date":3459},"/posts/python-virtual-environments","Python Virtual Environments (venv)","2023-09-17 22:00:00",{"_path":3461,"title":3462,"date":3463},"/posts/api-first-principles","API First principles","2023-09-01 22:15:18",{"_path":3465,"title":3466,"date":3467},"/posts/how-to-configure-ssh-key-based-authentication","How To Configure SSH Key-Based Authentication","2021-02-21 03:46:12",{"_path":3469,"title":3470,"date":3471},"/posts/algorithms-binary-search-tree","Algorithms - Binary Search Tree in kotlin","2020-12-06T11:00:00.000Z",{"_path":3473,"title":3474,"date":3471},"/posts/how-to-create-a-twitter-bot","How to create a Twitter bot",{"_path":3476,"title":3477,"date":3478},"/posts/manage-aws-infrastructure-with-terraform","Bootstrap complete Java application infrastructure in AWS with Terraform","2020-12-02T11:00:00.000Z",{"_path":3480,"title":3481,"date":3482},"/posts/algorithms-working-with-trees","Algorithms - Binary tree traversals in kotlin","2020-11-24T11:00:00.000Z",{"_path":3484,"title":3485,"date":3486},"/posts/playing-with-ocr-libraries","Playing with OCR libraries","2020-11-22T11:00:00.000Z",1790578305104]