‹ All interview questions

Core Concepts

Your team lead says 'We don't write raw REST controllers in ServiceNow — we use the Table API and Scripted…

The question

Your team lead says 'We don't write raw REST controllers in ServiceNow — we use the Table API and Scripted REST APIs.' As someone who's built hundreds of Web API controllers in .NET, explain how ServiceNow's REST story compares. When would you use Table API vs Scripted REST, and what catches developers off guard?

Why Interviewers Ask This

This tests whether you can translate your deep REST API experience into ServiceNow's two-tier API model. The key insight: ServiceNow gives you a free, auto-generated CRUD API for every table (Table API), plus the ability to build custom endpoints (Scripted REST) when you need more control.

The Direct Comparison

// ASP.NET Web API (what you've built)
[Route("api/incidents")]
public class IncidentController : ApiController {
  [HttpGet] public IEnumerable<Incident> GetAll() { ... }
  [HttpGet("{id}")] public Incident Get(string id) { ... }
  [HttpPost] public IActionResult Create(Incident model) { ... }
}

// ServiceNow Table API (auto-generated, zero code)
// GET  /api/now/table/incident              → GetAll
// GET  /api/now/table/incident/{sys_id}     → Get by ID
// POST /api/now/table/incident              → Create
// PUT  /api/now/table/incident/{sys_id}     → Update
// Every table gets this for free — no controller needed

Table API vs Scripted REST

  • Table API = Like scaffolded controllers from dotnet aspnet-codegenerator. Zero code, full CRUD, supports query parameters (sysparm_query, sysparm_fields, sysparm_limit). Perfect for standard integrations. It's the equivalent of OData endpoints in .NET — auto-exposed, filterable, paginated.
  • Scripted REST API = Like writing a custom ApiController from scratch. You define the HTTP method, path, query params, and write the processing logic. Use when Table API isn't enough — custom business logic, aggregations, multi-table joins, or non-CRUD operations.
// Scripted REST API Resource (like a .NET Controller Action)
(function process(request, response) {
  var incidentId = request.pathParams.id;
  
  // Custom logic — aggregate child tasks
  var gr = new GlideRecord('incident');
  if (gr.get(incidentId)) {
    var taskCount = new GlideAggregate('task');
    taskCount.addQuery('parent', incidentId);
    taskCount.addAggregate('COUNT');
    taskCount.query();
    
    response.setBody({
      number: gr.getValue('number'),
      state: gr.getDisplayValue('state'),
      child_task_count: taskCount.next() ? 
        parseInt(taskCount.getAggregate('COUNT')) : 0
    });
  } else {
    response.setStatus(404);
    response.setBody({error: 'Incident not found'});
  }
})(request, response);

What Catches Developers Off Guard

  • No model binding: There's no [FromBody] attribute. You parse the request body manually with request.body.data — feels like Express.js more than ASP.NET.
  • ACLs still apply: Unlike .NET where you explicitly add [Authorize], ServiceNow's ACL system enforces row-level and field-level security on Table API calls by default. Your API consumer only sees records they're authorized to access.
  • Response format: Table API wraps everything in {"result": [...]}. If you're building a mobile app expecting flat JSON, Scripted REST gives you full control over the response shape.
  • Rate limiting is platform-managed: No need to implement your own throttling middleware — ServiceNow handles rate limits at the instance level.
💡 .NET Developer Pro Tip

Think of Table API as your auto-generated OData endpoints and Scripted REST as your custom ApiControllers. The decision matrix is the same: use the auto-generated path for standard CRUD, build custom when business logic demands it. Your instinct to version APIs, validate input, and return proper HTTP status codes transfers perfectly.