Discourse Onboarding LabChapter IV — JSON Serializers: The Contract with Ember0/6Contents
Chapter IV

JSON Serializers: The Contract with Ember

PostSerializer is the single source of truth for what a Post looks like as JSON to the Ember frontend: it picks which ActiveRecord attributes and computed values are exposed, under what conditions, and with what key names. Because the Ember Post model reads these keys directly onto tracked properties, any rename, removal, or conditional-inclusion change here has to be mirrored on the frontend or the UI silently breaks. This module walks through the attribute declarations, the include_*? conditional pattern, and the opts-driven instance variables that let one serializer class render many different contexts (topic view vs single post vs notifications).


walkthrough

Passing extra context into the serializer

app/serializers/post_serializer.rb · lines 318
line 3

PostSerializer inherits from BasicPostSerializer, so it only needs to declare the fields that are specific to viewing a full post (BasicPostSerializer already covers the bare minimum like cooked and username).

lines 5–16

INSTANCE_VARS lists flags and data (add_title, add_excerpt, draft_sequence, etc.) that aren't on the Post model at all — they're contextual switches the caller passes in, e.g. 'include the excerpt for this render' or 'here are the post_actions I already loaded in bulk'.

line 18

This metaprogramming line dynamically defines an attr_accessor for every name in INSTANCE_VARS, so methods below can read @add_title, @add_excerpt, etc. as plain ivars.

class PostSerializer < BasicPostSerializer  # To pass in additional information we might need  INSTANCE_VARS = %i[    parent_post    add_raw    add_title    single_post_link_counts    draft_sequence    post_actions    all_post_actions    add_excerpt    notice_created_by_users    ignored_user_like_counts  ]  INSTANCE_VARS.each { |v| public_send(:attr_accessor, v) }
step 1 of 3
  def initialize(object, opts)    super(object, opts)    PostSerializer::INSTANCE_VARS.each do |name|      public_send("#{name}=", opts[name]) if opts.include? name    end  end
app/serializers/post_serializer.rb · lines 107113
Figure I · The opts hash callers pass into PostSerializer.new(post, opts) (e.g. add_title: true, add_excerpt: true) is copied onto the matching instance variables here. Those ivars then drive the include_*? predicate methods that decide which optional fields make it into the JSON.
  def include_topic_title?    @add_title  end  def include_topic_html_title?    @add_title  end  def include_category_id?    @add_title  end  def include_excerpt?    @add_excerpt  end  def include_truncated?    @add_excerpt  end
app/serializers/post_serializer.rb · lines 123141
Figure II · ActiveModel::Serializer looks for an include_<attribute>? method for each declared attribute; when it returns falsy, the key is omitted from the JSON entirely (not sent as null). This is how the same serializer renders a lean payload for a topic listing and a richer one for a single-post view, controlled by the add_title/add_excerpt flags set during initialize.
From ActiveRecord to Ember tracked property
ActiveRecord Postapp/serializers/post_serializer.rb
BasicPostSerializer…ializers/basic_post_serializer.rb
PostSerializerapp/serializers/post_serializer.rb
JSON responseapp/serializers/post_serializer.rb
Ember Post model…tend/discourse/app/models/post.js
Figure III · Click a node to see what it does, where it lives in the code, and its labeled connections — the annotation opens beside it.
checkpoint

Check your understanding

Answered in place — nothing is graded, everything is explained. 0 / 2 passed

What determines whether `topic_title` shows up in a post's serialized JSON at all?

Where do flags like `add_title` and `add_excerpt` come from before they control which attributes are included?