iTranslated by AI
[NGSI-LD and @Context] Building a Smart City with FIWARE (Knowledge Enhancement Part 2)
Overview
This is the third installment in the "Learning FIWARE" article series.
In the previous article, we reviewed the specifications of NGSIv2 and NGSI-LD, which are the format standards for data flowing in FIWARE, through a sample case.
This article aims to provide a thorough understanding of the next-generation NGSI-LD.
For information on the previous generation NGSIv2 and related topics, please refer to the following article:
What is NGSI-LD?
As a review of the previous article, let's look at an overview of NGSI-LD in comparison with NGSIv2.

From the official tutorial
Suppose store context information is defined and stored in a database as shown above.[1]
The characteristics of NGSI-LD are as follows:
- All things existing in the world are represented as an
Entity, and eachEntityhas attributes calledPropertyand their values (Value). - Additionally, a
Relationshipmay be defined between multipleEntities, and eachRelationshiphas aPropertyand itsValue, just like anEntity. - Furthermore, a
Relationshipcan have anotherRelationship. This allowsRelationshipsto be linked together...
These characteristics represent data in a way that did not exist in its predecessor, NGSIv2. As the name Linked Data suggests, NGSI-LD provides a richer representation of connections between Entities.
Let's look at the data representation in the figure above. As we do, be mindful of the following hierarchy that can be seen within the JSON file.
| Field | Can hold | |
|---|---|---|
Entity |
-> | Property |
Entity |
-> | Relationship |
Property |
-> | Value |
Relationship |
-> | Property |
Relationship |
-> | Relationship |
{
"@context": "https://uri.etsi.org/ngsi-ld/v1/ngsi-ld-core-context-v1.3.jsonld",
"id": "urn:ngsi-ld:Shelf:unit001",
"type": "https://fiware.github.io/tutorials.Step-by-Step/schema/Shelf",
"https://fiware.github.io/tutorials.Step-by-Step/schema/locatedIn": {
"type": "Relationship",
"object": "urn:ngsi-ld:Building:store001",
"https://fiware.github.io/tutorials.Step-by-Step/schema/installedBy": {
"type": "Relationship",
"object": "urn:ngsi-ld:Person:employee001"
},
"https://fiware.github.io/tutorials.Step-by-Step/schema/requestedBy": {
"type": "Relationship",
"object": "urn:ngsi-ld:Person:bob-the-manager"
},
"https://fiware.github.io/tutorials.Step-by-Step/schema/statusOfWork": {
"type": "Property",
"value": "https://fiware.github.io/tutorials.Step-by-Step/schema/completed"
}
},
"https://fiware.github.io/tutorials.Step-by-Step/schema/maxCapacity": {
"type": "Property",
"value": 50
},
"https://fiware.github.io/tutorials.Step-by-Step/schema/numberOfItems": {
"type": "Property",
"value": 50
},
"https://fiware.github.io/tutorials.Step-by-Step/schema/stocks": {
"type": "Relationship",
"object": "urn:ngsi-ld:Product:001"
},
"name": {
"type": "Property",
"value": "Corner Unit"
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
13.398611,
52.554699
]
}
}
}
Now, there are several points of interest in the sample above.
- The presence of
@context - The way the
typepart is written - Parts of
PropertyandRelationshipare in URL notation
3-1. Why are the notation methods different?
These unusual notations are good examples of the characteristics of NGSI-LD... but it's hard to tell what's what! 😅
So, let's take a look at the notation methods in NGSI-LD.
The World of @Context
In this article, we will continue our explanation using the official tutorial.
First, the tutorial describes the problems that NGSI-LD aims to solve as follows:
① To create an interoperable system of computer-readable links, we must use a well-defined data format (JSON-LD) and assign unique IDs (URLs or URNs) to both data entities and relationships between entities, so that meaning can be programmatically retrieved from the data itself.
② Furthermore, the use and creation of these unique IDs should be handed over as much as possible so as not to interfere with the processing of the data objects themselves.
Point ① and Point ②... they were a bit hard to grasp, so let's break them down.
JSON-LD
First, how should connections between different media be represented in data? (Like the Relationship between Entities.)
We can look at web services (websites) as a reference.
As you know, a website is a visualization of document-based data composed of HTML and JavaScript. HTML elements have a structure that is very easy for humans to read. On the other hand, data sent through communication (e.g., HTTPS) is in JSON format. Because of its structured and simple format, the JSON format is easy for machines to analyze (parse). Of course, it is also easy for humans to read.
In other words,
- As a data format for representing data and connections, JSON is human and machine-readable.
Next, let's consider the representation of connections with other websites.
Just like hyperlinks on a website, the URL (location) of other websites must be public and accessible to everyone. If such link information to other websites is embedded in your own site, you will be able to navigate to those other sites.
Let's try writing the link information in JSON like this (key-value information is very easy to read):
{
"type": "Person",
"name": "Bob",
"homepage": "https://xxx.com"
}
Now, here's where the problem arises: problems occur when managing (receiving) transition information to multiple websites.
In other words, in key-value notation using keywords like "type", "name", or "homepage", ambiguity is inevitable.
Let's look at the following example.
// Assume the following json data was received from other sites.
// From Site A
{
"name": "Bob",
"homepage": "https://xxx.com"
}
// From Site B
{
"name": "bboi4", // Login name?
"homepage": "https://xxx.com" // Wait, it's the same URL as Site A??
}
In this case, the "name" part is different, but the "homepage" part is the same. With this data alone, your own site cannot infer "which link information has what meaning"...
One way to avoid such ambiguity is to write the key clearly. That is, to express the Property more specifically.
{
"http://ex.com/name": "Bob",
"http://y.com/homepage": "http://z.com/"
}
If it's written like this, it becomes clear that even if the "name" is the same, its origin is different.
However, as a result, it has become very difficult for humans to read... (Verbose). From a human perspective, something like "name" would be much easier to read.
Therefore, JSON-LD's Context allows for the use of shortened versions of URLs by defining and referencing a @context.
{
"@context": "http://v.com/myapp.jsonld",
"name": "Bob",
"homepage": "X"
}
JSON-LD is a data format that introduced a mechanism for parsing data while considering the context, maintaining data uniqueness while remaining human and machine-readable.
{
"@context": "http://v.com/myapp.jsonld",
"@id": "http://y.com/Bob",
"name": "Bob",
"homepage": "http://bob.com"
}
For more on JSON-LD, check out the video below. The official website is also important for deepening your understanding.
For RDFa, which appears in the middle of the video, refer to articles like these:
How to Create @context Files
NGSI-LD is an extension of the JSON-LD format, improved in terms of readability and ambiguity for JSON files that include connection expressions.
The core is the @context part, and it's important to appropriately design the model definitions and URLs referenced by the @context.
Now, let's look at how to create a @context file. The general flow is as follows:
- Design the data structure within the database.
- Select data models, including Entities within the data structure from step 1.
2-1. Consider whether existing public Data Models can be reused.
2-2. If reusable: eliminate redundant parts and add necessary components.
2-3. If not reusable: define a new data model.
2-4. For both (2-2) and (2-3), define types in.yaml. - Create the
@contextfile from the.yamlfile created in (2-4).
Smart Agriculture Example

From the official tutorial
We want to use the data structure shown above. As we can see, the required Entities are Building, Device (TemperatureSensor, FillingLevelSensor), and Person.
We want to create files describing these data definitions... but there's no need to define them from scratch!! Several models are already public and can be reused.
Entity |
Attribute |
Property or Relationship
|
type | Existing Model | available? |
|---|---|---|---|---|---|
Building |
- | - | - | dataModel.Building | - |
| - | id | Property |
URN | - | - |
| - | name | Property |
text | dataModel.Building | yes |
| - | address | Property |
address | schema.org/address | yes |
| - | location | Property |
GeoProperty | geojson.org | yes |
| - | temperature | Property |
number | schema.org/Number | no |
| - | fillingLevel | Property |
number | schema.org/Number | no |
| - | owner | Relationship |
URL | - | no |
Person |
- | - | - | schema.org/Person | - |
| - | id | Property |
URN | - | - |
| - | name | Property |
Text | schema.org/Text | yes |
Temperature Sensor |
- | - | - | dataModel.Device | - |
| - | id | Property |
URN | - | - |
| - | temperature | Property |
number | schema.org/Number | - |
FillingLevelSensor |
- | - | - | dataModel.Device | - |
| - | id | Property |
URN | - | - |
| - | fillingLevel | Property |
number | schema.org/Number | - |
Building can be based on dataModel.Building, and Person can be based on schema.org/Person. As for FillingLevelSensor and Temperature Sensor, while there are no directly related models, we can create new models by referencing dataModel.Device.
Baseline Data Model
Let's start creating the @context file. To create a @context file, you need a data model file. However, creating the final version of the data model file right away can be difficult, so we'll first create a baseline data model. This is the original data model without any redundancy elimination or extensions. For the XXXSensor types, since the original is Device, we'll use Device as the baseline.
components:
schemas:
# This is the basic definition of a building
Building:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/building.yaml#/Building"
# These are all the building categories defined within the
# Smart City and Smart Agri-Food domains
BuildingCategory:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/building.yaml#/Categories"
# This is the basic definition of an IoT device
Device:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/device.yaml#/Device"
# This is a complete list of IoT device categories
DeviceCategory:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/Categories"
# This is a complete list of context attributes measurable by devices
ControlledProperties:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/ControlledProperties"
# This is the NGSI-LD definition of a person
# Since schema.org Person is JSON-LD,
# additional type and id attributes are required
Person:
allOf:
- $ref: "https://fiware.github.io/tutorials.NGSI-LD/models/ngsi-ld.yaml#/Common"
- $ref: "https://fiware.github.io/tutorials.NGSI-LD/models/schema.org.yaml#/Person"
Application Data Model
baseline.yaml is merely a basic data model. In practice, there are instances where you need to deal with redundant data definitions or require extensions, so we will create agriculture.yaml to reflect those.
components:
schemas:
# This is the basic definition of a building
Building:
allOf:
- $ref: "https://fiware.github.io/tutorials.NGSI-LD/models/building.yaml#/Building"
properties:
temperature:
$ref: https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/temperature
fillingLevel:
$ref: https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/fillingLevel
# These are all the building categories defined within the
# Smart City and Smart Agri-Food domains
BuildingCategory:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/building.yaml#/Categories"
# Added
TemperatureSensor:
type: object
required:
- temperature
allOf:
- $ref: https://fiware.github.io/tutorials.NGSI-LD/models/device.yaml#/Device
properties:
temperature:
$ref: https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/temperature
# Added
FillingLevelSensor:
type: object
required:
- FillingLevelSensor
allOf:
- $ref: https://fiware.github.io/tutorials.NGSI-LD/models/device.yaml#/Device
properties:
fillingLevel:
$ref: https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/fillingLevel
# (Unnecessary) This is the basic definition of an IoT device
# Device:
# $ref: "https://fiware.github.io/tutorials.NGSI-LD/models/device.yaml#/Device"
# This is a complete list of IoT device categories
DeviceCategory:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/Categories"
# This is a complete list of context attributes measurable by devices
ControlledProperties:
$ref: "https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/ControlledProperties"
# This is the NGSI-LD definition of a person
# Since schema.org Person is JSON-LD,
# additional type and id attributes are required
Person:
allOf:
- $ref: "https://fiware.github.io/tutorials.NGSI-LD/models/ngsi-ld.yaml#/Common"
- $ref: "https://fiware.github.io/tutorials.NGSI-LD/models/schema.org.yaml#/Person"
The file above allows you to create a data definition file for NGSI-LD. Note that the notation refers to Swagger specifications.
Let's take a look specifically at the TemperatureSensor part.
TemperatureSensor:
type: object
required:
- temperature
allOf:
- $ref: https://fiware.github.io/tutorials.NGSI-LD/models/device.yaml#/Device
properties:
temperature:
$ref: https://fiware.github.io/tutorials.NGSI-LD/models/saref-terms.yaml#/temperature
-
type: The data type, which can beNumber,String,Array, etc. Here it's an object type (assuming it will contain properties of various types). -
required: A constraint stating that the specifiedPropertymust be provided during registration. -
allOf: The definition ofTemperatureSensorfalls entirely underallOf(meaning it is limited to what is in$ref). -
$ref: The referenced data model definition file (reused if defined elsewhere). -
properties: Attribute names defined withinTemperatureSensor.
saref-related definitions can be found on the following Ontology definition site.
Automatically Creating NGSI-LD @context Files
Once the data definition file is created, it's finally time to create the @context file.
It's very simple; the NGSI-LD @context file can be generated from the Swagger data model as follows:
$ ./services ngsi [file]
# Creating a NGSI-LD @context file for normalized interactions datamodels.context-ngsi.jsonld created
Looking at the generated file, it looks like this:
{
"@context": {
"type": "@type",
"id": "@id",
"ngsi-ld": "https://uri.etsi.org/ngsi-ld/",
"fiware": "https://uri.fiware.org/ns/data-models#",
"schema": "https://schema.org/",
...etc
"Building": "fiware:Building",
"FillingLevelSensor": "fiware:FillingLevelSensor",
"Person": "fiware:Person",
"TemperatureSensor": "fiware:TemperatureSensor",
... etc
"actuator": "https://w3id.org/saref#actuator",
"additionalName": "schema:additionalName",
"address": "schema:address",
"airPollution": "https://w3id.org/saref#airPollution",
"atmosphericPressure": "https://w3id.org/saref#atmosphericPressure",
"barn": "https://wiki.openstreetmap.org/wiki/Tag:building%3Dbarn",
"batteryLevel": "fiware:batteryLevel",
"category": "fiware:category",
"configuration": "fiware:configuration",
"conservatory": "https://wiki.openstreetmap.org/wiki/Tag:building%3Dconservatory",
"containedInPlace": "fiware:containedInPlace",
"controlledAsset": "fiware:controlledAsset",
"controlledProperty": "fiware:controlledProperty",
"cowshed": "https://wiki.openstreetmap.org/wiki/Tag:building%3Dcowshed",
...etc
}
}
In this, the following three elements can be found:
- A list of standard terms and abbreviations. This eliminates the need to repeat URIs, reducing the overall file size. (e.g., the prefix "fiware" is defined as "https://uri.fiware.org/ns/data-models#", so "Building" = "fiware:Building" (="https://uri.fiware.org/ns/data-models#Building") can be shortened as such.
- A defined set of entity types (such as Building).
- A list of attributes (e.g., address) and enumerated values (e.g., barn).
Experiencing NGSI-LD with @context
Now, the created @context file is meaningless unless it is referenced by the database.
Therefore, we will place the @context file and prepare a mechanism so that other services can reference the NGSI-LD data definitions.
To promote interoperability of data exchange, the NGSI-LD Context Broker explicitly exposes the JSON-LD @context file to define the data held within context entities.
Let's prepare for the demo by seeing where to place the created @context file and how to reference it for data exchange.
Architecture

From the official tutorial
Comparing it with the previous article, you might notice a new component (JSON-LD @context). This is exactly where the @context file is stored.
Furthermore, this location needs to be accessible by anyone.
A simple way is to provide the static @context file, which defines the context entities within the system, via an HTTP Web-Server.
Now, let's define the required containers.
services:
orion:
image: fiware/orion-ld
hostname: orion
container_name: fiware-orion
depends_on:
- mongo-db
networks:
- default
ports:
- "1026:1026"
command: -dbhost mongo-db -logLevel DEBUG
healthcheck:
test: curl --fail -s http://orion:1026/version || exit 1
mongo-db:
image: mongo:4.2
hostname: mongo-db
container_name: db-mongo
expose:
- "27017"
ports:
- "27017:27017"
networks:
- default
ld-context: # HTTP server to host @context files
image: httpd:alpine
hostname: context # Accessible via http://context/<filename>
container_name: fiware-ld-context
ports:
- "3004:80" # HTTP port
You can see that we are trying to start a container for an HTTP server to host @context files.
Placing the @context File
...And now, we want to place the created @context on this HTTP server, but it's not there yet.
This is because the @context file is delivered at the same time as the data is POSTed to the Context Broker.
Let's look at a sample:
curl -iX POST 'http://localhost:1026/ngsi-ld/v1/entities/' \
-H 'Content-Type: application/ld+json' \
--data-raw '{
"id": "urn:ngsi-ld:Building:farm001",
"type": "Building",
"category": {
"type": "Property",
"value": ["farm"]
},
"address": {
"type": "Property",
"value": {
"streetAddress": "Großer Stern 1",
"addressRegion": "Berlin",
"addressLocality": "Tiergarten",
"postalCode": "10557"
},
"verified": {
"type": "Property",
"value": true
}
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [13.3505, 52.5144]
}
},
"name": {
"type": "Property",
"value": "Victory Farm"
},
"@context": "http://context/ngsi-context.jsonld" # Focus here
}'
Just like with NGSIv2, you can send data to http://localhost:1026/ngsi-ld/v1/entities/ using the POST method.
Please pay attention to the end. It says @context: "http://context/ngsi-context.jsonld". This provides an instruction that "the @context file will be placed (is placed) under http://context/."
Through this initial POST request, the @context file (ngsi-context.jsonld) is placed on the HTTP server, making it accessible to anyone from then on.
Then, in subsequent requests (GET or POST), you can send and receive data using the shortened JSON definition by specifying the URL of this @context file.
curl -iX POST 'http://localhost:1026/ngsi-ld/v1/entities/' \
-H 'Content-Type: application/json' \
-H 'Link: <http://context/ngsi-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' \
--d '{
"id": "urn:ngsi-ld:Building:barn002",
"type": "Building",
"category": {
"type": "Property",
"value": ["barn"]
},
"address": {
"type": "Property",
"value": {
"streetAddress": "Straße des 17. Juni",
"addressRegion": "Berlin",
"addressLocality": "Tiergarten",
"postalCode": "10557"
},
"verified": {
"type": "Property",
"value": true
}
},
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [13.3698, 52.5163]
}
},
"name": {
"type": "Property",
"value": "Big Red Barn"
}
}'
Take a look at the header part: Link: <http://context/ngsi-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json". The Link header explicitly specifies the reference destination for the @context. This allows Key-value pairs that would normally remain ambiguous, such as "address" or "location," to be deciphered by the machine or server.
CRUD Operations with @Context
Finally, let's look at REST operations through NGSI-LD.
Generally, the following request methods are used depending on the data processing you want to perform:
-
GET: Read data -
POST: Create new entities and attributes -
PATCH: Modify entities and attributes -
DELETE: Delete entities and attributes
Additionally, you can specify the response from the Broker.
In the header part:
-
Accept: application/json: Response is in JSON format -
Accept: application/ld+json: Response is in JSON-LD format -
Accept: application/geo+json: Response is in GeoJSON or GeoJSON-LD format
Omitting the last one, here are the main differences between JSON and JSON-LD responses:
- When the JSON-LD format is selected,
@contextis included as an additional attribute within the response body. - When the JSON-only format is used,
@contextis passed as an additional Link header element and is not represented in the response body.
...Since the response display changes depending on what is written in the header, we will continue to use the following headers so it's clear to everyone:
-H 'Content-Type: application/json' \
-H 'Link: <http://context/ngsi-context.jsonld>; rel="http://www.w3.org/ns/json-ld#context"; type="application/ld+json"' \
-H 'Accept: application/ld+json' \
| Description | Method | URL | Comment |
|---|---|---|---|
| Create a new entity | POST | /ngsi-ld/v1/entities/ |
- |
| Create a new attribute in an existing entity | POST | /ngsi-ld/v1/entities/<ID>/attrs |
Only the attribute part (ID and Type not included) |
| Create multiple entities or attributes at once | POST | ngsi-ld/v1/entityOperations/create |
Extension of single creation -> into an array |
| Overwrite multiple entities or attributes at once | POST | ngsi-ld/v1/entityOperations/upsert |
If the entity already exists, the request updates its attributes. If it doesn't exist, a new entity is created. |
| Read a specific entity | GET | /ngsi-ld/v1/entities/<ID> |
-d 'options=keyValues' for simple Key-value display |
| --> Read by specifying attributes | GET | + -d 'attrs=<attribute_name>'
|
Comma (,) separated for multiple |
| --> List specific entities | GET | + -d 'type=<entity_name>'
|
- |
| Overwrite an attribute value | PATCH | /ngsi-ld/v1/entities/<entity-id>/attrs/<attribute> |
Different from adding an attribute. Only changes the value. |
| Overwrite multiple attributes | PATCH | /ngsi-ld/v1/entities/<entity-id>/attrs |
Just avoid specifying a specific attribute name |
| Batch update attributes of multiple data entities | POST | /ngsi-ld/v1/entityOperations/upsert?options=update |
For updating multiple entities simultaneously |
| Batch replacement of entity data | POST | /ngsi-ld/v1/entityOperations/update?options=replace |
- |
| Delete an entity | DELETE | /ngsi-ld/v1/entities/<ID> |
Deletion of a single entity |
| Delete an attribute | DELETE | /ngsi-ld/v1/entities/<ID>/attrs |
- |
| Batch deletion of multiple entities | POST | ngsi-ld/v1/entityOperations/delete |
- |
| Batch deletion of multiple attributes from an entity | PATCH | /ngsi-ld/v1/entities/<entity-id>/attrs |
- |
Summary
In this article, I explained NGSI-LD, the next-generation API and data standard used on FIWARE.
NGSI-LD is an extension of JSON-LD, a data definition that maintains the human and machine readability of JSON files while eliminating ambiguity.
The key is that by creating and referencing @context files, you can exchange data in a manner similar to NGSIv2 based on traditional JSON files.
Furthermore, in FIWARE operations using NGSI-LD, it is necessary to create data definition files in advance and publicly expose the created @context files.
With this article, the discussion on NGSI, the "common language" on FIWARE, concludes.
In the subsequent series, I plan to provide overviews of Open API groups other than Orion and MongoDB and create some small IoT applications.
Finally, it's time for the practical part! 😒
-
In FIWARE, "context" is the general term for "information about things." ↩︎
Discussion