Fetching models using the new Model::Find() macro
Published: 2024-06-27 06:43:20

Trailblazer comes with predefined steps we call ["macros"](https://trailblazer.to/2.1/docs/operation/#operation-macro-api) that help you with common tasks such as validating a form object or finding an existing model using ActiveRecord (actually, any other ORM you approve of).

The [newly released `Model::Find()` macro](https://trailblazer.to/2.1/docs/macro/#macro-model-model-find) is a replacement for `Model()` that, over the years, turned out to be helpful but a bit hard to customize, as it wouldn't really allow you to change how things are performed in the background.

```ruby
class Update < Trailblazer::Activity::Railway
  step Model::Find(Song, find_by: :id, params_key: :slug)
```

You are going to need `trailblazer-macro` 2.1.16 for this goodie.

## Extracting the ID

The new macro provides options such as `:params_key` and `:column_key` to configure how the ID is extracted. If you want to do it yourself, simply use a block.

```ruby
step Model::Find(Song, find_by: :id) { |ctx, params:, **|
  params[:song] && params[:song][:id]
}
```

A bunch of discussions with long-term users lead us to the decision that overriding ID extraction should be straight-forward since this is more than just an edge case.

## Customizing the query

Once the ID is extracted, it's now very simple to customize how the query is performed (e.g. `find_by: id` vs `find(id)`). Nevertheless, the new key feature is the `:query` option that allows you to write that code manually.

```ruby
step Model::Find(
  Song,
  query: ->(ctx, id:, current_user:, **) { where(id: id, user: current_user) }
)
```

Note how the query logic can access `ctx` and keyword arguments, just like a real step. The extracted ID is available in the variable `:id`.


## Not_found terminus

If one of the two steps weren't successful, you can instantly go to a [new terminus `not_found`](https://trailblazer.to/2.1/docs/macro/#macro-model-model-find-not-found) in your business operation, indicating to the outer world that this particular step failed. With the release of `trailblazer-endpoint` this will become interesting as the endpoint code could, for instance, automatically render a 404 page for you.

## Discussion

The code of this macro is nothing special. In fact, it simply creates a tiny nested activity behind the scenes with two steps, one to extract the ID, and one to actually fetch the model.

Anyhow, we strongly recommend sticking with this macro instead of writing your own, for three reasons.

1. Well, code we write and maintain is less work for you. Keep in mind that we also provide [documentation](https://trailblazer.to/2.1/docs/macro/#macro-model-model-find).
2. Features like the `not_found` terminus we added with forward-compatibility in mind: they will save you code once endpoints are becoming a thing.
3. Debugging `Model::Find()` is a matter of using our internal tracing. [In the trace](https://trailblazer.to/2.1/docs/macro/#macro-model-model-find-debugging), you can see which part failed.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ldoy6sfcwtfth7m70ikp.png)



Please [give us some feedback](https://github.com/trailblazer/trailblazer/discussions/257) about what's missing or what you like about this simple addition to our stack. Have fun!