Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C liability, trademark and document use rules apply.
A set of best practices and simple approach for a read-write Linked Data architecture, based on HTTP access to web resources that describe their state using RDF.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This First Public Working Draft was published by the Linked Data Platform Working Group. This document builds on a W3C Member Submission. All known issues are documented inline as well as in the LDP WG Issue Tracker. If you wish to make comments regarding this document, please send them to public-ldp@w3.org (subscribe, archives). All feedback is welcome.
Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document describes the use of HTTP for accessing, updating, creating and deleting resources from servers that expose their resources as Linked Data. It provides some new rules as well as clarifications and extensions of the four rules of Linked Data [LINKED-DATA]
1. Use URIs as names for things
2. Use HTTP URIs so that people can look up those names
3. When someone looks up a URI, provide useful information, using the standards (RDF*, SPARQL)
4. Include links to other URIs. so that they can discover more things
The best practices and anti-patterns covered in this document are:
Resources - a summary of the HTTP and RDF standard techniques and best practices that you should use, and anti-patterns you should avoid, when constructing clients and servers that read and write linked data.
Containers - defines resources that allow new resources to be created using HTTP POST and existing resources to be found using HTTP GET.
Additionally, it is the intention of this document to enable additional rules and layered groupings of rules, such as additional specifications. The scope is intentionally narrow to provide a set of key rules for reading and writing Linked Data that most, if not all, other specifications will depend on and implementations will support.
Terminology is based on W3C's Architecture of the World Wide Web [WEBARCH] and Hyper-text Transfer Protocol [HTTP11]
Sample resource representations are provided in text/turtle
format [TURTLE].
Commonly used namespace prefixes:
@prefix dcterms: <http://purl.org/dc/terms/>. @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. @prefix ldp: <http://www.w3.org/ns/ldp#>. @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words must, must not, required, should, should not, recommended, may, and optional in this specification are to be interpreted as described in [RFC2119].
Linked Data Platform Resources (LDPRs) are HTTP resources that conform to the simple patterns and conventions in this section. HTTP requests to access, modify, create or delete LDPRs are accepted and processed by LDPR servers. Most LDPRs are domain-specific resources that contain data for an entity in some domain, which could be commercial, governmental, scientific, religious, or other.
Some of the rules defined in this document provide clarification and refinement of the base Linked Data rules [LINKED-DATA], others address additional needs.
The rules for Linked Data Platform Resources address basic questions such as:
The following sections define the rules and guidelines for use of LDPRs.
rdf:type
to
represent the concept of type. The use of non-standard type
predicates, as well as dcterms:type
, is
discouraged. [DC-RDF]
rdf:type
set explicitly. This makes the representations much more useful to
client applications that don’t support inferencing.
URI | Description |
---|---|
http://www.w3.org/2001/XMLSchema#boolean | Boolean type as specified by XSD Boolean |
http://www.w3.org/2001/XMLSchema#date | Date type as specified by XSD date |
http://www.w3.org/2001/XMLSchema#dateTime | Date and Time type as specified by XSD dateTime |
http://www.w3.org/2001/XMLSchema#decimal | Decimal number type as specified by XSD Decimal |
http://www.w3.org/2001/XMLSchema#double | Double floating-point number type as specified by XSD Double |
http://www.w3.org/2001/XMLSchema#float | Floating-point number type as specified by XSD Float |
http://www.w3.org/2001/XMLSchema#integer | Integer number type as specified by XSD Integer |
http://www.w3.org/2001/XMLSchema#string | String type as specified by XSD String |
http://www.w3.org/1999/02/22-rdf-syntax-ns#XMLLiteral | Literal XML value as specified by RDF |
ETag
header values.
text/turtle
representation of the requested LDPR.[TURTLE]
application/rdf+xml
representation of the requested LDPR.[RDF-SYNTAX]
rdf:type
.
rdf:type
values
of a given LDPR may change over time.
There are no additional requirements on HTTP POST for LDPRs.
dcterms:modified
and dcterms:creator
that are never under
client control - LDPR servers must ignore any values of these
properties that are provided by the client. Any LDPR servers that wish
to support a more sophisticated merge of data provided by the client
with existing state stored on the server for a resource must use HTTP
PATCH, not HTTP PUT.
If-Match
header and HTTP ETags
to ensure it isn’t
modifying a resource that has changed since the client last retrieved
its representation. LDPR servers should require the HTTP If-Match
header and HTTP ETags
to detect collisions. LDPR servers must respond with status code 412
(Condition Failed) if ETag
s fail to match if there are no other
errors with the request. [HTTP11]
rdf:type
, datatypes of
predicates and number of occurrences of predicates in triples, but
servers should minimize those restrictions. In other words, LDPR
servers need to enable simple modification of LDPRs. Enforcement of
more complex constraints will greatly restrict the types of clients
that can modify resources. For some server applications, excessive
constraints on modification of resources may be required.
Allow
”.
rdf:type
, datatypes of
predicates and number of occurrences of predicates in triples – but
server enforcement of detailed, domain-specific constraints will
greatly restrict the types of clients who can update resources.
This section summarizes some well-known RDF vocabularies that must be
used in Linked Data Platform Resources wherever a resource needs to use a
predicate whose meaning matches one of these. For example, if a BP
resource has a description, and the application semantic of that
description is compatible with dcterms:description
,
then dcterms:description
must be used. If
needed, additional application-specific predicates may be used. A
specification for a domain that is based on BP may require one or
more of these properties for a particular resource type. The Range
column in the tables below identify the recommended rdfs:range
for the properties.
URI: http://purl.org/dc/terms/
Property | Range/DataType | Comment |
dcterms:contributor |
dcterms:Agent |
|
dcterms:creator |
dcterms:Agent |
|
dcterms:created |
xsd:dateTime |
|
dcterms:description |
rdf:XMLLiteral |
Descriptive text about the resource represented as rich text in
XHTML format. should include only content that is valid and suitable inside an XHTML
<div> element.
|
dcterms:identifier |
rdfs:Literal |
|
dcterms:modified |
xsd:dateTime |
|
dcterms:relation |
rdfs:Resource |
The HTTP URI of a related resource. This is the predicate to use when you don't know what else to use. If you know more specifically what sort of relationship it is, use a more specific predicate. |
dcterms:subject |
rdfs:Resource |
|
dcterms:title |
rdf:XMLLiteral |
A name given to the resource. Represented as rich text in XHTML
format. should include only content that is valid inside an XHTML <span>
element. |
The predicate dcterms:type
should not be
used, instead use rdf:type
. [DC-RDF].
URI: http://www.w3.org/1999/02/22-rdf-syntax-ns#
Property | Range | Comment |
rdf:type |
rdfs:Class |
The type or types of the resource |
URI: http://www.w3.org/2000/01/rdf-schema#
Property | Range | Comment |
rdfs:member |
rdfs:Resource |
|
rdfs:label |
rdfs:Literal |
Only use this in vocabulary documents, to define the name of the vocabulary term. |
This section is non-normative.
Many HTTP applications and sites have organizing concepts that partition the overall space of resources into smaller containers. Blog posts are grouped into blogs, wiki pages are grouped into wikis, and products are grouped into catalogs. Each resource created in the application or site is created within an instance of one of these container-like entities, and users can list the existing artifacts within one. Containers answer some basic questions, which are:
This document defines the representation and behavior of containers
that address these issues. The set of members of a container is
defined by a set of triples in its representation (and state) called
the membership triples. The membership triples of a container all
have the same subject and predicate – the objects of the membership
triples define the members of the container. The subject of the
membership triples is called the membership subject and the predicate
is called the membership predicate. In the simplest cases, the
membership subject will be the LDPC resource itself, but it does not
have to be. The membership predicate is also variable and will often
be a predicate from the server application vocabulary or the rdfs:member
predicate.
This document includes a set of guidelines for using POST to create new resources and add them to the list of members of a container. This document also explains how to include information about each member in the container’s own representation and how to paginate the container representation if it gets too big.
The following illustrates a very simple container with only three members and some information about the container (the fact that it is a container and a brief title):
# The following is the representation of # http://example.org/container1 @prefix dcterms: <http://purl.org/dc/terms/>. @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. @prefix ldp: <http://www.w3.org/ns/ldp#>. <http://example.org/container1> a ldp:Container; dcterms:title "A very simple container"; rdfs:member <http://example.org/container1/member1>, <http://example.org/container1/member2>, <http://example.org/container1/member3>.
This example is very straightforward - the
membership predicate is rdfs:member
and the membership subject is the container
itself. A POST to this container will create a new resource
and add it to the list of members by adding a new membership triple
to the container.
Sometimes it is useful to use a subject
other than the container itself as the membership subject and to use
a predicate other than rdfs:member
as the membership predicate, as illustrated
below.
# The following is the representation of # http://example.org/netWorth/nw1/assetContainer @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. @prefix ldp: <http://www.w3.org/ns/ldp#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a ldp:Container; ldp:membershipSubject <http://example.org/netWorth/nw1>; ldp:membershipPredicate o:asset. <http://example.org/netWorth/nw1> a o:NetWorth; o:asset <http://example.org/netWorth/nw1/assetContainer/a1>, <http://example.org/netWorth/nw1/assetContainer/a2>.
The essential structure of the container is
the same, but in this example, the membership subject is not the
container itself – it is a separate net worth resource. The
membership predicate is o:asset
– a predicate from the domain model. A POST to
this container will create a new asset and add it to the list of
members by adding a new membership triple to the container. You
might wonder why we didn’t just make http://example.org/netWorth/nw1
a container and POST
the new asset directly there. That would be a fine design if http://example.org/netWorth/nw1
had only assets, but if it has separate
predicates for assets and liabilities, that design will not work
because it is unspecified to which predicate the POST should add a
membership triple. Having separate http://example.org/netWorth/nw1/assetContainer
and http://example.org/netWorth/nw1/liabilityContainer
container resources allows both assets and
liabilities to be created.
In this example, clients cannot simply guess which resource is the membership subject and which predicate is the membership predicate, so the example includes this information in triples whose subject is the LDPC resource itself.
In many – perhaps most – applications involving containers, it is desirable for the client to be able to get information about each container member without having to do a GET on each one. LDPC allows servers to include this information directly in the representation of the container. The server decides the amount of data about each member that is provided. Some common strategies include providing a fixed number of standard properties, or providing the entire RDF representation of each member resource, or providing nothing. The server application domain and its use-cases will determine how much information is required.
Continuing on from the net worth example, there will be additional triples for the member resources (assets) in the representation:
# The following is the representation of # http://example.org/netWorth/nw1/assetContainer @prefix dcterms: <http://purl.org/dc/terms/>. @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. @prefix ldp: <http://www.w3.org/ns/ldp#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a ldp:Container; dcterms:title "The assets of JohnZSmith"; ldp:membershipSubject <http://example.org/netWorth/nw1>; ldp:membershipPredicate o:asset. <http://example.org/netWorth/nw1> a o:NetWorth; o:asset <http://example.org/netWorth/nw1/assetContainer/a1>, <http://example.org/netWorth/nw1/assetContainer/a3>, <http://example.org/netWorth/nw1/assetContainer/a2>. <http://example.org/netWorth/nw1/assetContainer/a1> a o:Stock; o:value 10000. <http://example.org/netWorth/nw1/assetContainer/a2> a o:Bond; o:value 20000. <http://example.org/netWorth/nw1/assetContainer/a3> a o:RealEstateHolding; o:value 300000.
The representation of a container that has many members will be large. There are several important cases where clients need to access only the non-member properties of the container. Since retrieving the whole container representation to get this information may be onerous for clients and cause unnecessary overhead on servers, it is desired to define a way to retrieve only the non-member property values. Defining for each LDPC a corresponding resource, called the “non-member resource”, whose state is a subset of the state of the container, does this.
The example listed here only show a simple case where only a few simple non-member properties are retrieved. In real world situations more complex cases are likely, such as those that add other predicates to containers, for example providing validation information and associating SPARQL endpoints. [SPARQL-QUERY]
Here is an example requesting the non-member properties of a
container identified by the URL http://example.org/container1
and adding the query string ?non-member-properties
:
Request:
GET /container1?non-member-properties HTTP/1.1 Host: example.org Accept: text/turtle; charset=UTF-8
Response:
HTTP/1.1 200 OK Content-Type: text/turtle; chartset=UTF-8 ETag: "_87e52ce291112" Content-Length: 325 @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>. @prefix dcterms: <http://purl.org/dc/terms/>. @prefix ldp: <http://www.w3.org/ns/ldp#>. <http://example.org/container1> a ldp:Container; dcterms:title "A Linked Data Platform Container of Acme Resources"; ldp:membershipPredicate rdfs:member; dcterms:publisher <http://acme.com/>.
It sometimes happens that a container is too large to reasonably transmit its representation in a single HTTP response. This will be especially true if the container representation includes many triples from the representations of its members. A client may anticipate that a container will be too large - for example, a client tool that accesses defects may assume that an individual defect will usually be of sufficiently constrained size that it makes sense to request all of it at once, but that the container of all the defects ever created will typically be too big. Alternatively, a server may recognize that a container that has been requested is too big to return in a single message.
To address this problem, LDPCs may support a technique called Paging. Paging can be achieved with a
simple RDF pattern. For each container resource, <containerURL>
, we define a new
resource <containerURL>?firstPage
.
The triples in the representation of <containerURL>?firstPage
are a subset of the triples in <containerURL>
- same subject, predicate and object.
LDPC servers may respond to requests for a container by redirecting the client to the first page resource – using a 303 “See Other” redirect to the actual URL for the page resource.
Continuing on from the member information from the JohnZSmith net
worth example, we’ll split the response across two pages. The client
requests the first page as http://example.org/netWorth/nw1/assetContainer?firstPage
:
# The following is the representation of # http://example.org/netWorth/nw1/assetContainer?firstPage @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. @prefix dcterms: <http://purl.org/dc/terms/>. @prefix ldp: <http://www.w3.org/ns/ldp#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a ldp:Container; dcterms:title "The assets of JohnZSmith"; ldp:membershipSubject <http://example.org/netWorth/nw1>; ldp:membershipPredicate o:asset. <http://example.org/netWorth/nw1/assetContainer?firstPage> a ldp:Page; ldp:pageOf <http://example.org/netWorth/nw1/assetContainer>; ldp:nextPage <http://example.org/netWorth/nw1/assetContainer?p=2>. <http://example.org/netWorth/nw1> a o:NetWorth; o:asset <http://example.org/netWorth/nw1/assetContainer/a1>, <http://example.org/netWorth/nw1/assetContainer/a4>, <http://example.org/netWorth/nw1/assetContainer/a3>, <http://example.org/netWorth/nw1/assetContainer/a2>. <http://example.org/netWorth/nw1/assetContainer/a1> a o:Stock; o:value 100.00. <http://example.org/netWorth/nw1/assetContainer/a2> a o:Cash; o:value 50.00. # server initially supplied no data for a3 and a4 in this response
The following example is the result of retrieving the representation for the next page:
# The following is the representation of # http://example.org/netWorth/nw1/assetContainer?p=2 @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. @prefix dcterms: <http://purl.org/dc/terms/>. @prefix ldp: <http://www.w3.org/ns/ldp#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a ldp:Container; dcterms:title "The assets of JohnZSmith"; ldp:membershipSubject <http://example.org/netWorth/nw1>; ldp:membershipPredicate o:asset. <http://example.org/netWorth/nw1/assetContainer?p=2> a ldp:Page; ldp:pageOf <http://example.org/netWorth/nw1/assetContainer>; ldp:nextPage rdf:nil. <http://example.org/netWorth/nw1> a o:NetWorth; o:asset <http://example.org/netWorth/nw1/assetContainer/a5>. <http://example.org/netWorth/nw1/assetContainer/a5> a o:Stock; dcterms:title "Big Co."; o:value 200.02.
In this example, there is only one member in the container in the
final page. To indicate this is the last page, a value of rdf:nil
is used for the ldp:nextPage
predicate of the page resource.
LDPC guarantees that any and all the triples about the members will be on the same page as the membership triple for the member.
There are many cases where an ordering of the members of the
container is important. LDPC does not provide any particular support
for server ordering of members in containers, because any client can
order the members in any way it chooses based on the value of any
available property of the members. In the example below, the value of
the o:value
predicate is present for each
member, so the client can easily order the members according to the
value of that property. In this way, LDPC avoids the use of RDF
constructs like Seq and List for expressing order.
Order only becomes important for LDPC servers when containers are
paginated. If the server does not respect ordering when constructing
pages, the client is forced to retrieve all pages before
sorting the members, which would defeat the purpose of pagination. In
cases where ordering is important, a LDPC server exposes all the
members on a page with a higher sort order than all members on the
previous page and lower sort order than all the members on the next
page. The LDPC specification provides a predicate - ldp:containerSortPredicates
- that the server may use to communicate to the client which
predicates were used for page ordering. Multiple predicate values may
have been used for sorting, so the value of this predicate is an
ordered list.
Here is an example container described previously, with representation for ordering of the assets:
# The following is the ordered representation of # http://example.org/netWorth/nw1/assetContainer @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>. @prefix dcterms: <http://purl.org/dc/terms/>. @prefix ldp: <http://www.w3.org/ns/ldp#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a ldp:Container; dcterms:title "The assets of JohnZSmith"; ldp:membershipSubject <http://example.org/netWorth/nw1>; ldp:membershipPredicate o:asset. <http://example.org/netWorth/nw1/assetContainer?firstPage> a ldp:Page; ldp:pageOf <http://example.org/netWorth/nw1/assetContainer>; ldp:containerSortPredicates (o:value). <http://example.org/netWorth/nw1> a o:NetWorth; o:asset <http://example.org/netWorth/nw1/assetContainer/a1>, <http://example.org/netWorth/nw1/assetContainer/a3>, <http://example.org/netWorth/nw1/assetContainer/a2>. <http://example.org/netWorth/nw1/assetContainer/a1> a o:Stock; o:value 100.00. <http://example.org/netWorth/nw1/assetContainer/a2> a o:Cash; o:value 50.00. <http://example.org/netWorth/nw1/assetContainer/a3> a o:RealEstateHolding; o:value 300000.
As you can see by the addition of the ldp:containerSortPredicates
predicate, the o:value
predicate is used
to define the ordering of the results. It is up to the domain model
and server to determine the appropriate predicate to indicate the
resource’s order within a page, and up to the client receiving this
representation to use that order in whatever way is appropriate, for
example to sort the data prior to presentation on a user interface.
This section is non-normative.
rdfs:member
predicate.
ldp:membershipSubject
predicate that indicates the subject of the membership triples when container subject is not the LDPC itself.
ldp:membershipPredicate
predicate that indicates the predicate of the membership triple when
the container predicate is not rdfs:member
.
rdf:type
of ldp:Container
, but it may have additional
rdf:type
s.
rdf:Bag
,
rdf:Seq
and rdf:List
.
non-member-properties
" on the query
component of the LDPC URL. For example, if there is a LDPC URL <containerURL>,
the URL to request the
non-membership properties would be <containerURL>?non-member-properties
.
See section 5.1.2 Retrieving Non-member Properties for
additional details. A LDPC server that does not support a request to
retrieve non-member resource properties via a Request-URI of “<containerURL>?non-member-properties
”,
must return a HTTP status code 404 (Not Found).
<containerURL>
”,
the Request-URI of “<containerURL>?firstPage
”, must return a HTTP status code 404 (Not
Found).
firstPage
”
on the query component of the LDPC URL. For example, if there is a LDPC
URL <containerURL>
, the URL to request
the first page would be <containerURL>?firstPage
.
The representation for any page, including the first, will include
the URL for the next page. See section 5.1.3 titled “Paging” for additional details.
firstPage
”
query component of a request URL). This is also known as
server-initiated paging. See section 5.1.3 Paging for
additional details.
rdf:type
and object is ldp:Page;
it also should have 1 triple to indicate the container it is paging,
whose subject is the URL of the page, predicate is ldp:pageOf,
and object is the URL of the LDPC.
ldp:nextPage
and
object being the URL for the subsequent page.
ldp:nextPage
and object being rdf:nil
.
ldp:containerSortPredicates
predicate whose subject is that of the page and object is a list of
LDPC ordinal predicates. The default ordering is ascending. The only
ordinal predicate literal data types supported are those as defined
by SPARQL SELECT’s ORDER BY clause [SPARQL-QUERY].
ldp:containerSortPredicates
’,
the predicate used to indicate ordering, must not change between
subsequent pages. If it does, ordering among members of a container
across pages is undefined. See section 5.1.4 Ordering for
additional details.
The Linked Data Platform does not define how clients discover LDPCs.
Location
header set to the new resource’s URL.
text/turtle
[TURTLE].
application/rdf+xml
[RDF-SYNTAX].
rdf:type
, datatypes of
predicates and number of occurrences of predicates in triples - but
server enforcement of detailed, domain-specific constraints will
greatly restrict the types of clients that can create resources and
therefore discouraged.
<containerURL>?non-member-properties
, which
may exclude server managed properties such as ldp:membershipSubject
and ldp:membershipPredicate
.
There are no additional requirements on HTTP HEAD.
This section is non-normative.
The following people have been instrumental in providing thoughts, feedback, reviews, criticism and input in the creation of this specification:
This section is non-normative.
ldp
namespace (SS)This section is non-normative.
Other than LDP open actions and issues, included here are transient tasks and notes editors use. They have not meaning in final product of a published working draft and will be removed prior to publishing.