bob Go SQL Access Toolkit

repository·main·Indexed 23 days ago

https://github.com/stephenafamo/bob

A multi-layered Go SQL access toolkit providing a progressive path from a fluent query builder to a full type-safe ORM and factory code generation. Bob supports Postgres, MySQL/MariaDB, and SQLite, offering four layers of functionality: a dialect-specific query builder, ORM code generation based on database schemas, factory generation for testing relational data, and Go code generation for hand-crafted SQL queries.

Tokens
53.5K
Snippets
172
Records
271
Agent score
83%

What's inside bob

  1. Introduction to the Bob Query Builder

    main
    Bob is a Go SQL access toolkit designed to help build SQL queries without abstracting away the underlying SQL implementation. Instead of hiding SQL, it provides typed 'handrails' to improve the developer experience while allowing for custom crafting and progressive enhancement of queries.
  2. Overview of Bob's capabilities and database support

    main

    Bob is a Go SQL access toolkit designed for correctness, convenience without magic, and cooperation with the standard library. It can be adopted progressively, starting from raw SQL strings and moving toward fully typed models and factories.

    Supported Databases

    Bob provides full support (Queries, Models, ORM Gen, Factory Gen, and Query Gen) for:

    • Postgres
    • MySQL/MariaDB
    • SQLite
  3. Overview of Bob: Go SQL Access Toolkit

    main

    Bob is a set of Go packages and tools designed for working with SQL databases. It is built on three core philosophies: Correctness (following specifications closely), Convenience (providing helpful tools without unexplainable magic), and Cooperation (working well with the standard library and other tools).

    Bob allows for progressive adoption, starting from raw SQL query strings and moving towards fully typed queries with generated models and factories.

  4. Features not implemented in Bob

    main

    Bob intentionally excludes certain features to maintain simplicity and follow best practices:

    • Automatic timestamps (createdAt/updatedAt): Bob does not implement these at the ORM level, as it is considered better practice to implement them at the database level.
    • Soft deletes: Due to the complexity of handling edge cases, especially regarding relationships and cascading soft deletes, this is not implemented.
  5. New features available in Bob

    main

    Bob includes several features that are either not supported or more difficult to implement in SQLBoiler:

    • Cross schema generation
    • Preloading with LEFT JOINs
    • Multi-Key relationships
    • Context chaining in hooks
    • Relationships across tables (e.g., has-one-through, has-many-through)
    • Easier-to-use expression builders: Bob's query builder architecture reduces the need for non-typed strings when building expressions.
  6. Compare Bob vs GORM

    main

    Bob and GORM represent two different philosophies for SQL access in Go. Use Bob if you prefer a database-first approach with high type safety and incremental adoption. Use GORM if you prefer a code-first approach and require a large ecosystem of existing plugins.

    Key Differences

    FeatureBobGORM
    Source of TruthDatabase-first: You manage your DB with SQL/migrations and generate models from it.Code-first: You define models in Go and GORM manages the database schema.
    Type SafetyHigh: Generates type-safe code for everything, including relationships. Errors are caught at compile time.Low: Relies heavily on interface{} and magic strings, which can lead to runtime panics.
    Query BuildingPowerful: Supports almost any Select, Update, Insert, or Delete query supported by the dialect.Limited: Restricted to a subset of common features across supported dialects.
    EcosystemStandard Library: Works with *sql.DB, making it compatible with any standard library package.Specialized: Requires specific drivers, though it has many community plugins.
    AdoptionIncremental: You can use parts of Bob (like just the query builder) without full adoption.All-or-nothing: Requires full adoption of the framework.
    TestabilityHigh: Includes generated factories (inspired by Ruby's FactoryBot) for easy model testing.Low: Does not provide built-in testing helpers.
  7. Checking if a relationship has been loaded

    main

    Each model exposes an R.Loaded struct containing boolean flags for each relationship. This allows you to distinguish between a relationship that hasn't been loaded (Loaded.X == false) and one that was loaded but returned no results (Loaded.X == true and R.X == nil or len(R.X) == 0).

    Important: If you manually assign to R (e.g., jet.R.Pilot = pilot), you are responsible for manually updating R.Loaded to keep it in sync. The generated APIs (LoadX, Preload, ThenLoad, AttachX, InsertX) handle this automatically.

    jet, err := models.FindJet(ctx, db, 1)
    
    if !jet.R.Loaded.Pilot {
        // the pilot relationship has not been loaded
    }
    
    if err := jet.LoadPilot(ctx, db); err != nil {
        return err
    }
    
    // jet.R.Loaded.Pilot is now true.
    if jet.R.Loaded.Pilot && jet.R.Pilot == nil {
        // definitively no pilot
    }
    
    // For to-many:
    pilot, err := models.FindPilot(ctx, db, 1)
    if err := pilot.LoadJets(ctx, db); err != nil {
        return err
    }
    
    // pilot.R.Loaded.Jets is true even if the pilot has no jets.
    if pilot.R.Loaded.Jets && len(pilot.R.Jets) == 0 {
        // definitively zero jets
    }
  8. Retrieve related data using column naming conventions

    main

    Bob supports automatic nesting of related data by using specific naming patterns for returned columns:

    • related_table__column_name (double underscore): Indicates a to-one relationship.
    • related_table.column_name (dot): Indicates a to-many relationship.

    When using the All() method, Bob transforms and nests the rows according to these patterns. You can use the --prefix annotation in your SQL to simplify column naming for joins.

    -- Nested query example using --prefix
    SELECT
        users.*,
        --prefix:videos.
        videos.*,
        --prefix:videos.sponsor__
        sponsors.*
    FROM users
    LEFT JOIN videos ON videos.user_id = users.id
    INNER JOIN sponsors ON videos.sponsor_id = sponsors.id
    WHERE users.id IN ($1);
  9. How Bob's plugin system works

    main

    Bob's code generation engine is entirely plugin-based. Every output produced by Bob—including models, factories, and enums—is implemented as a plugin. Custom plugins are treated as first-class citizens, utilizing the same interfaces and mechanisms as the built-in plugins.

    Plugins are categorized into two types:

    1. Standalone output plugins: These register their own output directory and generate code in a dedicated package (e.g., models, enums, or factory).
    2. Template extension plugins: These do not create new output directories. Instead, they extend an existing standalone output by appending additional templates to it (e.g., where, loaders, and joins extend the models output).
  10. What are bob factories?

    main

    Bob factories are objects used to create templates for your models, which in turn can be used to generate model instances for testing. You can create a factory using factory.New() and then generate specific templates (e.g., NewJet) using mods to define how the models should be populated.

    To speed up testing, you can set 'base mods' on the factory itself. These mods will be automatically applied to every new template generated by that factory instance.

    f := factory.New()
    
    // Set base mods that apply to ALL templates from this factory
    f.AddBaseJetMods(
        factory.JetMods.RandomID(),
        factory.JetMods.RandomAirportID(),
    )
    
    // These templates will automatically include the base mods
    jetTemplate1 := f.NewJet()
    jetTemplate2 := f.NewJet()
    
    // Clear base mods if needed
    f.ClearBaseJetMods()
  11. Understand Bob's approach to automatic timestamps and soft deletes

    main

    Bob does not provide built-in support for automatic createdAt/updatedAt timestamps or soft deletes.

    • Timestamps: Bob recommends implementing createdAt and updatedAt logic at the database level rather than within the ORM to avoid unnecessary complexity.
    • Soft Deletes: Bob does not support soft deletes due to the complexity of handling edge cases, particularly regarding relationships and cascading deletes.
  12. Use the Bob Query Builder

    main
    Bob provides a dialect-specific query builder that is highly spec-compliant. Because builders are custom-crafted for each dialect (e.g., Postgres, MySQL), it is difficult to construct invalid queries. It is intended as an alternative to packages like squirrel or goqu.