Copyright © IBM Corporation 2012. This document is available under the W3C Document License. See the W3C Intellectual Rights Notice and Legal Disclaimers for additional information.
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 can be found in the W3C technical reports index at http://www.w3.org/TR/.
This document is a part of the Linked Data Basic Profile Submission, which comprises two documents:
By publishing this document, W3C acknowledges that the Submitting Members have made a formal Submission request to W3C for discussion. Publication of this document by W3C indicates no endorsement of its content by W3C, nor that W3C has, is, or will be allocating any resources to the issues addressed by it. This document is not the product of a chartered W3C group, but is published as potential input to the W3C Process. A W3C Team Comment has been published in conjunction with this Member Submission. Publication of acknowledged Member Submissions at the W3C site is one of the benefits of W3C Membership. Please consult the requirements associated with Member Submissions of section 3.3 of the W3C Patent Policy. Please consult the complete list of acknowledged W3C Member Submissions.
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 [4Rules]
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 profiles. The scope is intentionally narrow to provide a set of key rules for reading and writing Linked Data that most, if not all, other profiles 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 [RFC2616]
Conformance is defined in accordance with the use of the words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY and RECOMMENDED as described in RFC 2119 [RFC2119] .
Conformance statements on BPR representations apply to servers and clients that either create or generate representations that represent the state of a BPR.
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 bp: <http://open-services.net/ns/basicProfile#>. @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
Basic Profile Resources (BPRs) are HTTP resources that conform to the simple patterns and conventions in this section. HTTP requests to access, modify, create or delete BPRs are accepted and processed by BPR servers. Most BPRs 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 [4Rules], others address additional needs.
The rules for Basic Profile Resources address basic questions such as:
The following sections define the rules and guidelines for use of BPRs.
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#date | Date type as specified by XSD date |
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.
application/rdf+xml
representation of the requested BPR.
text/turtle
representation of the requested BPR.
rdf:type
.
rdf:type
values
of a given BPR may change over time.
There are no additional requirements on HTTP POST for BPRs.
dcterms:modified
and dcterms:creator
that are never under
client control - BPR servers MUST ignore any values of these
properties that are provided by the client. Any BPR 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. BPR servers SHOULD require the HTTP If-Match
header and HTTP ETags
to detect collisions. BPR servers MUST respond with status code 412
(Condition Failed) if ETags fail to match if there are no other
errors with the request. [RFC2616]
rdf:type
, datatypes of
predicates and number of occurrences of predicates in triples, but
servers SHOULD minimize those restrictions. In other words, BPR
servers need to enable simple modification of BPRs. Enforcement of
more complex constraints will greatly restrict the types of clients
who 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 Basic Profile 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
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 | 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 |
he 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:Resource |
Only use this in vocabulary documents, to define the name of the vocabulary term. |
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 BPC 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 bp: <http://open-services.net/ns/basicProfile#>. <http://example.org/container1> a bp: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 bp: <http://open-services.net/ns/basicProfile#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a bp:Container; bp:membershipSubject <http://example.org/netWorth/nw1>; bp: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
has 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 BPC 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. BPC 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 JohnZSmith 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 bp: <http://open-services.net/ns/basicProfile#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a bp:Container; dcterms:title "The assets of JohnZSmith"; bp:membershipSubject <http://example.org/netWorth/nw1>; bp: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 BPC 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, which may not seem important enough to warrant addressing this problem. There are cases that add other predicates to containers, for example providing validation information and associating SPARQL endpoints.
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 bp: <http://open-services.net/ns/basicProfile#>. <http://example.org/container1> a bp:Container; dcterms:title "A Basic Profile Container of Acme Resources"; bp: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, BPCs 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.
BPC 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 2 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 bp: <http://open-services.net/ns/basicProfile#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a bp:Container; dcterms:title "The assets of JohnZSmith"; bp:membershipSubject <http://example.org/netWorth/nw1>; bp:membershipPredicate o:asset. <http://example.org/netWorth/nw1/assetContainer?firstPage> a bp:Page; bp:pageOf <http://example.org/netWorth/nw1/assetContainer>; bp: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 bp: <http://open-services.net/ns/basicProfile#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a bp:Container; dcterms:title "The assets of JohnZSmith"; bp:membershipSubject <http://example.org/netWorth/nw1>; bp:membershipPredicate o:asset. <http://example.org/netWorth/nw1/assetContainer?p=2> a bp:Page; bp:pageOf <http://example.org/netWorth/nw1/assetContainer>; bp: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 bp:nextPage
predicate of the page resource.
BPC 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. BPC 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 above, 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, BPC avoids the use of RDF
constructs like Seq and List for expressing order.
Order only becomes important for BPC servers when containers are
paginated. If the server does not respect ordering when constructing
pages, the client would be forced to retrieve all pages before
sorting the members, which would defeat the purpose of pagination. In
cases where ordering is important, a BPC 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 BPC specification provides a predicate - bp: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 bp: <http://open-services.net/ns/basicProfile#>. @prefix o: <http://example.org/ontology/>. <http://example.org/netWorth/nw1/assetContainer> a bp:Container; dcterms:title "The assets of JohnZSmith"; bp:membershipSubject <http://example.org/netWorth/nw1>; bp:membershipPredicate o:asset. <http://example.org/netWorth/nw1/assetContainer?firstPage> a bp:Page; bp:pageOf <http://example.org/netWorth/nw1/assetContainer>; bp: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 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.
rdfs:member
predicate.
bp:membershipSubject
predicate that indicates the subject of the membership triples.
bp:membershipPredicate
predicate that indicates the predicate of the membership triple when
the container predicate is not rdfs:member
.
rdf:type
of bp: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 BPC URL. For example, if there is a BPC URL <containerURL>,
the URL to request the
non-membership properties would be <containerURL>?non-member-properties
.
See section 5.1.2 titled “Retrieving Non-member Properties” for
additional details. A BPC 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 BPC URL. For example, if there is a BPC
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. If paging is supported, the last page
MUST be signaled by the presence of rdf:nil
object as the URL for the
next page. See section 5.1.3 titled “Paging” for additional details.
firstPage
”
query component of request URL). This is also known as
server-initiated paging. See section 5.1.3 titled “Paging” for
additional details.
rdf:type
and object is bp:Page;
it also SHOULD have 1 triple to indicate the container it is paging,
whose subject is the URL of the page, predicate is bp:pageOf,
and object is the URL of the BPC.
bp:nextPage
and
object being the URL for the subsequent page.
bp:containerSortPredicates
predicate whose subject is that of the page and object is a list of
BPC 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].
bp:containerSortPredicates
’ object –
predicate used to indicate ordering – MUST NOT change between
subsequent pages, otherwise ordering between members of a container
across pages is undefined. See section 5.1.4 titled “Ordering” for
additional details.
The Basic Profile does not define how clients discover BPCs.
Location
header set to the new resource’s URL.
application/rdf+xml
.
text/turtle
.
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 create resources and
therefore discouraged.
<containerURL>?non-member-properties
, which
MAY exclude server managed properties such as bp:membershipSubject
and bp:membershipPredicate
.
There are no additional requirements on HTTP HEAD.
Additional Contributors:
Arthur Ryman (IBM), Ian Green (IBM), Barbara McKee (IBM), Sally Hehir (IBM)