Speaker 1: Welcome back to the deep died curious minds. You know
Speaker 1: that feeling you've got this really powerful tool, maybe like
Speaker 1: a brand new engine, but your current setup, your infrastructure,
Speaker 1: maybe uses a slightly different kind of fuel. It doesn't
Speaker 1: quite mesh. Well, today we're tackling exactly that kind of challenge.
Speaker 1: But in the world of web development, specifically with mber Data.
Speaker 1: It's the data layer for mber dotjs apps and well,
Speaker 1: it's designed to make interacting with APIs and managing your
Speaker 1: data really smooth, supercharge that user experience, you know. But
Speaker 1: here's the kicker. What if your API isn't shall we
Speaker 1: say standard, What if it uses different naming or weird
Speaker 1: URL structures or throws errors you don't expect. That's what
Speaker 1: we're digging into today. How mber data is a really
Speaker 1: smart design actually lets you adapt to any API out there.
Speaker 1: Think of it like getting a universal translator. Yeah, making
Speaker 1: your app and your back end speak the same language,
Speaker 1: no matter how quirky that back end might be.
Speaker 2: Exactly that's a challenge a lot of developers face, right,
Speaker 2: integrating a really robust front end like ember data with
Speaker 2: an excus sting back end API that might be well,
Speaker 2: let's call it unique. Ember Data definitely has its own
Speaker 2: opinionated way of doing things, it's preferred conventions. But its
Speaker 2: real power, I think, is in how extensible it is.
Speaker 2: We're going to look at its core parts, the store,
Speaker 2: the adapters, and the serializers and see how understanding those
Speaker 2: lets you connect to pretty much any data source. It
Speaker 2: really saves you from reinventing the wheel for common stuff
Speaker 2: like identity mapping or managing relationships.
Speaker 3: Okay, let's unpack that. An analogy might help imagine. Your app.
Speaker 3: Is this like bustling city and all your data users
Speaker 3: products whatever they're the citizens moving around. Mber Data is
Speaker 3: like the city management system, keeping track of everything, and
Speaker 3: at its heart there are these three key departments running
Speaker 3: the show. First up, you mentioned the store. What's that
Speaker 3: like city hall?
Speaker 2: Sort of? Yeah, think of the store as the city's
Speaker 2: central records office, but also like a super fast memory bank.
Speaker 2: When your application needs some data, it doesn't go straight
Speaker 2: to the API. It asks the store first.
Speaker 3: Ah, so it checks its cap precisely, and when data
Speaker 3: comes in, the store caches it intelligently. But it's more
Speaker 3: than just a simple cash The really clever part is
Speaker 3: the identity map. This prevents you from having say two
Speaker 3: different JavaScript objects representing the exact same user record and memory.
Speaker 3: How does that work? Well, imagine you fetch a list
Speaker 3: of contacts, maybe GD contacts, and later you fetch a
Speaker 3: specific one GD contacts one. If contact one was in
Speaker 3: both responses, the store ensures you only ever have one
Speaker 3: object instance for that contact with EED one. It preserves
Speaker 3: that object identity.
Speaker 2: Okay, I see, so no confusing duplicates floating around. That
Speaker 2: sounds vital for keeping things consistent, especially in complex apps.
Speaker 3: Absolutely crucial for scalability and uh just general sanity. Really.
Speaker 2: So, the store holds the records, keeps them unique. But
Speaker 2: how does it actually, you know, talk to the outside world,
Speaker 2: to our potentially quirky API.
Speaker 3: Right, The store doesn't talk directly. It delegates. That's the
Speaker 3: job of the adapter.
Speaker 2: The adapter. Okay, so if the store City Hall, the
Speaker 2: adapter is the Department of Foreign Affairs. Huh yeah, that's
Speaker 2: a good way to put it. The adapter handles how
Speaker 2: to communicate with external data sources your API. Its job
Speaker 2: is to figure out the full URL for a request,
Speaker 2: actually make the HTTP call using ajax, fetch maybe even
Speaker 2: web sockets, and it can even talk to things that
Speaker 2: aren't remote APIs like the browser's local storage or index
Speaker 2: dB possible very and the key thing is this separation.
Speaker 2: If your API changes how it expects requests, maybe moves
Speaker 2: from rest to grah ql, or just changes its URL scheme,
Speaker 2: often you only need to update the adapter, not rewrite
Speaker 2: huge chunks of your application logic decoupling nice exactly. Ember
Speaker 2: Data ships with a few standard ones REST adapter for
Speaker 2: common rest stuff JSON APA adapter, which is the default
Speaker 2: and follows the Jason Dot API spec and there used
Speaker 2: to be an active model adapter for rails though that's
Speaker 2: an add on now.
Speaker 3: Okay, So the adaptor handles the where and how of
Speaker 3: the request the transport layer basically, But what about the
Speaker 3: actual data the payload? What if the app I sends
Speaker 3: back data that looks completely different from what mber data expects,
Speaker 3: different grammar, different vocabulary.
Speaker 2: That, my friend, is where the serializer steps in.
Speaker 3: The serializer the linguist.
Speaker 2: Precisely, The serializer is your data's translator. It has two
Speaker 2: main jobs. First, serialization, taking data from your Mber app
Speaker 2: and formatting it correctly to send to the server. Second, normalization,
Speaker 2: taking the raw data received from the server and translating
Speaker 2: it into a structure. Mber Data understands, so.
Speaker 3: Adapter handles the connection. Serializer handles the language you got it.
Speaker 2: That separation is fundamental to ember Data's adaptability. It lets
Speaker 2: you tackle the how to talk and the what to
Speaker 2: say independently and.
Speaker 3: I'm guessing just like adapters, there are different serializers for
Speaker 3: different API languages.
Speaker 2: Spot on, mber Data gives you three main ones out
Speaker 2: of the box. There's the basic Jason serializer for simple
Speaker 2: flat Jason good starting points sometimes, and the rest serializer.
Speaker 2: This one expects data wrapped in a root key like contact,
Speaker 2: and it also understands how to handle sideloading.
Speaker 3: Sideloading what's that?
Speaker 2: Oh, that's when an API includes related data in the
Speaker 2: same response to save extra network requests like getting a
Speaker 2: contact and their company details all at once. The rest
Speaker 2: serializer can unpack.
Speaker 3: That gotcha, reduces chattiness exactly.
Speaker 2: And finally, there's the Jason up a serializer, which is
Speaker 2: built specifically for APIs that strictly follow the official Jason
Speaker 2: dot API specification. That's often the default. The idea is
Speaker 2: you pick the built in serializer that's closest to what
Speaker 2: your API does, and that minimizes the custom transation work
Speaker 2: you need to do initially.
Speaker 3: Okay, this is where it gets really practical for you listening,
Speaker 3: because let's face it, real world APIs aren't always textbook
Speaker 3: perfect are they? Rarely ember data anticipates this. That's why
Speaker 3: these customization hooks exist. It's about making your app talk
Speaker 3: smoothly to your specific back end warts and all. Let's
Speaker 3: start with customizing adapters. Say my API uses singular nouns
Speaker 3: for URLs like contact one instead of contacts one. How
Speaker 3: do I tell data?
Speaker 2: Yeah, that's a common one. It's surprisingly easy. You just
Speaker 2: override a method called pathsfotype in the adapter for that
Speaker 2: specific model. So in your app adapters contact dot js file,
Speaker 2: you'd add a path four type model name function and
Speaker 2: just make it return the singular model name. Done. Simple enough,
Speaker 2: so that handles the base path. But what if I
Speaker 2: need a totally custom URL structure for a specific query,
Speaker 2: like searching contacts by city, but the API wants app
Speaker 2: contacts by city Los Angeles instead of a query peram
Speaker 2: like dot city los Angeles.
Speaker 3: Right, for specific query logic, you'd override are all four query. Again,
Speaker 3: probably in your contact adapter. That method gets the queer
Speaker 3: parameters and the model name, so you have everything you
Speaker 3: need to build that custom URL string like app contacts
Speaker 3: by cityquery dot city and yeah, you might need to
Speaker 3: remember to dasherize the city name turn los Angeles into
Speaker 3: los Angeles, which is pretty standard for URLs.
Speaker 2: Okay, that makes sense. You get full control over the
Speaker 2: URL generation for different actions. And thinking about, say using
Speaker 2: an external API like reddits. Their API for subreddit posts
Speaker 2: is something like reddit dot com. Rememberjs dot Jason, we'd
Speaker 2: use exactly that role for query override in a post
Speaker 2: adapter to hit that specific nonstandard URL.
Speaker 4: Right.
Speaker 2: Absolutely. That's a perfect example of adapting to a completely external,
Speaker 2: maybe slightly unusual API structure using the adapter hooks. And
Speaker 2: while we're talking adapters, there's more fine grain control too.
Speaker 2: You can influence how ember data decides whether to refetch
Speaker 2: data that's already in the store, methods like should background
Speaker 2: reload all or should background reload record. Let you return
Speaker 2: true or false to control background updates.
Speaker 3: So you can fine tune the caching behavior prevent unnecessary requests,
Speaker 3: but still keep data fresh.
Speaker 2: Precisely, maybe for certain data you always want the latest,
Speaker 2: so you force a reload for other less critical data.
Speaker 2: The cased version is fine for longer.
Speaker 3: Very cool. Okay, let's switch gears to serializers. The translators.
Speaker 3: APIs love different naming conventions, don't they snake case like
Speaker 3: first name, title case CamelCase. Our JavaScript usually prefers CamelCase.
Speaker 3: How do we bridge that gap without making our model ugly?
Speaker 2: Oh yeah, the casing battle. Mber data has elegant solutions
Speaker 2: for this. For just one or two specific fields, you
Speaker 2: can use the actress hash in your serializer. You map
Speaker 2: the model's camel case name to the API's key like
Speaker 2: first name.
Speaker 3: Oh, first name okay for isolated cases.
Speaker 2: But if your entile API uses, say, snake case, doing
Speaker 2: that for every attribute would be tedious. So instead you
Speaker 2: override a method called key for attribute in your main
Speaker 2: application serializer. This function receives the camel case attribute name
Speaker 2: from your model, and you just return the transformed snakecase version.
Speaker 2: Ember data then uses this for all attributes.
Speaker 3: Automatically, nice like setting a global.
Speaker 2: Rule exactly and again. Back to that Reddit example. Reddit
Speaker 2: uses snake case for things like numb comments. Right, So
Speaker 2: overwriting key for attribute lets our mber model use a
Speaker 2: clean numb comments while the serializer handles translating it to
Speaker 2: numb comments when talking to the Reddit API keeps the
Speaker 2: front end cord nice and idiomatic.
Speaker 3: Love it. What about relationships? APIs often just give you
Speaker 3: a foreign key like companied. How do we link that
Speaker 3: to a proper belongs to company relationship?
Speaker 2: In Ember another common pattern, you can override key for
Speaker 2: relationship in your serializer, similar to key for attribute. This
Speaker 2: method lets you tell ember Data how relationship keys are
Speaker 2: represented in the payload. You could tell it to expect
Speaker 2: ad appended to the relationship name. For instance, so company
Speaker 2: becomes companied, so.
Speaker 3: It automates linking company to the company relationship. Yeap.
Speaker 2: And one more quick one, what if your API doesn't
Speaker 2: use e'd as the primary key, maybe uses eed or
Speaker 2: SSN or something else. Just set the primary key property
Speaker 2: on your serializer to whatever the API uses like primary
Speaker 2: key ad. Mber Data will then know how to uniquely
Speaker 2: identify your records.
Speaker 3: Okay, handling keys casing relationships. That covers a lot of
Speaker 3: common API quirks. Yeah, but what if the whole structure
Speaker 3: of the response is just different, not just the keys,
Speaker 3: but the nesting, the wrapping, Like that Reddit example where
Speaker 3: the good stuff is buried deep.
Speaker 2: Now we're talking normalization. This is the serializer's superpower for
Speaker 2: reshaping incoming data. Let's say your API wraps everything in
Speaker 2: a generic data key like data Yeah, you're chosen. Serializer
Speaker 2: might not expect that wrapper, yes, so you override normalizer response.
Speaker 2: This method gets the raw payload straight from the adapter.
Speaker 2: Inside normalizer response, you can manipulate that payload. You could
Speaker 2: say payload equal payload dot data to unwrap it. Then
Speaker 2: crucially you call super dot normalizer response store primary model class, payload,
Speaker 2: ID request type, passing the modified payload along. This lets
Speaker 2: the standard normalization process take over with data it now understands.
Speaker 3: Is there like preprocessing the raw response before Mberdata's main
Speaker 3: normalization engine.
Speaker 2: Sees it exactly, You're cleaning it up, reshaping it into
Speaker 2: the format your base serializer, like Jason serializer Arrest serializer expects.
Speaker 2: Remember ember Data's internal format it aims for is kind
Speaker 2: of like jason dot API, but with singular, dasherized types
Speaker 2: and camelcased attributes. Normalization bridges the gap between your API
Speaker 2: and that internal format.
Speaker 3: And for that Reddit example, with data nested under data
Speaker 3: dot children, not data, that's where normalized response or maybe
Speaker 3: a more specific one like normalized array response.
Speaker 2: Comes in precisely, you'd override the appropriate normalized method, dig
Speaker 2: into that nested structure, extract the array of actual post data,
Speaker 2: maybe do some transformation on each item, and then pass
Speaker 2: that cleaned up array to super makes sense.
Speaker 3: It lets you handle really arbitrarily structured APIs.
Speaker 2: It does, And normalized response actually delegates to more specific
Speaker 2: methods based on the type of request normalized final response,
Speaker 2: normalized find record response, normalized save response, etc. So you
Speaker 2: can target your normalization logic very precisely if needed.
Speaker 3: Super granular control. Now, when we send data back saving
Speaker 3: or updating, you mentioned something called a snapshot object. Why
Speaker 3: is that important?
Speaker 2: Ah, yes, the snapshot. When you save a record, the
Speaker 2: adapter doesn't get the live ember data record directly, it
Speaker 2: gets a snapshot. Think of the snapshot as a re
Speaker 2: only sort of frozen representation of the record's data at
Speaker 2: that specific moment in time.
Speaker 3: Okay, why not just use the live record.
Speaker 2: Because accessing properties on a live Ember data record can
Speaker 2: sometimes trigger side effects like automatically fetching unloaded relationships. You
Speaker 2: don't want that happening accidentally when you're just trying to
Speaker 2: package up data to send to the server. The snapshot
Speaker 2: provides a safe, stable copy of the data for the
Speaker 2: serializer to work with. Serialize into hash serialized methods. Use
Speaker 2: it without any unexpected side effects. It guarantees you're only
Speaker 2: sending the intended data.
Speaker 3: Got it. It's like a safe copy for serialization purposes,
Speaker 3: prevents unexpected data.
Speaker 2: Loading exactly keeps things predictable.
Speaker 3: Wow. Okay, that covers a ton of ground on adapting
Speaker 3: the core communication. But what about more complex data patterns
Speaker 3: like nested objects. What if my API sends back an
Speaker 3: address object right inside the contact payload?
Speaker 2: Good question. Mber data gives you a couple of options there.
Speaker 2: For simple read only nested objects, where the address doesn't
Speaker 2: really need to be its own manager, you can just
Speaker 2: declare the attribute without a specific type or transform like
Speaker 2: it at Bautry address. Mber data will just treat it
Speaker 2: as a plain JavaScript object and pass it through Simple.
Speaker 3: Okay for basic nested blobs of data.
Speaker 2: But if that nested object like the address, has its
Speaker 2: own ID and conceptually is a separate entity, you might
Speaker 2: want to manage your link to elsewhere. Then you can
Speaker 2: treat it as an embedded record. You use the embedded
Speaker 2: records mixin in your serializer. This tells the serializer to
Speaker 2: look for those nested objects and actually turn them into proper,
Speaker 2: fully fledged MBER data records, establishing the has many or
Speaker 2: belongs to relationship automatically, so.
Speaker 3: It extracts the nested AID one three street mood yeah
Speaker 3: and makes it a real address record linked.
Speaker 2: To the contact precisely. Like if you have skills AID
Speaker 2: one name, MBRA eighty two name texting embedded in a
Speaker 2: user payload, the mixin can turn those into actual skill
Speaker 2: records related to the user. One important caveat, though the
Speaker 2: embedded records mixin is designed primarily for the rest serializer
Speaker 2: or custom serializers, it doesn't really play nicely with the
Speaker 2: strict Jason a PI serializer.
Speaker 3: Okay, so if you're using strict Jason, that API embedding
Speaker 3: isn't the standard approach anyway, Right, you'd use relationship.
Speaker 2: Links exactly JSON that API prefers relationships defined by type
Speaker 2: addresses ED one twenty three in a relationships block rather
Speaker 2: than embedding the full address object directly under the contact.
Speaker 3: That makes sense. Use the tool that fits the standard
Speaker 3: you're working with.
Speaker 2: Right, Embedded records are powerful for certain API styles, but
Speaker 2: maybe not the JSON dot API way.
Speaker 3: Okay, moving on errors they happen. How does mber data
Speaker 3: help us handle API errors gracefully?
Speaker 2: It actually has pretty robust built in error handling. It
Speaker 2: maps common HTTP status codes to specific error objects. The
Speaker 2: most common one is probably four hundred and twenty two
Speaker 2: unprocessable entity. Mber data treats this as a validation error
Speaker 2: and creates an invalid air object. Importantly, this invalid air
Speaker 2: expects the API response to follow the JSON dot API
Speaker 2: error object specification with d DE tales about what went wrong,
Speaker 2: often including pointers to the specific fields.
Speaker 3: So it expects errors detail marted source pointer data attributes.
Speaker 2: Femail ideally yes. That allows you to easily display validation
Speaker 2: messages next to the correct form fields. For example, It
Speaker 2: also maps other codes four one becomes an unauthorized error,
Speaker 2: four or three of forbidden AER for a four and
Speaker 2: not founder four on nine a conflict error, and five
Speaker 2: hundred range usually a server er. These all inherit from
Speaker 2: a base adapter AIR class.
Speaker 3: That's handy, but what if my API uses say, four
Speaker 3: hundred bad requests for validation airs instead of four to
Speaker 3: twenty two.
Speaker 2: You can customize that the adapter has an is invalid
Speaker 2: status Header's payload method, you can override it to return true.
Speaker 2: If the status code is four hundred or whatever your
Speaker 2: API uses telling ember data to treat it as an
Speaker 2: invalid error.
Speaker 3: Okay, so we can map status codes. What about the
Speaker 3: error payload itself? What if it's not Jason dot API compliant.
Speaker 2: Two main places to hook in there. For general error
Speaker 2: response shaping, you can override handle response in adapter that
Speaker 2: gives you the raw response regardless of status. But if
Speaker 2: you specifically want to normalize the structure of validation error messages,
Speaker 2: like transforming your API's custom air format into the json
Speaker 2: dot api ers array, the extra errors method on the
Speaker 2: serializer is usually the better place. It's semantically focused on error.
Speaker 3: Payload interpretation adapter for status code mapping and general handling
Speaker 3: serializer for normalizing the air content.
Speaker 2: Got you got it.
Speaker 3: Let's tackle something a bit more mind vending. Polymorphic relationships,
Speaker 3: where a relationship can point to different types of records,
Speaker 3: like a notification could be about a new comment or
Speaker 3: a friend request. Ah.
Speaker 2: Yes, polymorphism very powerful for certain data models. You enable
Speaker 2: this in your model by setting polymorphic true on the
Speaker 2: belongs to or has many relationship definition. The key requirement
Speaker 2: then falls on the API response When including that relationship
Speaker 2: data either sideloaded or linked. The API must provide both
Speaker 2: the ID and the type for each related record.
Speaker 3: So like add five foot type new comment or ABC
Speaker 3: type friend request.
Speaker 2: Exactly that type tells Ember data which specific model new
Speaker 2: comment or friend request to load for that relationship link.
Speaker 2: Of course, those models should ideally inherit from a common
Speaker 2: base model like notification subject and can.
Speaker 3: You customize how that type information is read or written
Speaker 3: if the EPI uses a non standard way.
Speaker 2: Absolutely. The serializer offers hooks like key for polymorphic type,
Speaker 2: serialize polymorphic type, and extract polymorphic relationship to handle custom
Speaker 2: conventions for identifying and processing those polymorphic types in the payload.
Speaker 3: Incredible flexibility there. It really feels like you can mold
Speaker 3: MBER data to almost anything, which brings us to testing.
Speaker 3: With all these potential customizations and adapters and serializers, how
Speaker 3: do we make sure they actually work correctly and don't
Speaker 3: break later?
Speaker 2: Testing is absolutely critical. Yes, you wouldn't build a complex
Speaker 2: machine without testing the parts right. You can unit test
Speaker 2: adapters and serializers in isolation. For an adapter, you might
Speaker 2: call EARL four query directly and assert the output URL.
Speaker 2: For a serializer, you could test key for attribute or
Speaker 2: normalize with sample payloads.
Speaker 3: Okay, unit tests for specific methods.
Speaker 2: But often the most valuable tests are integration tests that
Speaker 2: exercise the adapter and serializer together working through the store.
Speaker 2: This more closely mirrors how your application actually uses them.
Speaker 2: You'd use the store to find or save a record,
Speaker 2: and then you can assert things about the outgoing request,
Speaker 2: like the URL or payload sent by the adapter, serial
Speaker 2: ether combo, or the data that ends up in the
Speaker 2: store after normalization.
Speaker 3: And how do you do that without hitting a real API.
Speaker 2: Tools like Embercli Mirage are perfect for this. Mirage lets
Speaker 2: you easily define mock API endpoints right within your Mber app.
Speaker 2: So your test run against this predictable mock server. You
Speaker 2: can make the store fetch data, Mirage intercepts it returns
Speaker 2: a specific payload you defined, and then you can assert
Speaker 2: that your serializer normalized it correctly into the store, or
Speaker 2: assert that saving a record sent the correctly serialized payload
Speaker 2: to Mirage.
Speaker 3: So Mirage acts as the customized fake back end for
Speaker 3: testing the whole store adapter serializer stack precisely.
Speaker 2: It gives you high confidence that your data layer customizations
Speaker 2: are working as intended.
Speaker 3: Right, That makes a lot of sense. Test the whole flow.
Speaker 3: So let's tie this all together. What's the big takeaway
Speaker 3: for everyone listening. We've gone through the core part store, adapter, serializer.
Speaker 3: We've seen how to tweak URLs, map keys, handle nesting,
Speaker 3: manage errors, even deal with polymorphism.
Speaker 2: I think the main point is that ember data isn't
Speaker 2: just about fetching data in one specific, prescribed way. It's
Speaker 2: really about giving you, the developer, the power to create
Speaker 2: clean abstractions over any kind of API, no matter how
Speaker 2: complex or non standard it might be. It provides strong
Speaker 2: conventions as a starting point, which is great for productivity,
Speaker 2: but it's real strength, it's genius lies in these thoughtful
Speaker 2: extension points, the adapter and serializer hooks that let you
Speaker 2: handle the inevitable real world variations. It frees you up
Speaker 2: to focus on building the actual features and user experience,
Speaker 2: rather than getting bogged down in the messy detail of
Speaker 2: data fetching and translation for every single API call.
Speaker 3: So, whether your API is a perfect JSON dot API
Speaker 3: implementation or something a bit more creative. From the back
Speaker 3: end team.
Speaker 4: Mber Data's extensibility means you likely have the tools to
Speaker 4: make it work cleanly within your MBER application. Hopefully this
Speaker 4: deep dive has shown you not just how to use
Speaker 4: these tools, but why they're designed the way they are,
Speaker 4: empowering you to really master your application's data flow. Think
Speaker 4: about your own projects. Which of these techniques Overwriting EARL
Speaker 4: for query, customizing key for attribute, using normalized response might
Speaker 4: solve that nagging API integration headache you've been avoiding. The
Speaker 4: tools are there. That's our time for this deep dive.
Speaker 4: Until next time, keep exploring, keep customizing, and keep learning.