LENABI: Prototype of new metadata API for serlo.org

The metadata format

We use the specification Allgemeines Metadatenprofil für Bildungsressourcen to describe our learning resources. It uses JSON-LD as a format with schema.org as the main vocabulary. For example, the metadata for the article "Addition" is:

{
  "@context": [
    "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
    {
      "@language": "de"
    }
  ],
  "id": "https://serlo.org/1495",
  "type": [
    "LearningResource",
    "Article"
  ],
  "dateCreated": "2014-03-01T20:36:44+00:00",
  "dateModified": "2021-03-08T20:51:17+00:00",
  "description": "Addition, auch Plusrechnen genannt, geh\\u00f6rt zu den Grundrechenarten der Mathematik. Lerne, was ein Summand ist. \\u21d2 Hier lernst du, dass das Assoziativgesetz und Kommutativgesetz gelten.  \\u21d2 Veranschaulichung durch Merktabellen und Zahlengeraden. F\\u00fcr den Anfang kannst du auch schriftlich addieren! Viele \\u00dcbungsaufgaben sind verf\\u00fcgbar.\\u2713 Lernen mit Serlo!",
  "headline": "Addition",
  "identifier": {
    "type": "PropertyValue",
    "propertyID": "UUID",
    "value": 1495
  },
  "inLanguage": [
    "de"
  ],
  "isAccessibleForFree": true,
  "isFamilyFriendly": true,
  "learningResourceType": "Article",
  "license": {
    "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
  },
  "maintainer": "https://serlo.org/",
  "name": "Addition",
  "publisher": [
    {
      "id": "https://serlo.org/"
    }
  ],
  "version": "https://serlo.org/197588"
}

Each property is a schema.org property. For example, identifier is the same as the property https://schema.org/identifier. The site https://json-ld.org/#developers lists tools for working with JSON-LD.

Further reading

Accessing the metadata via GraphQL

The metadata can be accessed via our GraphQL endpoint at https://api.serlo-staging.dev/graphql (serlo-staging.dev is our testing environment). In the query namespace metadata, we have two properties: publisher and entities. publisher points to the metadata about Serlo Education e.V. (i.e. the publisher). Via entities you can access the metadata about our learning resources. entities supports pagination via the properties first and after, filtering by language via instance and filtering by modification date via modifiedAfter:

extend type Query {
  metadata: MetadataQueryNamespace!
}

type MetadataQueryNamespace {
  # Returns metadata about Serlo Education e.V.
  publisher: JSONObject!

  # Returns metadata about learning resources with pagination and filter options
  entities(
    first: Int,           # Number of metadata objects which shall
                          # be returned (default is 100)
    after: String,        # Cursor to the entity after which the
                          # new metadata shall be returned (see `endCursor` in `HasNextPageInfo`)
    instance: Instance,   # Filter for the subdomain / language
    modifiedAfter: String # Filters entities which have been modified
                          # after the given date (format YYYY-MM-DDTHH:MM:SSZ)
  ): EntitiesMetadataConnection!
}

type EntitiesMetadataConnection {
  # Array of metadata objects
  nodes: [JSONObject!]!

  # Information whether there are more resources to query
  pageInfo: HasNextPageInfo!
}

type HasNextPageInfo {
  # Is true when more metadata objects can be queried
  hasNextPage: Boolean!

  # Cursor which needs to be passed to `after` in order to fetch more metadata objects
  endCursor: String
}

The site https://graphql.org/code/ lists client tools for GraphQL. In the following sections you will also find code examples for accessing our metadata via the GraphQL API.

In case you program a crawler which shall run regularly you might find the modifiedAfter argument helpful. By setting it to the date of the last execution you can fetch the metadata of all learning resources which have been modified since then. Note that the returned metadata objects are always ordered by id even if modifiedAfter is set.

Further reading

Code examples with Python

For the following examples you need to have requests installed (for example via pip install requests).

Helper functions for displaying the results

In [1]:
import json

from IPython.display import display, Markdown, HTML

def display_json(value, title="The result"):   
    json_formated = json.dumps(value, indent=2)
    
    display_markdown(f"#### {title}")
    display_markdown(f"```json\n{json_formated}\n```")
    
def display_len(list_object, explanation="elements were fetched"):
    display_markdown("**Result:** %s %s" % (len(list_object), explanation))

def display_markdown(text):
    display(Markdown(text))

Fetching metadata about Serlo Education e.V. (i.e. the publisher)

In [2]:
import requests

def get_publisher():
    req = requests.post(
        "https://api.serlo-staging.dev/graphql",
        headers = {
            "Content-Type": "application/json",
        },
        json = {
            "query": """
                query {
                    metadata {
                        publisher
                    }
                }
            """
        }
    )
    
    return req.json()

display_json(get_publisher())

The result

{
  "data": {
    "metadata": {
      "publisher": {
        "@context": [
          "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
          {
            "@language": "de"
          }
        ],
        "id": "https://serlo.org/",
        "type": [
          "EducationalOrganization",
          "NGO"
        ],
        "name": "Serlo Education e.V.",
        "alternateName": "Serlo",
        "url": "https://de.serlo.org/",
        "description": "Serlo.org bietet einfache Erkl\u00e4rungen, Kurse, Lernvideos, \u00dcbungen und Musterl\u00f6sungen mit denen Sch\u00fcler*innen und Studierende nach ihrem eigenen Bedarf und in ihrem eigenen Tempo lernen k\u00f6nnen. Die Lernplattform ist komplett kostenlos und werbefrei.",
        "image": "https://assets.serlo.org/5ce4082185f5d_5df93b32a2e2cb8a0363e2e2ab3ce4f79d444d11.jpg",
        "logo": "https://de.serlo.org/_assets/img/serlo-logo.svg",
        "address": {
          "type": "PostalAddress",
          "streetAddress": "Daiserstra\u00dfe 15 (RGB)",
          "postalCode": "81371",
          "addressLocality": "M\u00fcnchen",
          "addressRegion": "Bayern",
          "addressCountry": "Germany"
        },
        "email": "de@serlo.org"
      }
    }
  }
}

Fetching the first page of metadata for entities

In [3]:
import requests

req = requests.post(
    "https://api.serlo-staging.dev/graphql",
    headers = {
        "Content-Type": "application/json",
    },
    json = {
        "query": """
            query {
                metadata {
                    entities(first: 2) {
                        nodes
                    }
                }
            }
        """
    }
)

display_json(req.json())

The result

{
  "data": {
    "metadata": {
      "entities": {
        "nodes": [
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "de"
              }
            ],
            "id": "https://serlo.org/1495",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-03-01T20:36:44+00:00",
            "dateModified": "2021-03-08T20:51:17+00:00",
            "description": "Addition, auch Plusrechnen genannt, geh\u00f6rt zu den Grundrechenarten der Mathematik. Lerne, was ein Summand ist. \u21d2 Hier lernst du, dass das Assoziativgesetz und Kommutativgesetz gelten.  \u21d2 Veranschaulichung durch Merktabellen und Zahlengeraden. F\u00fcr den Anfang kannst du auch schriftlich addieren! Viele \u00dcbungsaufgaben sind verf\u00fcgbar.\u2713 Lernen mit Serlo!",
            "headline": "Addition",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 1495
            },
            "inLanguage": [
              "de"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
            },
            "maintainer": "https://serlo.org/",
            "name": "Addition",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/197588"
          },
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "de"
              }
            ],
            "id": "https://serlo.org/1497",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-03-01T20:36:51+00:00",
            "dateModified": "2021-09-06T11:11:40+00:00",
            "description": "",
            "headline": "Kleinstes gemeinsames Vielfaches",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 1497
            },
            "inLanguage": [
              "de"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
            },
            "maintainer": "https://serlo.org/",
            "name": "Kleinstes gemeinsames Vielfaches",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/224107"
          }
        ]
      }
    }
  }
}

Fetching the first and the second page

In [4]:
import requests

def fetch_entities(first, after=None):
    req = requests.post(
        "https://api.serlo-staging.dev/graphql",
        headers = {
            "Content-Type": "application/json",
        },
        json = {
            "query": """
                query($first: Int, $after: String) {
                    metadata {
                        entities(first: $first, after: $after) {
                            nodes
                            pageInfo {
                                hasNextPage
                                endCursor
                            }
                        }
                    }
                }
            """,
            "variables": { "first": first, "after": after }
        }
    )
    
    return req.json()

# Fetching the first page with the first two metadata elements
first_result = fetch_entities(first=2)

# Display the `PageInfo` object of the first request
# Note that `hastNextPage` is true, so there are more objects which can be fetched
first_result_page_info = first_result["data"]["metadata"]["entities"]["pageInfo"]
display_json(first_result_page_info, title="The PageInfo object of the first request")

# Fetching the second page with the next two elements
# Note that `endCursor` of the first request is passed as the `after` argument in order to get the next elements 
second_result = fetch_entities(first=2, after=first_result_page_info["endCursor"])

display_json(second_result, title="Metadata of the second page")

The PageInfo object of the first request

{
  "hasNextPage": true,
  "endCursor": "MTQ5Nw=="
}

Metadata of the second page

{
  "data": {
    "metadata": {
      "entities": {
        "nodes": [
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "de"
              }
            ],
            "id": "https://serlo.org/1499",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-03-01T20:37:01+00:00",
            "dateModified": "2021-09-06T11:41:11+00:00",
            "description": "Binomische Formeln einfach erkl\u00e4rt. Verwendung der binomischen Formel zum Aufl\u00f6sen von Klammern  und Faktorisieren.  Mit vielen Beispielen und \u00dcbungen! Erfahre mehr zu leichten Beweisen der binomischen Formel mithilfe des Quadrats. \u21d2 Ein Kochrezept zur allgemeinen Vorhergehensweise. Video\u2713",
            "headline": "Binomische Formeln",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 1499
            },
            "inLanguage": [
              "de"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
            },
            "maintainer": "https://serlo.org/",
            "name": "Binomische Formeln",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/224109"
          },
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "de"
              }
            ],
            "id": "https://serlo.org/1501",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-03-01T20:37:01+00:00",
            "dateModified": "2021-09-08T10:24:28+00:00",
            "description": "",
            "headline": "Ergebnismenge",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 1501
            },
            "inLanguage": [
              "de"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
            },
            "maintainer": "https://serlo.org/",
            "name": "Ergebnismenge",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/224215"
          }
        ],
        "pageInfo": {
          "hasNextPage": true,
          "endCursor": "MTUwMQ=="
        }
      }
    }
  }
}

Fetching metadata of all learning resources

The following example shows how the PageInfo object can be used to fetch all entities in a loop:

In [5]:
import requests    

def fetch_all_entities(first=500):
    result = []
    endCursor = None
    
    while True:
        current_page = fetch_entities(first, after=endCursor)["data"]["metadata"]["entities"]
        
        result += current_page["nodes"]
        
        if current_page["pageInfo"]["hasNextPage"]:
            endCursor = current_page["pageInfo"]["endCursor"]
        else:
            break
    
    return result

def fetch_entities(first, after=None):
    req = requests.post(
        "https://api.serlo-staging.dev/graphql",
        headers = {
            "Content-Type": "application/json",
        },
        json = {
            "query": """
                query($first: Int, $after: String) {
                    metadata {
                        entities(first: $first, after: $after) {
                            nodes
                            pageInfo {
                                hasNextPage
                                endCursor
                            }
                        }
                    }
                }
            """,
            "variables": { "first": first, "after": after }
        }
    )
    
    return req.json()

all_entities = fetch_all_entities()

display_len(all_entities)

Result: 8371 elements were fetched

Fetching all metadata objects with filters instance and modifiedAfter

In [6]:
import requests    

def fetch_all_entities(first=500, instance=None, modifiedAfter=None):
    result = []
    endCursor = None
    
    while True:
        current_page = fetch_entities(first, after=endCursor, instance=instance, modifiedAfter=modifiedAfter)
        current_page = current_page["data"]["metadata"]["entities"]
        
        result += current_page["nodes"]
        
        if current_page["pageInfo"]["hasNextPage"]:
            endCursor = current_page["pageInfo"]["endCursor"]
        else:
            break
    
    return result

def fetch_entities(first, after=None, instance=None, modifiedAfter=None):
    req = requests.post(
        "https://api.serlo-staging.dev/graphql",
        headers = {
            "Content-Type": "application/json",
        },
        json = {
            "query": """
                query($first: Int, $after: String, $instance: Instance, $modifiedAfter: String) {
                    metadata {
                        entities(first: $first, after: $after, instance: $instance, modifiedAfter: $modifiedAfter) {
                            nodes
                            pageInfo {
                                hasNextPage
                                endCursor
                            }
                        }
                    }
                }
            """,
            "variables": {
                "first": first,
                "after": after,
                "instance": instance,
                "modifiedAfter": modifiedAfter
            }
        }
    )
    
    return req.json()

# == Fetch elements by language / subdomain ==
german_entities = fetch_all_entities(instance="de")

display_len(german_entities, explanation="entities fetched from de.serlo.org")

# == Fetch elements which are modified in 2021 ==
# Format for modifiedAfter is YYYY-MM-DDTHH:MM:SSZ
entities2021 = fetch_all_entities(modifiedAfter="2021-01-01T00:00:00Z")

display_len(entities2021, explanation="entities fetched which are modified in 2021")

Result: 7458 entities fetched from de.serlo.org

Result: 3179 entities fetched which are modified in 2021

Code examples with curl

The following examples use curl for making the requests and jq for pretty-printing the JSON responses.

Fetching the publisher

In [7]:
%%bash

curl https://api.serlo-staging.dev/graphql \
     --silent -X POST \
     -H "Content-Type: application/json" \
     --data '{
         "query": "query { metadata { publisher } }"
     }' | jq -C
{
  "data": {
    "metadata": {
      "publisher": {
        "@context": [
          "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
          {
            "@language": "de"
          }
        ],
        "id": "https://serlo.org/",
        "type": [
          "EducationalOrganization",
          "NGO"
        ],
        "name": "Serlo Education e.V.",
        "alternateName": "Serlo",
        "url": "https://de.serlo.org/",
        "description": "Serlo.org bietet einfache Erklärungen, Kurse, Lernvideos, Übungen und Musterlösungen mit denen Schüler*innen und Studierende nach ihrem eigenen Bedarf und in ihrem eigenen Tempo lernen können. Die Lernplattform ist komplett kostenlos und werbefrei.",
        "image": "https://assets.serlo.org/5ce4082185f5d_5df93b32a2e2cb8a0363e2e2ab3ce4f79d444d11.jpg",
        "logo": "https://de.serlo.org/_assets/img/serlo-logo.svg",
        "address": {
          "type": "PostalAddress",
          "streetAddress": "Daiserstraße 15 (RGB)",
          "postalCode": "81371",
          "addressLocality": "München",
          "addressRegion": "Bayern",
          "addressCountry": "Germany"
        },
        "email": "de@serlo.org"
      }
    }
  }
}

Fetching metadata of learning resources

In [8]:
%%bash

curl https://api.serlo-staging.dev/graphql \
     --silent -X POST \
     -H "Content-Type: application/json" \
     --data '{
         "query": "query { metadata { entities(first: 2) { nodes } } }"
     }' | jq -C
{
  "data": {
    "metadata": {
      "entities": {
        "nodes": [
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "de"
              }
            ],
            "id": "https://serlo.org/1495",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-03-01T20:36:44+00:00",
            "dateModified": "2021-03-08T20:51:17+00:00",
            "description": "Addition, auch Plusrechnen genannt, gehört zu den Grundrechenarten der Mathematik. Lerne, was ein Summand ist. ⇒ Hier lernst du, dass das Assoziativgesetz und Kommutativgesetz gelten.  ⇒ Veranschaulichung durch Merktabellen und Zahlengeraden. Für den Anfang kannst du auch schriftlich addieren! Viele Übungsaufgaben sind verfügbar.✓ Lernen mit Serlo!",
            "headline": "Addition",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 1495
            },
            "inLanguage": [
              "de"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
            },
            "maintainer": "https://serlo.org/",
            "name": "Addition",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/197588"
          },
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "de"
              }
            ],
            "id": "https://serlo.org/1497",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-03-01T20:36:51+00:00",
            "dateModified": "2021-09-06T11:11:40+00:00",
            "description": "",
            "headline": "Kleinstes gemeinsames Vielfaches",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 1497
            },
            "inLanguage": [
              "de"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
            },
            "maintainer": "https://serlo.org/",
            "name": "Kleinstes gemeinsames Vielfaches",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/224107"
          }
        ]
      }
    }
  }
}

Fetching entities with arguments

The following example provides a bash function which you can use to test requests against the entities property with any combination of arguments.

In [9]:
%%bash

function get_entities_with {
    QUERY='
        query(
            $first: Int,
            $after: String,
            $instance: Instance,
            $modifiedAfter: String
        ) {
            metadata {
                entities(
                    first: $first,
                    after: $after,
                    instance: $instance,
                    modifiedAfter: $modifiedAfter
                ) {
                    nodes
                    pageInfo {
                        hasNextPage
                        endCursor
                    }
                }
            }
        }
    '
    REQUEST_BODY="{
        \"query\": \"$(echo "$QUERY" | tr -d "\n")\",
        \"variables\": "$1"
    }"

    curl https://api.serlo-staging.dev/graphql \
         --silent -X POST \
         -H "Content-Type: application/json" \
         --data "$REQUEST_BODY" | jq -C
}

echo "== Get the first English entity == "

get_entities_with '{ "first": 1, "instance": "en" }'


echo
echo "== Get the following entity with the endCursor of the first request =="

get_entities_with '{ "first": 1, "instance": "en", "after": "MzI5OTY=" }'


echo
echo "== Get an entity which was modified after 2021-11-01 =="

get_entities_with '{ "first": 1, "modifiedAfter": "2021-11-01T00:00:00Z" }'
== Get the first English entity == 
{
  "data": {
    "metadata": {
      "entities": {
        "nodes": [
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "en"
              }
            ],
            "id": "https://serlo.org/32996",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-11-16T15:02:57+00:00",
            "dateModified": "2021-10-21T19:48:02+00:00",
            "description": "",
            "headline": "Gaussian elimination",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 32996
            },
            "inLanguage": [
              "en"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "http://creativecommons.org/licenses/by/4.0/"
            },
            "maintainer": "https://serlo.org/",
            "name": "Gaussian elimination",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/227897"
          }
        ],
        "pageInfo": {
          "hasNextPage": true,
          "endCursor": "MzI5OTY="
        }
      }
    }
  }
}

== Get the following entity with the endCursor of the first request ==
{
  "data": {
    "metadata": {
      "entities": {
        "nodes": [
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "en"
              }
            ],
            "id": "https://serlo.org/33401",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-11-30T11:27:40+00:00",
            "dateModified": "2021-08-19T20:29:05+00:00",
            "description": "",
            "headline": "System of linear equations",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 33401
            },
            "inLanguage": [
              "en"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "http://creativecommons.org/licenses/by/4.0/"
            },
            "maintainer": "https://serlo.org/",
            "name": "System of linear equations",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/222974"
          }
        ],
        "pageInfo": {
          "hasNextPage": true,
          "endCursor": "MzM0MDE="
        }
      }
    }
  }
}

== Get an entity which was modified after 2021-11-01 ==
{
  "data": {
    "metadata": {
      "entities": {
        "nodes": [
          {
            "@context": [
              "https://w3id.org/kim/lrmi-profile/draft/context.jsonld",
              {
                "@language": "de"
              }
            ],
            "id": "https://serlo.org/1525",
            "type": [
              "LearningResource",
              "Article"
            ],
            "dateCreated": "2014-03-01T20:37:31+00:00",
            "dateModified": "2022-01-07T23:05:21+00:00",
            "description": "Hier lernst du Schritt für Schritt in verschiedenen Situation (Klammer mal Klammer, Faktor mal Klammer und Produkt in der Klammer) das richtige Ausmultiplizieren mit der Klammer.",
            "headline": "Klammern ausmultiplizieren",
            "identifier": {
              "type": "PropertyValue",
              "propertyID": "UUID",
              "value": 1525
            },
            "inLanguage": [
              "de"
            ],
            "isAccessibleForFree": true,
            "isFamilyFriendly": true,
            "learningResourceType": "Article",
            "license": {
              "id": "https://creativecommons.org/licenses/by-sa/4.0/deed.de"
            },
            "maintainer": "https://serlo.org/",
            "name": "Klammern ausmultiplizieren",
            "publisher": [
              {
                "id": "https://serlo.org/"
              }
            ],
            "version": "https://serlo.org/234900"
          }
        ],
        "pageInfo": {
          "hasNextPage": true,
          "endCursor": "MTUyNQ=="
        }
      }
    }
  }
}

Roadmap / Planned Features

This metadata API is a prototype. Before deploying the metadata API in production we plan to integrate at least the following features:

  • support of all useful properties of schema.org / AMB for learning resources
  • support of all types of learning resources we have at serlo.org (course pages and subexercises are excluded in the prototype)
  • possibility to fetch metadata about our taxonomy elements (like "Ableitung von Funktionen")
  • filter out community related sites like "Editor Anleitung" which do not represent learning resources

We look forward to hearing from you if you get in touch with us in case you need a particular feature. You can either open an issue at https://github.com/serlo/api.serlo.org/issues or reach us via de@serlo.org.

Behind the scenes

In case you are interested in how we generate the metadata you can find the most relevant source code in this pull request. It includes the code to generate the metadata from the data in our database. The metadata is passed from our database layer (see repository serlo.org-database-layer) to our GraphQL server (see repository api.serlo.org) from which it can be fetched. You can find a brief description of our infrastructure on this page.

All of our source code is licensed under the open source license Apache License 2.0 and can be used freely. The full license notice is available here.

License and disclaimer

This documentation with all code examples are licensed under the CC0 license.

This metadata API is a prototype. It might change in the future depending on the feedback we will receive. You can find the current documentation at https://lenabi.serlo.org/metadata-api If you want to use our metadata API we are happy if get in contact with us. You can reach us at de@serlo.org.