SujanDutta
ServiceNow Employee

 

If you're a ServiceNow developer in 2026, there's a good chance AI is writing at least some of your code. And there's an equally good chance that code defaults to GlideRecord. It works, mostly — until it doesn't, and the failures are the silent kind that slip into production without a trace.

That's the starting point for this conversation between Developer Advocate Sujan and Amit Gujarathi, a five-time ServiceNow MVP and Solution Architect with over 11 years in the ServiceNow ecosystem. Together they walk through GlideQuery on a live instance, showing exactly where it outperforms GlideRecord and why it deserves a permanent spot in your scripting toolkit.

 

GlideQuery Is Not a Replacement — It's a Safer Layer

First things first: GlideQuery is a wrapper around GlideRecord. Same engine, same database calls. The difference is in the interface it puts on top. Amit compared it this way — GlideRecord is like driving with no seat belts and no lane assist. It'll let you do anything, but if something goes wrong, you're on your own. GlideQuery adds guardrails that catch problems before they become incidents of a different kind.

 

The Typo That Deletes Everything

The first demo hit hard. A simple query filtering on an "activated" field (a typo for "active") was run with GlideRecord. No error. No warning. GlideRecord just ignored the invalid field and returned every record in the table. Now imagine that typo was in a deleteMultiple operation — you'd wipe the table clean without realising.

With GlideQuery, the same typo throws a clear error message: "unknown field 'activated' in table incident." Better yet, it lists all valid fields on the table so you can spot the correct name immediately. That's not just error handling — it's built-in time savings.

 

JavaScript Type Comparisons That Actually Work

The second scenario tackled a notoriously tricky bug. When you pull an email field from GlideRecord and compare it with triple-equals (===) to a plain string, the comparison fails even when the values are identical. That's because GlideRecord returns a GlideElement object, not a JavaScript string, so a strict type comparison will never match.

GlideQuery returns plain JavaScript values. Triple-equals works as expected. This eliminates a whole class of bugs that are painful to debug because the logged output looks perfectly correct.

 

Immutable Queries: Define Once, Extend Anywhere

With GlideRecord, if you need to run three queries that share a base condition (say, active equals true) but differ on priority, you end up copy-pasting that base condition into each one. Change the base later, and you have three places to update.

GlideQuery's query objects are immutable. You define your base condition once, then use .where() to create new query objects that extend it. The original stays untouched. Less code, fewer places for things to go wrong, and much easier maintenance.

 

Select Only What You Need

GlideRecord pulls the entire row every time. GlideQuery's .select() method lets you specify exactly which fields you want. On large tables, this is a meaningful performance improvement — you're only transferring the data you'll actually use.

The .selectOne() method goes further by returning an Optional. Instead of writing if-else blocks to handle missing records, you chain .orElse() with a default object. It's cleaner, more readable, and prevents widgets from crashing when data is missing.

 

Insert and Update with Plain JSON

Creating a record with GlideRecord means calling initialize(), then setValue() for each field, then insert(). With GlideQuery, you pass a JSON object directly to .insert(). Two lines instead of ten. For integrations where payloads arrive as JSON, this is a significant time saver — if your keys match the table's field names, you can pass the payload straight through.

Updates work similarly. Single-record updates require a sys_id (intentionally, to prevent accidental mass updates). Bulk updates accept a query plus a JSON object of changes. And the insertOrUpdate method handles the "upsert" pattern — check if a record exists, update it if it does, create it if it doesn't — in a single method call.

 

Aggregates Without GlideAggregate

This is where GlideQuery really separates itself. With GlideRecord, aggregation means switching to a separate GlideAggregate class. With GlideQuery, you chain .aggregate('COUNT') and .groupBy() right into your existing query. Count active incidents per category? Nine lines of code, and the calculation happens at the database level rather than pulling records into memory and counting with JavaScript.

The session also covered average calculations with .aggregate('AVG') grouped by state, and a two-dimensional grouping example (priority × state) that builds a structured report object ready for a portal widget or REST API response — all in a single fluent chain.

 

1 Comment
aasch
Mega Sage

Hi,

thank your for the insightful article. I'll be sure to adopt GlideQuery in the future.

 

I have a few things, which I think are important to keep in mind.

 

"GlideRecord pulls the entire row every time. GlideQuery's .select() method lets you specify exactly which fields you want. On large tables, this is a meaningful performance improvement — you're only transferring the data you'll actually use."

GlideQuery uses GlideRecord under the hood without exception according to available documentation as far as I know. It is strictly "just" a wrapper for GlideRecord.

Following from that, we can not make performance claims - the improvment is purely for code safety and ease-of-use, which is a very valid and valuable use case. But we must assume and keep in mind that GlideQuery still strictly pulls all fields of all rows via GlideRecord in the background.

 

"Nine lines of code, and the calculation happens at the database level rather than pulling records into memory and counting with JavaScript."

In the light of the previous quote it should be noted that counting at the database level happens because of GlideAggregate. GlideQuery uses GlideAggregate in the background and GlideAggregate is the object that already provides the counting at database level - not GlideQuery itself. So no inherent performance gain over using GlideAggregate explicitly.

 

A small nitpick:

"When you pull an email field from GlideRecord and compare it with triple-equals (===) to a plain string, the comparison fails even when the values are identical."

The values aren't identical, which is why the comparison fails, not the other way round.

Using "==" forces implicit type coercion of the GlideElement to the value the GlideElement contains. If you're using "===" you're comparing the GlideElement-object itself as a value against an email string.

This is a bit more on the technical side, but it's already important to know what you're doing when scripting.

 

Best regards,

Aaron