Der API Compact Guide ist die ausführliche Gesamtanleitung zur API – mit allen Konzepten und konkreten Request-/Response-Beispielen. Der komplette Inhalt der Präsentation steht hier auf dieser Seite; zusätzlich kannst du ihn als PDF, PowerPoint oder online ansehen.
Sprache & Server: Die Originalpräsentation ist in englischer Sprache verfasst und nennt projectfacts-Beispiel-Server (
sync.projectfacts.de). Für teamspace gilt dieselbe API unter deiner eigenen Server-Adresse (z. B.https://app1.teamspace.de). Die Inhalte unten sind ins Deutsche übertragen; die Code- und JSON-Beispiele sind unverändert übernommen.
Herunterladen
- API Compact Guide ansehen (PDF) — PDF, 31 Seiten (~290 KB)
- API Compact Guide herunterladen (PowerPoint) — PPTX (~240 KB)
- Online ansehen (Google Präsentation)
projectfacts API Compact Guide — Version 0.36, benötigt projectfacts 6.00 oder neuer (Stand: 2021-03-09).
Überblick
Der Guide führt durch die folgenden Themen:
- Buzzwords – welche Standards und Best Practices verwendet werden.
- Autorisierung – was über die API gemacht werden kann.
- Authentifizierung – das Sicherheitskonzept der API mit seinen drei Wegen:
- Login-Authentifizierung
- Token-Authentifizierung
- Interface-Authentifizierung
- Der projectfacts API-Explorer – das Werkzeug zum Erkunden der API.
- Durchgängiges Beispiel – die Adresse einer Organisation aktualisieren.
- Was zu beachten ist – Hinweise für die Praxis.
Buzzwords: Was du vorher kennen solltest
Bevor es losgeht, solltest du mit diesen Themen vertraut sein:
- REST
- JSON
- HATEOAS
- Basic Auth
Autorisierung: Was über die API möglich ist
Grundsätzlich hat ein API-Nutzer dieselben Rechte wie in der Weboberfläche (projectfacts/teamspace):
- Wer in der Weboberfläche Projekte löschen darf, kann das auch über die API tun. Das lässt sich nicht verhindern.
- Ebenso kannst du diesen Nutzer nicht daran hindern, eine eigene App zum Löschen von Projekten zu entwickeln: Du kannst einem Nutzer die Nutzung der API nicht grundsätzlich verbieten.
- Wer in der Weboberfläche keine Zeiten buchen darf, kann das auch über die API nicht.
Ausnahme: Die Nutzung der API über projectfacts-Interfaces (Interface-Authentifizierung) folgt eigenen Regeln – dazu später mehr.
Authentifizierung: Das Sicherheitskonzept der API
Du kannst die API nicht einfach mit deinem projectfacts-Passwort verwenden. Würde man das Passwort bei jeder Anfrage mitschicken, entstünden mehrere Probleme:
- Passwort bei jeder Anfrage übertragen? ➜ Risiko, dass es abgefangen wird. 😬
- Ein Client wird kompromittiert? ➜ Passwort ändern ➜ alle Clients würden getrennt. 😞
- Das Passwort müsste auf dem Client gespeichert werden. 😲
Deshalb gibt es drei eigene Authentifizierungswege.
Login-Authentifizierung
Die einzige Ressource, die du mit deinen projectfacts-Zugangsdaten (Login-Authentifizierung) ansprechen kannst, ist die device-Ressource (Gerät). Sie muss beim ersten Anmelden aus deiner App heraus angelegt werden:
POST https://sync.projectfacts.de/api/device/
{
"email": "user@provider.com",
"password": "projectfactsPassword",
"deviceName": "YourAppName",
"deviceType": "de.fivepoint.other"
}
Gibt es einen Nutzer mit passender E-Mail und passendem Passwort, antwortet der Server mit der neuen device-Ressource. Sie enthält die Device-ID und den API-Token, mit denen du alle folgenden Anfragen authentifizierst:
{
"_id": "10001234",
"token": "D1C2B3A4",
"deviceName": "NameOfYourApp",
[...]
}
Wichtig: Diese Daten muss deine App speichern – niemals das ursprüngliche projectfacts-Passwort! Den Token bekommst du nur dieses eine Mal.
Token-Authentifizierung
Alle regulären Anfragen werden per Basic Auth mit Device-ID und Device-Token abgesichert – sie repräsentieren sowohl den Nutzer deiner App als auch das verwendete Gerät.
So baust du den Authorization-Header in JavaScript auf:
// API-Request in Javascript
var credsB64 = window.btoa(device._id + ':' + device.token)
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Authorization', 'Basic ' + credsB64);
xhr.onload = function(response) { /*...*/ };
xhr.send();
Dabei liegt im lokalen Speicher deiner App:
{
"_id": "10001234",
"token": "D1C2B3A4",
[...]
}
Die Berechtigung eines Geräts kann entzogen werden, indem das Gerät innerhalb von projectfacts gelöscht wird („Meine Geräte”). Andere Geräte/Apps sind davon nicht betroffen.
Interface-Authentifizierung
Für nicht-personenbezogene Apps, die nicht „im Namen” eines Nutzers handeln (z. B. ein Check-In/Out-Terminal):
- Basic Auth mit Interface-ID und -Passwort.
- Das Passwort wird in der projectfacts-Konfiguration (Interfaces) gesetzt.
- Keine Nutzer-Berechtigungen – die Rechte ergeben sich aus den Einstellungen des Interfaces.
- Eine Einschränkung der verbindenden IP-Adresse ist möglich.
Der projectfacts API-Explorer
Den API-Explorer erreichst du unter:
https://sync.projectfacts.de/htdocs/apps/apiexplorer/index.html
…oder unter der Wurzel deines pf-Servers + /htdocs/....
Es handelt sich um eine HTML5-/AngularJS-App, mit der du:
- unsere API durchsuchst,
- siehst, wie Ressourcen und Relationen dargestellt werden,
- lernst, wie man Queries nutzt,
- die Kommunikation über die Entwicklertools des Browsers verfolgst.
Die JSON-Repräsentationen als TypeScript-Klassen findest du unter:
https://sync.projectfacts.de/api/api/dto.ts
Schau dir das unbedingt einmal an!
Beispiel: Die Adresse einer Organisation aktualisieren
Wir suchen in unserer App nach „Example corp” und aktualisieren deren Adresse. Dazu sind folgende Schritte nötig:
GETauf/api/contactinklusive Such-Query.GETauf die gewünschte contact-Ressource.GETauf die Hauptadresse des Kontakts.PUTauf diese Adresse.
Schritt 1 — Kontakt suchen
Wir suchen nach „Example corp.” (der Nutzer hat „examp” in unsere App getippt). Dazu führen wir ein GET mit Device-ID und Token als Basic-Auth-Header aus:
GET https://sync.projectfacts.de/api/contact?caption*=examp
Die Bestandteile der URL:
| Bestandteil | Bedeutung |
|---|---|
contact | Zu durchsuchende Ressource (contact resource). |
caption | Feld, auf das gefiltert wird (Bezeichnung des Elements). |
*= | Operator „enthält” – im Gegensatz zu = „ist gleich”. |
examp | Suchwert (der Operator = ist nicht case-sensitiv). |
Ergebnis (Auszug):
{
"size": 3, "offset": 0, "limit": 100,
"items": [
{"caption": "Max TexaMP", "href": "https://…/api/contact/1234", "value": 1234},
{"caption": "Ben Examplename", "href": "https://…/api/contact/2345", "value": 2345},
{"caption": "Example corp.", "href": "https://…/api/contact/3456", "value": 3456}
]
}
HATEOAS: Über das mitgelieferte
hrefgeht es hier weiter – du baust keine IDs selbst zusammen, sondern folgst dem Link.
Schritt 2 — Kontakt laden
Wir laden „Example corp.” (der Nutzer hat den dritten Treffer ausgewählt):
GET https://sync.projectfacts.de/api/contact/3456
Ergebnis (Auszug):
{
"_id": 3456, "_idKey": "39", "_lastModifiedDate": "2015-11-19T10:44:26.000+0000",
"active": true,
"person": false,
"caption": "Example corp.",
"firstname": null,
"lastname": "Example corp.",
"description": "Address outdated\nMoved?",
"mainAddress": {"caption": ";;Examplestreet 7;Examplecity;;122456;Deutschland",
"href": "…/api/contactfield/173880993", "rel": "contactfield",
"title": "preferred postal address",
"value": 173880993, "idKey": "174"}
}
Dazu drei Beobachtungen:
- Meta-Informationen beginnen mit einem Unterstrich (
_id,_idKey,_lastModifiedDate). - Kontakte können Personen wie auch Organisationen sein (
"person": false⇒ Organisation). - Komplexe Daten erscheinen als Link-Objekt (wieder HATEOAS): Die
mainAddressist ein Verweis. Ihrcaptiondient als „Vorschau” der Ressource.
Schritt 3 — Adressfeld laden
Wir laden die contactfield-Ressource, die als mainAddress referenziert ist:
GET https://sync.projectfacts.de/api/contactfield/173880993
Ergebnis (Auszug):
{
"_id": 173880993, "_idKey": "174",
"value": ";;Examplestreet 7;Examplecity;;122456;Deutschland",
"customlabel": null,
"type": {"caption": "Address", "value": "ADR",
"href": "https://ws-cp/api/enum/contactfieldtype/ADR",
"optionsUrl": "…/api/enum/contactfieldtype"},
"subtype": {"caption": "Work", "value": "WORK",
"href": "…/api/enum/contactfieldsubtype/WORK",
"optionsUrl": "https://ws-cp/api/enum/contactfieldsubtype"},
"contact": {"href": "…/api/contact/3456", "title": "Contact",
"value": 3456, "idKey": "39"}
}
Dazu:
- Die Adresse liegt im vCard-Format vor, inklusive der Möglichkeit für ein eigenes Label (
customlabel). - Der Typ des Kontaktfelds ist
ADR– wie in vCard. typeundsubtypesind wieder Link-Objekte, die auf die Details verweisen. Sie verlinken zugleich eine Sammlung möglicher Werte (optionsUrl).
Schritt 4 — Adresse speichern
Nachdem wir den Wert geändert haben, speichern wir die geänderte contactfield-Ressource per PUT:
PUT https://sync.projectfacts.de/api/contactfield/173880993
Request-Daten (Auszug):
{
"_id": 173880993, "_idKey": "174",
"value": ";;Saalbaustraße 27;Darmstadt;;64283;Deutschland",
"customlabel": null,
"type": {"caption": "Address", "value": "ADR",
"href": "…/api/enum/contactfieldtype/ADR",
"optionsUrl": "…/api/enum/contactfieldtype"},
"subtype": {"caption": "Arbeit", "value": "WORK",
"href": "…/api/enum/contactfieldsubtype/WORK",
"optionsUrl": "…/api/enum/contactfieldsubtype"},
"contact": {"href": "…/api/contact/28883816", "title": "Contact",
"value": 28883816, "idKey": "39"}
}
Einfache Datentypen wie Strings (hier
value) lassen sich problemlos ändern.
Bonus — Link-Objekte aktualisieren
Wie aktualisiert man die Link-Objekte (z. B. subtype)? Nur der value zählt. Du musst also nicht das komplette Link-Objekt zurückschicken – es genügt, den value zu setzen:
{
"_id": 173880993, "_idKey": "174",
"value": ";;Saalbaustraße 27;Darmstadt;;64283;Deutschland",
"customlabel": null,
"type": {"caption": "Adresse", "value": "ADR",
"href": "…/api/enum/contactfieldtype/ADR",
"optionsUrl": "…/api/enum/contactfieldtype"},
"subtype": "HOME",
"contact": {"href": "…/api/contact/28883816", "title": "Contact",
"value": 28883816, "idKey": "39"}
}
…so funktioniert es auch! Statt des vollständigen Link-Objekts für
subtypereicht der reine Wert"HOME".
Was zu beachten ist
- Unsere API unterstützt dich beim Caching – nutze es!
- Das Erste, was eine App beim Start tun sollte, ist, ihre device-Ressource zu laden – so weißt du von Anfang an, ob der Token noch gültig ist.
- Denke daran, den Token sicher in deiner App zu speichern.
- Unsere API ist noch nicht vollständig, wird aber von Zeit zu Zeit um weitere Ressourcen erweitert.
- Unser projectfacts-Chat ist ein weiteres Beispiel, umgesetzt in TypeScript.
- Wenn du eine öffentliche App entwickeln möchtest, kontaktiere uns – wir unterstützen dich!
Cheat-Sheet: Query-Filter (Sammlungen)
Filter werden als Query-Parameter an eine Sammlungs-URL gehängt:
| Typ | Syntax | Beispiel | Bedeutung |
|---|---|---|---|
| Match (gleich) | …?field=value | name="bob ross"&car=volvo&age=3 | Anführungszeichen nur bei Bedarf (nur Strings). |
| Contains (enthält) | …?field*=val | name*="bo"&car=vol | Namen, die „bo” enthalten. |
| Range (Bereich) | …?field=1..3 | name="alice".."bob"&age=20..30 | Werte 20, 30 und dazwischen. |
| Or (oder) | …?field=A,B,C | name=alice,"bob ross",charlie | Alle Alices, Charlies und Bob Rosses. |
| Not empty (nicht leer) | …?field | name=bob&car | Bobs, die ein Auto besitzen. |
| Empty (leer) | …?!field | name=bob&!car | Bobs, die kein Auto besitzen. |
Cheat-Sheet: Matrix-Parameter (Sammlungen)
Matrix-Parameter steuern Sortierung, Umfang und Auflösung einer Sammlung:
| Funktion | Syntax | Bedeutung |
|---|---|---|
| Aufsteigend sortieren | ...;sort=name?... | Elemente nach name aufsteigend sortieren. |
| Absteigend sortieren | ...;sort=!name?... | Elemente nach name absteigend sortieren. |
| Ergebnisgröße | ...;limit=100?... | Die ersten 100 Elemente nach dem Offset laden. |
| Ergebnis-Offset | ...;offset=200?... | Die ersten 200 Elemente überspringen. |
| Teilbaum | ...;parent=1234?... | Kinder von Element 1234 laden (nur Baumstrukturen). |
| Tiefe (evtl. LANGSAM!) | …;depth=1?... | Erste Generation von Referenzen auflösen (LANGSAM!). |
| Hinweise anzeigen | …;showhints=true?... | Verfügbare Sortierungen anzeigen (sort by xyz). |
Cheat-Sheet: Matrix-Parameter (Elemente)
| Funktion | Syntax | Bedeutung |
|---|---|---|
| 7-Tage-Caching-Header | …;lck=value?... | Die Antwort wird mit einem 7-Tage-Caching-Header zurückgegeben. |
„value" sollte eine Art Version der Ressource abbilden. Solange sie sich nicht ändert, verwendet dein HTTP-Client seinen Cache, statt die Ressource erneut vom Server zu laden.
Tipp: Verwende das
"_lastModifiedDate"aus der Sammlungs-Ressource, um sicherzustellen, dass du ein frisches Element erhältst, wenn es auf dem Server geändert wurde.
Hinweise
- Die lokalen Dateien (PDF/PowerPoint) sind eine Kopie des Stands zum Redaktionszeitpunkt. Die Online-Version kann aktueller sein.
- Der Guide ist im Original in englischer Sprache verfasst und nennt projectfacts-Beispiel-Server (
sync.projectfacts.de); für teamspace gilt dieselbe API unter deiner eigenen Server-Adresse.
Verwandte Themen
- API – Einführung API Einführung
- Aufbau der API-Adressen (URL-Struktur) API Konzept
- Feldreferenz: Spesen- und Belegfelder API Referenz