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:

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:

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 authorization http://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:

Table of contents