
Generating Linked Data with YARRRML: using linked HTTP request targets
Before we start the tutorial
Learning objective
At the end of this tutorial, you'll be able to manually write YARRRML rules that output RDF to web resources that are linked to other web resources.
Prerequisites
We assume that you have completed our getting started and using targets tutorials. For this tutorial, we use the method using Docker containers and use the following versions of our tools:
- YARRRML parser:
1.11.0
- RMLMapper:
7.3.3
We use the following bash script map.sh
on Linux:
#!/bin/bash
# Usage:
# - run in the directory where this script is located
# - supply the YARRRML file as the one and only argument
#
# Example:
# ./map.sh rules.yml
YARRRML_FILE=$1
RML_FILE=temp.rml.ttl
docker run --rm -it -v $(pwd):/data rmlio/yarrrml-parser:1.11.0 -i /data/${YARRRML_FILE} -o /data/${RML_FILE}
docker run --net=host --rm -it -v $(pwd):/data rmlio/rmlmapper-java:v7.3.3 -m /data/${RML_FILE}
Create a working directory on your local machine now,
download this script as map.sh
, and
make it executable via
chmod +x map.sh
If you're not on Linux, try to make a similar script that suits your environment.
To read and write to web resources, we use an instance of the Community Solid Server with a single pod. Start it now via a Docker container:
docker run --rm -v ./Solid:/data -p 3000:3000 -it solidproject/community-server:7.1.7 -c config/file-root-pod.json
You do not need to know the details about Solid pods, but you need to know the following:
- A Solid pod can host web resources that are accessible via HTTP methods.
- Solid's Authorization Rules specify how to grant access permissions for web resources.
- These authorization rules can be activated by publishing them as RDF in Access Control List (ACL) resources.
- ACL resources are linked to the web resource they apply to, as specified in ACL Resource Discovery.
Concepts
An HTTP request target is a target resulting in output going to a web resource, using an HTTP method. We distinguish two types of HTTP request targets:
- Direct HTTP request targets, which write to web resources. We handled these in our using targets tutorial.
- Linked HTTP request targets, which write to web resources that are linked to other web resources. We handle these here.
Example
Consider the following columns from the CSV file people.csv:
person_id | firstname | lastname |
---|---|---|
0 | Natsu | Dragneel |
1 | Gray | Fullbuster |
2 | Gajeel | Redfox |
3 | Lucy | Heartfilia |
4 | Erza | Scarlet |
They contain information about five different characters, corresponding with the five rows, that appear in the same TV show. The information includes their id, first name, and last name.
Also, consider the CSV file people-acl.csv. It contains input for writing Solid's authorization rules.
id_acl | read_access | write_access | control_access | agent_webid | agent_class |
---|---|---|---|---|---|
0 | Yes | Yes | Yes | http://localhost:3000/profile/card#me | |
1 | Yes | No | No | http://xmlns.com/foaf/0.1/Agent |
Each row specifies an authorization for a web resource:
- The first row specifies that the agent whose WebID is http://localhost:3000/profile/card#me has read, write, and control access.
- The second row specifies that agents whose class is http://xmlns.com/foaf/0.1/Agent have read access. Practically, this means that everybody has read access.
We would like to annotate every character, generate the corresponding RDF triples, and write them to a first web resource, from here on called the "data resource". In addition, we would like to annotate Solid's authorization rules, generate the corresponding RDF triples, and write them to a second web resource: the ACL resource which is the web resource linked to the data resource.
Initial YARRRML document
Based on what we learned in the using targets tutorial, the following YARRRML document generates the aforementioned triples for the data resource:
prefixes:
e: http://myontology.com/
ex: http://www.example.com/
schema: http://schema.org/
sources:
source-people: ['people.csv~csv']
targets:
target-people-data:
type: directhttprequest
access: http://localhost:3000/people-data
serialization: turtle
authentication: auth1
authentications:
auth1:
type: cssclientcredentials
email: test@example.com
password: secret!
webId: http://localhost:3000/profile/card#me
oidcIssuer: http://localhost:3000/
mappings:
people:
sources:
- source-people
s:
value: ex:$(person_id)
targets:
- target-people-data
po:
- [a, schema:Person]
- [schema:givenName, $(firstname)]
- [schema:familyName, $(lastname)]
To produce RDF output,
we download the above YARRRML document,
save it to initial.yml
in our working directory,
download people.csv
and people-acl.csv to our working directory,
and execute the mapping commands by calling our map.sh
script:
./map.sh initial.yml
The data is now available at http://localhost:3000/people-data.
But, if we try to access the data using curl
curl http://localhost:3000/people-data
then we receive the following error message:
{"name":"UnauthorizedHttpError","message":"","statusCode":401,"errorCode":"H401","details":{}}
This happens because as an unauthenticated user, we do not have the right to read the web resource. But the data is there.
In the test setup we use, the contents of our
Community Solid Server instance's pod
is mapped to our ./Solid
subdirectory, by means of the command line option -v ./Solid:/data
in the Docker command we used to start the server.
We can inspect our data resource there.
It is located at ./Solid/people-data$.ttl
.
The suffix $.ttl
in the filename is added by the Community Solid Server for resources in Turtle format
whose URL does not end with .ttl
.
The file content is:
<http://www.example.com/0> a <http://schema.org/Person>;
<http://schema.org/familyName> "Dragneel";
<http://schema.org/givenName> "Natsu" .
<http://www.example.com/1> a <http://schema.org/Person>;
<http://schema.org/familyName> "Fullbuster";
<http://schema.org/givenName> "Gray" .
<http://www.example.com/2> a <http://schema.org/Person>;
<http://schema.org/familyName> "Redfox";
<http://schema.org/givenName> "Gajeel" .
<http://www.example.com/3> a <http://schema.org/Person>;
<http://schema.org/familyName> "Heartfilia";
<http://schema.org/givenName> "Lucy" .
<http://www.example.com/4> a <http://schema.org/Person>;
<http://schema.org/familyName> "Scarlet";
<http://schema.org/givenName> "Erza" .
Output locations
Currently, we output our generated RDF triples from the people
mapping
to a data resource at URL http://localhost:3000/people-data, serialized as Turtle.
We also want to output generated RDF triples implementing Solid's authorization rules
to an ACL resource linked to the data resource at URL http://localhost:3000/people-data.
The question is "what is the URL of this linked resource?"
We can discover this in our Solid pod.
We know from ACL Resource Discovery
that we have to send an HTTP request to http://localhost:3000/people-data
and read the Link
Header with the rel
value of acl
in the response.
The URL in that Link
header is the URL of the ACL resource we are looking for.
We can check this with a curl
command that displays response headers (using the -i
option):
curl -i http://localhost:3000/people-data
The output is
HTTP/1.1 401 Unauthorized
Vary: Accept,Authorization,Origin
X-Powered-By: Community Solid Server
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Accept-Patch,Accept-Post,Accept-Put,Allow,Content-Range,ETag,Last-Modified,Link,Location,Updates-Via,WAC-Allow,Www-Authenticate
Content-Type: application/json
Link: <http://localhost:3000/people-data.meta>; rel="describedby"
Link: <http://localhost:3000/.notifications/StreamingHTTPChannel2023/b0>; rel="http://www.w3.org/ns/solid/terms#updatesViaStreamingHttp2023"
Link: <http://localhost:3000/people-data.acl>; rel="acl"
WWW-Authenticate: Bearer scope="openid webid"
Date: Tue, 15 Jul 2025 11:41:27 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Transfer-Encoding: chunked
{"name":"UnauthorizedHttpError","message":"","statusCode":401,"errorCode":"H401","details":{}}
We do not have access to the contents of the data resource now as shown by HTTP/1.1 401 Unauthorized
,
but this is the Link
header we are looking for:
Link: <http://localhost:3000/people-data.acl>; rel="acl"
The conclusion is that the ACL resource's URL is http://localhost:3000/people-data.acl
.
Note: If you ask yourself now "Why all the fuzz, it's just the data resource's URL with
.acl
appended", you are right, but that is an implementation detail valid for the current version of the Community Solid Server we are using. It is not in the ACL resource specification.
What rules are needed
In our example, we need extra rules that define:
- how to generate triples implementing Solid's authorization rules for the web resource at URL http://localhost:3000/people-data,
- that a web resource linked to the web resource at URL http://localhost:3000/people-data contains the triples implementing Solid's authorization rules,
- how that linked web resource is linked to the web resource at URL http://localhost:3000/people-data,
- that the triples of that linked web resource are serialized as Turtle, and
- the authentication required for writing to that linked web resource.
How to generate triples for Solid's authorization rules
This section is only essential if you are interested in authorization rules as defined for Solid. If not, take the complete YARRRML document obtained in this section for granted and continue at How to output to linked web resources.
In our example,
we need to define how to generate triples implementing
Solid's authorization rules.
Therefore, we add a new mapping people-acl
.
Looking back at our people-acl.csv,
we see that each row has the information to produce the triples for one authorization:
id_acl | read_access | write_access | control_access | agent_webid | agent_class |
---|---|---|---|---|---|
0 | Yes | Yes | Yes | http://localhost:3000/profile/card#me | |
1 | Yes | No | No | http://xmlns.com/foaf/0.1/Agent |
We define a subject URI for the authorization using the value in column id_acl
and
define that the subject is an acl:Authorization
:
people-acl:
s:
value: ex:acl_$(id_acl)
po:
- [ a, acl:Authorization~iri ]
We define that the subject specifies access to our one and only data resource http://localhost:3000/people-data
using the predicate acl:accessTo
:
people-acl:
s:
value: ex:acl_$(id_acl)
po:
- [ a, acl:Authorization~iri ]
- [ acl:accessTo, http://localhost:3000/people-data~iri ]
Using the predicate acl:mode
, we add access mode acl:Read
,
only if the value in column read_access
is equal to Yes
.
To do so, we add a condition
key and in it use the function equal
to test the contents in column read_access
.
The access mode will only be added if the function returns true.
Now our mapping looks like this:
people-acl:
s:
value: ex:acl_$(id_acl)
po:
- [ a, acl:Authorization~iri ]
- [ acl:accessTo, http://localhost:3000/people-data~iri ]
- p: acl:mode
o: acl:Read~iri
condition:
function: equal
parameters:
- [ str1, $(read_access) ]
- [ str2, Yes ]
We define the agent and/or agent class to whom the authorization applies
using the predicates acl:agent
and acl:agentClass
and by reading the values from agent_webid
and agent_class
:
people-acl:
s:
value: ex:acl_$(id_acl)
po:
- [ a, acl:Authorization~iri ]
- [ acl:accessTo, http://localhost:3000/people-data~iri ]
- p: acl:mode
o: acl:Read~iri
condition:
function: equal
parameters:
- [ str1, $(read_access) ]
- [ str2, Yes ]
- p: acl:agent
o:
value: $(agent_webid)
type: iri
- p: acl:agentClass
o:
value: $(agent_class)
type: iri
After adding access modes acl:Write
and acl:Control
in a similar way as we did for acl:Read
and specifying the source for our new mapping people-acl
,
our complete YARRRML document looks like this:
prefixes:
acl: http://www.w3.org/ns/auth/acl#
e: http://myontology.com/
ex: http://www.example.com/
schema: http://schema.org/
sources:
source-people: ['people.csv~csv']
source-people-acl: ['people-acl.csv~csv']
targets:
target-people-data:
type: directhttprequest
access: http://localhost:3000/people-data
serialization: turtle
authentication: auth1
authentications:
auth1:
type: cssclientcredentials
email: test@example.com
password: secret!
webId: http://localhost:3000/profile/card#me
oidcIssuer: http://localhost:3000/
mappings:
people:
sources:
- source-people
s:
value: ex:$(person_id)
targets:
- target-people-data
po:
- [a, schema:Person]
- [schema:givenName, $(firstname)]
- [schema:familyName, $(lastname)]
people-acl:
sources:
- source-people-acl
s:
value: ex:acl_$(id_acl)
po:
- [ a, acl:Authorization~iri ]
- [ acl:accessTo, http://localhost:3000/people-data~iri ]
- p: acl:mode
o: acl:Read~iri
condition:
function: equal
parameters:
- [ str1, $(read_access) ]
- [ str2, Yes ]
- p: acl:mode
o: acl:Write~iri
condition:
function: equal
parameters:
- [ str1, $(write_access) ]
- [ str2, Yes ]
- p: acl:mode
o: acl:Control~iri
condition:
function: equal
parameters:
- [ str1, $(control_access) ]
- [ str2, Yes ]
- p: acl:agent
o:
value: $(agent_webid)
type: iri
- p: acl:agentClass
o:
value: $(agent_class)
type: iri
To produce RDF output,
we download
the above YARRRML document,
save it to auth-rules.yml
in our working directory,
and execute the mapping commands by calling our map.sh
script:
./map.sh auth-rules.yml
The mapping engine writes the RDF output from mapping people-acl
to the console:
<http://www.example.com/acl_0> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/auth/acl#Authorization> .
<http://www.example.com/acl_0> <http://www.w3.org/ns/auth/acl#accessTo> <http://localhost:3000/people-data> .
<http://www.example.com/acl_0> <http://www.w3.org/ns/auth/acl#agent> <http://localhost:3000/profile/card#me> .
<http://www.example.com/acl_0> <http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Control> .
<http://www.example.com/acl_0> <http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Read> .
<http://www.example.com/acl_0> <http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Write> .
<http://www.example.com/acl_1> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/ns/auth/acl#Authorization> .
<http://www.example.com/acl_1> <http://www.w3.org/ns/auth/acl#accessTo> <http://localhost:3000/people-data> .
<http://www.example.com/acl_1> <http://www.w3.org/ns/auth/acl#agentClass> <http://xmlns.com/foaf/0.1/Agent> .
<http://www.example.com/acl_1> <http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Read> .
How to output to linked web resources
Now that we have the triples for Solid's authorization rules defining access to http://localhost:3000/people-data
,
we still have to put them in the right ACL resource to make them effective.
Here is where linked HTTP request targets come in.
If we assign a linked HTTP request target to a mapping,
the mapping engine will discover the URL of the ACL resource for us,
and write the RDF output to the linked resource.
These are the requirements for our linked HTTP request target:
- it must discover a resource linked to
http://localhost:3000/people-data
; - it must read the resource's URL in the Link header where
rel="acl"
; - it must use serialization
turtle
; - it must use authentication
auth1
.
We do this as follows:
target-people-acl:
type: linkedhttprequest
access: http://localhost:3000/people-data
rel: acl
serialization: turtle
authentication: auth1
We specify that this new target is a target of our people-acl
mapping and
get the following complete YARRRML document:
prefixes:
acl: http://www.w3.org/ns/auth/acl#
e: http://myontology.com/
ex: http://www.example.com/
schema: http://schema.org/
sources:
source-people: ['people.csv~csv']
source-people-acl: ['people-acl.csv~csv']
targets:
target-people-data:
type: directhttprequest
access: http://localhost:3000/people-data
serialization: turtle
authentication: auth1
target-people-acl:
type: linkedhttprequest
access: http://localhost:3000/people-data
rel: acl
serialization: turtle
authentication: auth1
authentications:
auth1:
type: cssclientcredentials
email: test@example.com
password: secret!
webId: http://localhost:3000/profile/card#me
oidcIssuer: http://localhost:3000/
mappings:
people:
sources:
- source-people
s:
value: ex:$(person_id)
targets:
- target-people-data
po:
- [a, schema:Person]
- [schema:givenName, $(firstname)]
- [schema:familyName, $(lastname)]
people-acl:
sources:
- source-people-acl
s:
value: ex:acl_$(id_acl)
targets:
- target-people-acl
po:
- [ a, acl:Authorization~iri ]
- [ acl:accessTo, http://localhost:3000/people-data~iri ]
- p: acl:mode
o: acl:Read~iri
condition:
function: equal
parameters:
- [ str1, $(read_access) ]
- [ str2, Yes ]
- p: acl:mode
o: acl:Write~iri
condition:
function: equal
parameters:
- [ str1, $(write_access) ]
- [ str2, Yes ]
- p: acl:mode
o: acl:Control~iri
condition:
function: equal
parameters:
- [ str1, $(control_access) ]
- [ str2, Yes ]
- p: acl:agent
o:
value: $(agent_webid)
type: iri
- p: acl:agentClass
o:
value: $(agent_class)
type: iri
To produce RDF output,
we download
the above YARRRML document,
save it to auth-rules-linked.yml
in our working directory,
and execute the mapping commands by calling our map.sh
script:
./map.sh auth-rules-linked.yml
To prove that our mapping works,
try again reading http://localhost:3000/people-data using curl
.
Remember, the last time we did so we received 401 Unauthorized
.
curl http://localhost:3000/people-data
This time it works. The output is
<http://www.example.com/0> a <http://schema.org/Person>;
<http://schema.org/familyName> "Dragneel";
<http://schema.org/givenName> "Natsu" .
<http://www.example.com/1> a <http://schema.org/Person>;
<http://schema.org/familyName> "Fullbuster";
<http://schema.org/givenName> "Gray" .
<http://www.example.com/2> a <http://schema.org/Person>;
<http://schema.org/familyName> "Redfox";
<http://schema.org/givenName> "Gajeel" .
<http://www.example.com/3> a <http://schema.org/Person>;
<http://schema.org/familyName> "Heartfilia";
<http://schema.org/givenName> "Lucy" .
<http://www.example.com/4> a <http://schema.org/Person>;
<http://schema.org/familyName> "Scarlet";
<http://schema.org/givenName> "Erza" .
Note: we cannot read back what we wrote to the ACL resource. The above
curl
command is just an indirect proof that it works. As an unauthenticated user, this time we were able to read the data resource. This proves at least that our authorizationhttp://www.example.com/acl_1
works.
To check the contents of our ACL resource, we can view it in our ./Solid
subdirectory.
It is located at ./Solid/people-data.acl
and has the content:
<http://www.example.com/acl_0> a <http://www.w3.org/ns/auth/acl#Authorization>;
<http://www.w3.org/ns/auth/acl#accessTo> <http://localhost:3000/people-data>;
<http://www.w3.org/ns/auth/acl#agent> <http://localhost:3000/profile/card#me>;
<http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Control>, <http://www.w3.org/ns/auth/acl#Read>,
<http://www.w3.org/ns/auth/acl#Write> .
<http://www.example.com/acl_1> a <http://www.w3.org/ns/auth/acl#Authorization>;
<http://www.w3.org/ns/auth/acl#accessTo> <http://localhost:3000/people-data>;
<http://www.w3.org/ns/auth/acl#agentClass> <http://xmlns.com/foaf/0.1/Agent>;
<http://www.w3.org/ns/auth/acl#mode> <http://www.w3.org/ns/auth/acl#Read> .
Wrapping up
Congratulations! You have created your own YARRRML rules that:
- result in triples for Solid authorization rules,
- output RDF to linked web resources.
Good job! We hope you now understand how to use linked HTTP request targets.
More information
You can find more information in the following: