Ghost/core/server/api
kirrg001 38e93c19b5 Reverted & Solved apiType differently
no issue

- was unable to revert 9dd7aff9c6, because it contains members changes
- functional calls did not work correctly, because the content and admin ctrl differentiation happend in the web layer
- `isContentAPI` returned true for `api.v2.settings.edit(data, {context: {internal:true{})`
- content & admin API are using different controllers
- we can just tell which ctrl is content API and which is not
- the direction fits for the content & admin API split
2019-02-26 08:33:10 +01:00
..
shared Reverted & Solved apiType differently 2019-02-26 08:33:10 +01:00
v0.1 Added back "theme.uploaded" analytics event (#10450) 2019-02-05 17:38:40 +01:00
v2 Reverted & Solved apiType differently 2019-02-26 08:33:10 +01:00
index.js Added more webhooks & changed payload 2019-02-07 23:14:27 +01:00
README.md Added tiny framework to support multiple API versions (#9933) 2018-10-05 00:50:45 +02: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.

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: []
  }
}

API Controller

A controller is no longer just a function, it's a set of configurations.

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);
  }
}
edit: {
  validation() {
    // custom validation, skip framework
  },
  permissions: {
    unsafeAttrs: ['author']
  },
  query(frame) {
    return models.Post.edit(frame.data, frame.options);
  }
}