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.
|
2019-05-06 15:24:12 +03:00
|
|
|
Next to the API folders there is a shared folder, which contains shared code, which all API versions use.
|
2018-09-27 21:33:21 +03:00
|
|
|
|
2019-05-06 15:24:12 +03:00
|
|
|
**NOTE: v0.1 is deprecated and we won't touch the shared folder at all. The v0.1 folder
|
2018-09-27 21:33:21 +03:00
|
|
|
contains the API layer which we have used since Ghost was born.**
|
|
|
|
|
|
|
|
## Stages
|
|
|
|
|
|
|
|
Each request goes through the following stages:
|
|
|
|
|
2019-05-06 15:24:12 +03:00
|
|
|
- input validation
|
2018-09-27 21:33:21 +03:00
|
|
|
- input serialisation
|
|
|
|
- permissions
|
|
|
|
- query
|
|
|
|
- output serialisation
|
|
|
|
|
2019-05-06 15:24:12 +03:00
|
|
|
The framework we are building pipes a request through these stages in respect of the API controller configuration.
|
2018-09-27 21:33:21 +03:00
|
|
|
|
|
|
|
|
2018-10-05 01:50:45 +03:00
|
|
|
## Frame
|
|
|
|
|
2019-05-06 15:24:12 +03:00
|
|
|
Is a class, which holds all the information for request processing. We pass this instance by reference.
|
|
|
|
Each function can modify the original instance. No need to return the class instance.
|
2018-10-05 01:50:45 +03:00
|
|
|
|
|
|
|
### 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
|
|
|
|
},
|
2019-05-06 15:24:12 +03:00
|
|
|
// Allowed url/query params
|
2018-10-05 01:50:45 +03:00
|
|
|
options: ['include']
|
2019-05-06 15:24:12 +03:00
|
|
|
// Url/query param validation configuration
|
2018-10-05 01:50:45 +03:00
|
|
|
validation: {
|
|
|
|
options: {
|
|
|
|
include: {
|
|
|
|
required: true,
|
|
|
|
values: ['tags']
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
permissions: true,
|
2019-05-06 15:24:12 +03:00
|
|
|
// Returns a model response!
|
2018-10-05 01:50:45 +03:00
|
|
|
query(frame) {
|
|
|
|
return models.Post.edit(frame.data, frame.options);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
```
|
|
|
|
read: {
|
2019-05-06 15:24:12 +03:00
|
|
|
// Allowed url/query params, which will be remembered inside `frame.data`
|
|
|
|
// This is helpful for READ requests e.g. `model.findOne(frame.data, frame.options)`.
|
|
|
|
// Our model layer requires sending the where clauses as first parameter.
|
2018-10-05 01:50:45 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|