2018-09-27 21:33:21 +03:00
|
|
|
# API Versioning
|
|
|
|
|
|
|
|
Ghost supports multiple API versions.
|
|
|
|
Each version lives in a separate folder e.g. api/v0.1, api/v2.
|
|
|
|
Next to the API folders there is a shared folder, which the API versions use.
|
|
|
|
|
|
|
|
**NOTE: v0.1 is deprecated and we won't touch this folder at all. The v0.1 folder
|
|
|
|
contains the API layer which we have used since Ghost was born.**
|
|
|
|
|
|
|
|
## Stages
|
|
|
|
|
|
|
|
Each request goes through the following stages:
|
|
|
|
|
|
|
|
- validation
|
|
|
|
- input serialisation
|
|
|
|
- permissions
|
|
|
|
- query
|
|
|
|
- output serialisation
|
|
|
|
|
|
|
|
The framework we are building pipes a request through these stages depending on the API controller implementation.
|
|
|
|
|
|
|
|
|
2018-10-05 01:50:45 +03:00
|
|
|
## Frame
|
|
|
|
|
|
|
|
Is a class, which holds all the information for API processing. We pass this instance per reference.
|
|
|
|
The target function can modify the original instance. No need to return the class instance.
|
|
|
|
|
|
|
|
### Structure
|
|
|
|
|
|
|
|
```
|
|
|
|
{
|
|
|
|
original: Object,
|
|
|
|
options: Object,
|
|
|
|
data: Object,
|
|
|
|
user: Object,
|
|
|
|
file: Object,
|
|
|
|
files: Array
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Example
|
|
|
|
|
|
|
|
```
|
|
|
|
{
|
|
|
|
original: {
|
|
|
|
include: 'tags'
|
|
|
|
},
|
|
|
|
options: {
|
|
|
|
withRelated: ['tags']
|
|
|
|
},
|
|
|
|
data: {
|
|
|
|
posts: []
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2018-09-27 21:33:21 +03:00
|
|
|
## API Controller
|
|
|
|
|
|
|
|
A controller is no longer just a function, it's a set of configurations.
|
|
|
|
|
2018-10-05 01:50:45 +03:00
|
|
|
### Structure
|
|
|
|
|
|
|
|
```
|
|
|
|
edit: function || object
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
|
|
|
edit: {
|
|
|
|
headers: object,
|
|
|
|
options: Array,
|
|
|
|
data: Array,
|
|
|
|
validation: object | function,
|
|
|
|
permissions: boolean | object | function,
|
|
|
|
query: function
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Examples
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
edit: {
|
|
|
|
headers: {
|
|
|
|
cacheInvalidate: true
|
|
|
|
},
|
|
|
|
options: ['include']
|
|
|
|
validation: {
|
|
|
|
options: {
|
|
|
|
include: {
|
|
|
|
required: true,
|
|
|
|
values: ['tags']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: true,
|
|
|
|
query(frame) {
|
|
|
|
return models.Post.edit(frame.data, frame.options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
|
|
|
read: {
|
|
|
|
data: ['slug']
|
|
|
|
validation: {
|
|
|
|
data: {
|
|
|
|
slug: {
|
|
|
|
values: ['eins']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: true,
|
|
|
|
query(frame) {
|
|
|
|
return models.Post.findOne(frame.data, frame.options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
2018-09-27 21:33:21 +03:00
|
|
|
|
2018-10-05 01:50:45 +03:00
|
|
|
```
|
|
|
|
edit: {
|
|
|
|
validation() {
|
|
|
|
// custom validation, skip framework
|
|
|
|
},
|
|
|
|
permissions: {
|
|
|
|
unsafeAttrs: ['author']
|
|
|
|
},
|
|
|
|
query(frame) {
|
|
|
|
return models.Post.edit(frame.data, frame.options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|