Introduction to api.serlo.org - serlo/documentation GitHub Wiki
General description of Apollo
Find the documentation of this service here: Official Apollo Server documentation
We decided to use Apollo Server for the following reasons:
- Apollo Server is the most used GraphQL server for TypeScript. Using TypeScript is a natural choice since we use TypeScript already for a lot of other projects. Thus no extension of the tech stack was necessary.
- Apollo Server is easy to use (especially for newcomers).
Requirements
Before you start reading this document you should already have a basic understanding about the Apollo Server and GraphQL. The following sites are a perfect introduction:
- Understanding GraphQL:
- Understanding Apollo Server:
Note: Apollo Server documents another way to implement data sources as we do. We have decided to use our own implementation in order to have a better control over the cache handling.
The API in Serlo's software architecture in a nut shell
The job of api.serlo.org is to provide a unified API to Serlo which clients like our frontend (frontend.serlo.org) or bots can use to access or modify our (learning) content. Thereby clients access the API via GraphQL.
api.serlo.org uses different other APIs of services. Such services are for example the database layer (which gives an API to our database) or the Google Spreadsheet API (we store the IDs of donors in a special spreadsheet). So api.serlo.org unifies the APIs of all used services into a single one. Therefore clients of Serlo only need to deal with api.serlo.org:
An example of a GraphQL endpoint
The way clients deal with the API is via the endpoints the API exposes. Clients can send requests to these endpoints. The starting point of each GraphQL request is either a query (reading / getting something) or a mutation (writing / changing something). An example for a query endpoint is uuid
:
type Query {
uuid(alias: { instance: Instance, path: String }): AbstractUuid
}
enum Instance {
en,
de,
...
}
Given a resource by specifying the path of the URL and the instance (= subdomain) of the URL it returns an AbstractUuid
object. AbstractUuid
might be an article, a course, a user or some other objects which can be rendered individually at our site. Here AbstractUuid
is an interface type which other types like Article
, Course
, etc implement:
interface AbstractUuid {
id: Int!
...
}
type Article implements AbstractUuid {
id: Int!
currentRevision: ArticleRevision
...
}
type ArticleRevision implements AbstractUuid {
id: Int!
title: String!
content: String!
...
}
Let's say we want to request the current title and content of the page https://en.serlo.org/community/41297/multiplying-decimal-fractions. From the URL we can deduce the instance (= subdomain) and the path:
path
----------------------------------------------
https://en.serlo.org/community/41297/multiplying-decimal-fractions
--
instance
Thus we can make the request
query {
uuid(
alias: {
instance: en
path: "/community/41297/multiplying-decimal-fractions"
}
) {
... on Article {
currentRevision {
title
content
}
}
}
}
This results in a response with the current title and content:
{
"data": {
"uuid": {
"currentRevision": {
"title": "Multiplying decimal fractions",
"content": "[[{\"col\":24,\"content\":\"## Procedure\"}],[{\"col\":16,\"content\":\"1. Ignore ..."
}
}
}
}
You can test the request by yourself at https://api.serlo.org/___graphql
The next article in this series is this one about the architecture of the type system.