Side Effects
Side effects scenarios come up often. What if we want to send an email
notification every time a Comment
is created?
It’s important to note that there are three overall categories of side effects, and each requires a different solution:
- Side effects internal to the
Model
: For example, setting apublished_at
attribute. - Side effects that should only occur on a specific request: For
example, only send an email update if we’re creating a
Post
for the first time, at the/posts
endpoint. - Side effects that should occur on every type of request: For
example, send an email notification every time a
Comment
is created - but not updated - whether it was created at the/comments
endpoint or sideposted at the/posts
endpoint.
Internal Side-Effects
For the first scenario, it’s OK to use ActiveRecord
callbacks (or the
equivalent functionality in a different ORM):
# app/models/user.rb
class Post < ApplicationRecord
before_save :set_published_at,
on: :update,
if: :publishing?
private
def set_published_at
self.published_at = Time.now
end
def publishing?
status_changed? && status == 'published'
end
end
Side-Effects on Specific Action
Just like you would with vanilla Rails, use the controller. Here we’ll
only send an email to our subscribers when the Post
is first created.
Keep in mind we could also “sidepost” Post
objects at the /blogs
endpoint, but this will only fire at the /posts
endpoint.
class PostsController < ApplicationController
def create
post, success = jsonapi_create.to_a
if success
PostMailer.published_email.deliver_later
render_jsonapi(post, scope: false)
else
render_errors_for(post)
end
end
end
Side-Effects on Every Request of a Given Type
Let’s add some special logging every time we create a Post
. Note this
will fire every time we create a Post
- whether we create it at the
/posts
endpoint or “sidepost” at the /blogs
endpoint.
We do not need to worry about this side-effect in our model specs, or rake tasks, as the functionality is only relevant to the API.
Edit your Resource
:
# app/resources/post_resource.rb
def create(attributes)
Rails.logger.info "Post begin created by #{context.current_user.email}..."
super
Rails.logger.info "Success!"
end