skip to content

In OData, how does the service document at the service root differ from the $metadata document, and what does a client use each one for?

level: juniorimportance: must knowfreq 30%

answer

  1. two fixed resources, two jobs
  2. a short list of entry points
  3. the whole model as CSDL
  4. XML unless the client asks otherwise

basics

~20 s

The OData service document at the service root lists entry points: entity sets, singletons and opted-in function imports, each with a name and URL. The $metadata document describes the whole model in CSDL: types, keys, relationships, operations and annotations.

solid answer

~40 s

An OData service exposes two fixed resources that describe it. The **service document** is what `GET` on the service root returns: in JSON, an object whose `value` array has one entry per entity set, singleton and opted-in function import, each with a `name`, a `url` and usually a `kind`; its context URL is the `$metadata` URL. The **metadata document**, at the service root plus `$metadata`, is the full data model in CSDL: entity and complex types, keys, navigation properties, actions, functions, the entity container and vocabulary annotations. With no format preference it MUST come back as CSDL XML; OData 4.01 adds a CSDL JSON form requested as `application/json`. A hypermedia client browses from the service document; a code generator or a generic UI needs `$metadata`.

code

json · 9 lines
json
{
  "@context": "https://api.example.com/sales/$metadata",
  "value": [
    { "name": "Orders", "kind": "EntitySet", "url": "Orders" },
    { "name": "Customers", "url": "Customers" },
    { "name": "Company", "kind": "Singleton", "url": "Company" },
    { "name": "TopProducts", "title": "Best sellers", "kind": "FunctionImport", "url": "TopProducts" }
  ]
}

go deeper

for a junior

Recall the two URLs, the service root and $metadata, and say in one line what each returns: a list of entry points versus the full CSDL model.

for a middle

Explain what the service document lists and leaves out (action imports never, function imports only if parameterless and opted in) and that $metadata defaults to CSDL XML.

for a senior

Show how a generic client uses both documents, the service document for navigation and $metadata for types, keys and annotations, and why a service without $metadata cripples tooling.

for a principal

Frame runtime discovery as OData's main promise and weigh what a service owner commits to by publishing a complete, stable $metadata document to every consumer.

## Two fixed resources describe every OData service The OData protocol says a service exposes two well-defined resources that describe its data model: a **service document** and a **metadata document**. Everything else (entity collections, single entities, property values, operation results) is a dynamic resource whose URL a client either receives in a payload or computes from the model. The two fixed documents answer different questions: *where can I start?* and *what does everything look like?* ## The service document: a list of entry points The service document is what a client receives from `GET` on the **service root**, the base URL of the service. Every OData service MUST return one there; it is the first item of the minimal conformance level. In the JSON format it is a single object with: - a **context** control information whose value MUST be the metadata document URL without a fragment, the link from the short list to the full model; - a `value` array with one object per entry point, each carrying `name` and `url` (absolute or relative), optionally a human-readable `title`, and a `kind` of `EntitySet`, `Singleton`, `FunctionImport` or `ServiceDocument` (an object without `kind` is an entity set). What appears is governed by the model: | Entity container child | Listed in the service document? | |---|---| | Entity set | Yes, unless `IncludeInServiceDocument="false"`; sets that cannot be queried without extra query options SHOULD NOT be listed | | Singleton | Yes, with `kind` `Singleton` | | Function import | Only for a parameterless function, and only when `IncludeInServiceDocument="true"` (absence means false) | | Action import | Never | The JSON shape is deliberately closed: services MUST NOT add other name/value pairs, clients MUST ignore unknown ones, and a client that meets an unknown `kind` MUST NOT stop processing or signal an error. Annotations MAY appear in any of its objects. ## The metadata document: the whole model The metadata document lives at the service root with `$metadata` appended and is fetched with a plain `GET`. It is the model written in the **Common Schema Definition Language (CSDL)**: entity types with their keys, complex and enumeration types, navigation properties, actions and functions, the entity container with its entity sets, singletons and imports, and the **vocabulary annotations** that add meaning such as descriptions, computed properties or capability restrictions. Two representations exist: 1. **CSDL XML**, rooted at `edmx:Edmx`, media type `application/xml`, the only form OData 4.0 defined. 2. **CSDL JSON**, a single JSON object, media type `application/json`, added by OData 4.01. If the request states no format preference (no `Accept` header, no `$format`), the service MUST return the XML representation. The 4.01 conformance rules make publishing `$metadata` a SHOULD at the minimal level (in both representations for a 4.01 service) and a MUST at the 4.0 advanced level. ## Side by side | | Service document | Metadata document | |---|---|---| | URL | service root | service root + `$metadata` | | Content | names and URLs of entry points | full CSDL model plus annotations | | Format | follows the format selected for the request | CSDL XML by default; CSDL JSON on request in 4.01 | | Obligation | MUST | SHOULD at minimal, MUST at advanced | | Typical reader | a hypermedia-driven browser of the service | a code generator, a generic UI, a validator | ## What a client does with each 1. A **hypermedia client** reads the service document, lets a user pick `Orders`, follows its `url`, and from then on follows links in responses. 2. A **code generator** fetches `$metadata` and turns types, sets and operations into typed client code. 3. A **generic runtime client** fetches `$metadata` at start-up to learn property names and types, to build `$filter` and `$select` expressions, and to read annotations telling it what the service permits. The service document alone cannot tell a client which properties an order has, what its key is, or which operations exist: it names the doors, not the rooms behind them. ## Common confusions - `$metadata` is not the `odata.metadata` format parameter, which controls how much control information a *data* response carries. - The service document is not a reduced CSDL file; it holds no types at all. - An entity set missing from the service document can still be declared in `$metadata` and addressed by URL.

  • Why might an OData entity set be missing from the service document although $metadata declares it?
    The entity set can carry `IncludeInServiceDocument="false"`, and sets that cannot be queried without extra query options SHOULD NOT be listed at all. Leaving a set out of the service document does not hide it: it is still declared in `$metadata` and can be addressed by its URL.
  • What must an OData client do when a JSON service document contains a kind value it does not recognise?
    It MUST NOT stop processing and MUST NOT signal an error; it skips or tolerates the entry. The same tolerance applies to unknown name/value pairs, which clients MUST ignore, while services MUST NOT emit pairs beyond those the JSON format defines.

The service document is a building's lobby directory: it lists the offices you can walk to and where they are. The $metadata document is the architect's full set of plans, showing every room, wall and connection, which you need before you can work inside the building.

saying these in an interview costs you the question

  • The service document and $metadata are the same model in two formats.
  • The service document lists every entity type with its properties.
  • In OData 4.01, $metadata returns JSON when no format is requested.
  • Action imports are listed in the service document beside function imports.
  • Leaving an entity set out of the service document makes it unreachable.