Search code examples
postgresqlgotransactionsgraphqlbuffalo

How to cause Buffalo transaction middleware to commit?


In trying to use the buffalo-pop/pop/popmw Transaction middleware, I am not having success writing to the database. No errors are returned, and the debug output shows the SQL statements, but the updates and inserts are not committed.

The handler looks like:


func MyHandler(c buffalo.Context) error {
    tx, ok := c.Value("tx").(*pop.Connection)
    if !ok {
        return errors.New("no transaction found")
    }

    f := models.File{
        Name: "file.txt",
    }
    if err := tx.Create(&f); err != nil {
        return err
    }

    return nil
}

app.go:

func App() *buffalo.App {
...
        app.GET("/myhandler", MyHandler)
        app.Use(popmw.Transaction(models.DB))
...
}

If I use DB, _ := pop.Connect("development") for my connection, it works correctly. I also observed that the autoincrement value on the table changes each time this handler is hit.

In the real app, we can't call c.Render to report a response code because we are using gqlgen as the http handler. It looks like this:

func GQLHandler(c buffalo.Context) error {
    h := handler.GraphQL(gqlgen.NewExecutableSchema(gqlgen.Config{Resolvers: &gqlgen.Resolver{}}))
    newCtx := context.WithValue(c.Request().Context(), "BuffaloContext", c)
    h.ServeHTTP(c.Response(), c.Request().WithContext(newCtx))

    return nil
}

Solution

  • One of the features of the Pop Middleware for Buffalo is to wrap the action and the middlewares below in the stack inside a DB transaction. Here are the conditions for an auto-commit from the Pop Middleware:

    • Commit if there was no error executing the middlewares and action; and the response status is a 2xx or 3xx.
    • Rollback otherwise.

    From Buffalo integration with Pop.

    So, make sure no error is returned in either your action or in a middleware of the stack; and the produced response status is 200-ish or 300-ish.