ReadOnly with Javascript Remoting Methods

I’ve been working almost exclusively on apex services for @RemoteAction methods and learned a few interesting, and often totally undocumented, tricks along the way.

First and foremost among them is the very basic but fundamental knowledge that apex collection types transfer incredibly well into the world of javascript, automatically being converted to their JSON equivalents when returned from a remoting method, the caveat being that Set objects are converted to arrays as there’s no set equivalent in JSON: Set<Integer> oddNumbers = new Set<Integer>{ 9, 7, 5, 3, 1};

Serializes to: [1,3,5,7,9]

Similarly any Map structure keyed with strings will serialize into JSON object, and since Map is an instance of Object in apex you can nest these to arbitrary depth:

results in the JSON you’d expect:

This is all well and good, but many people who’ve been working with javascript on are already well aware of this - the JSON.deserializeUntyped method hints strongly at this with it’s very existence. The great hidden secret this is building up to is: you can apply both @ReadOnly and @RemoteAction annotations to the same method, and when you do there are some subtle changes that happen. To demonstrate what’s so cool about this let’s dump some interesting limits method results to a client (simple page and controller are on github to play with).

The results are mostly the same, with the DML row and statement limit reduced to 0 in the @ReadOnly version, but there’s one other notable difference: the query row limit has doubled from 50,000 to 100,000. While this isn’t as generous as the limits for visualforce action methods it’s a significant improvement over the initial 50k, especially if you’re using aggregate queries at all. I’ve found it especially valuable as I almost never mix queries and DML in the same remoting methods, and this separation of duties is complemented well.

I’d also argue that it’s good practice stylistically to add the @ReadOnly annotation to remoting methods that don’t perform DML as a way of asserting nothing they call does and clearly communicates a design intent for the method to stay DML-free.