Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Posts API Response #2347

Closed
sebgie opened this issue Mar 6, 2014 · 16 comments
Closed

Posts API Response #2347

sebgie opened this issue Mar 6, 2014 · 16 comments
Labels
affects:api Affects the Ghost API
Milestone

Comments

@sebgie
Copy link
Contributor

sebgie commented Mar 6, 2014

The structure of our JSON/REST APIs are up for spring cleaning #2124. The most complex JSON object is a post. I have compiled what I think could be changed to improve consistency and readability?

Single Post

GET /ghost/api/v0.1/posts/1

JSON Object (v0.1)

{
    id: 1,
    uuid: "f1e6bf27-fdd5-47a1-85a7-90eb2d05a0e5",
    status: "published",
    title: "Public post",
    slug: "public-post",
    markdown: "my public post",
    html: "<p>my public post</p>",
    image: null,
    featured: 0,
    page: 0,
    language: "en_US",
    meta_title: null,
    meta_description: null,
    author_id: 1,
    created_at: "2014-03-05T10:25:29.594Z",
    created_by: 1,
    updated_at: "2014-03-05T10:25:32.299Z",
    updated_by: 1,
    published_at: "2014-03-05T10:25:32.299Z",
    published_by: 1,
    author: {...},
    user: {...},
    tags: [ {...}, {...} ]
}

New JSON Object:

  • author_id is renamed to author
  • wrapped in an object that contains a settings key and holds an array (jsonapi.org/format - singular ressource)
  • added links property (jsonapi.org/format - To-One Relationships)
  • added linked property (jsonapi.org/format - Compound documents)
  • Subject to change: tags, created_by, updated_by and published_by could become expandable properties (see Expanding JSON API Objects #2346).
{
    links: {
        posts.created_by: {
            href: "http://tbd.com",
            type: "users"
        },
        posts.updated_by: {
            href: "http://tbd.com",
            type: "users"
        },
        posts.author: {
            href: "http://tbd.com",
            type: "users"
        },
        posts.published_by: {
            href: "http://tbd.com",
            type: "users"
        },
        posts.tags: {
            href: "http://tbd.com",
            type: "tags"
        },
        tags.created_by: {
            href: "http://tbd.com",
            type: "users"
        },
        tags.updated_by: {
            href: "http://tbd.com",
            type: "users"
        },
        users.roles: {
            href: "http://tbd.com",
            type: "roles"
        },
        roles.permissions: {
            href: "http://tbd.com",
            type: "permissions"
        }
    },
    posts: [
        {
            id: 1,
            uuid: "f1e6bf27-fdd5-47a1-85a7-90eb2d05a0e5",
            status: "published",
            title: "Public post",
            slug: "public-post",
            markdown: "my public post",
            html: "<p>my public post</p>",
            image: null,
            featured: false,
            page: false,
            language: "en_US",
            meta_title: null,
            meta_description: null,
            created_at: "2014-03-05T10:25:29.594Z",
            updated_at: "2014-03-05T10:25:32.299Z",
            published_at: "2014-03-05T10:25:32.299Z",
            links: {
                author: 1,
                published_by: 1,
                updated_by: 1,
                created_by: 1,
                tags: [ 1, 2 ]
            }
        }
    ],
    linked: {
        users: [
            {
                id: 1,
                uuid: '27771667-2134-4899-891a-551c22e3b815',
                name: 'Joe Bloggs',
                slug: 'joe-blogs',
                ...,
                links: {
                    roles: 1
                }
            }
        ],
        roles: [
            {
                id: 1,
                description: 'Administrators,
                name: 'Administrator',
                ...,
                links: {
                    permissions: [1]
                }
            }
        ],
        permissions: [
            {
                id: 1,
                name: 'Edit posts',
                ...,
            },
            {...}
        ],
        tags: [
            {
                id: 1,
                uuid: "9c8d3aa0-205d-440c-a49c-412f3ca4bc62",
                name: "tag1",
                slug: "tag1-post",
                ...,
                links: {
                    created_by: 1,
                    updated_by: 1
                }
            },
            {
                id: 2,
                uuid: "9c8d3aa0-205d-440c-a49c-412f3ca4bc62",
                name: "tag2",
                slug: "tag2-post",
                ...,
                links: {
                    created_by: 1,
                    updated_by: 1
                }
            }
        ]
    }
}

Post Collection

GET /ghost/api/v0.1/posts/

JSON Object (v0.1)

{
    posts: [{...}, {...}],
    page: 1,
    limit: 15,
    pages: 2,
    total: 33
}

New JSON Object:

  • move additional information in a meta property (jsonapi.org/format - Top Level)
  • added linked property (jsonapi.org/format - Compound documents)
  • added links property (jsonapi.org/format - Compound documents)
  • change pagination information to more generic values (calculation for pagination can be done in our helper):
    • limit: number of posts per page
    • offset: offset in number of posts instead of page
    • total: number of all posts
{
    links: {...},
    posts: [
        {...},
        {...},
    ],
    linked: {
        role: [
            {...}
        ],
        permissions: [
            {...},
            {...}
        ]
    },
    meta: {
        limit: 15,
        offset: 0,
        total: 33
    }
}
@ErisDS ErisDS modified the milestone: 0.5 Mar 7, 2014
halfdan referenced this issue in ErisDS/Ghost Mar 7, 2014
issue TryGhost#2270

- from https://github.com/manuelmitasch/ghost-admin-ember-demo
- Not working properly: added ic-ajax mock in app.js but promise not resolving => loading route always active
@sebgie sebgie mentioned this issue Mar 7, 2014
61 tasks
@ErisDS ErisDS added the api label Mar 7, 2014
@ErisDS
Copy link
Member

ErisDS commented Mar 7, 2014

Discussion:

  • do we need a property for the object type? (type: 'post')
  • do we need a new concept for storing the 'published' state for a post as mentioned in JSON API Permissions #2264?

Don't think we need a type property. Not sure what you mean about published state - the response has status: published did you mean something like published: true/false ?

@sebgie
Copy link
Contributor Author

sebgie commented Mar 10, 2014

@ErisDS yes I meant something like published:true/false. The question came up following your comment about having a better way to determine if a post is accessible for noAuth users?

The status of a post (published or !published) is incredibly important to permissions. It is also very important that we are able to determine this in this boolean style - 'published' vs 'not published', and that we do not depend on 'published' vs 'draft' because this will fall down when other kinds of statuses like 'queued' come along.

@ErisDS
Copy link
Member

ErisDS commented Mar 10, 2014

I'm happy to have a published boolean in the response if it makes managing permissions easier :)

@sebgie
Copy link
Contributor Author

sebgie commented Mar 10, 2014

I withdraw from wanting a published: true/false field. As you meant that we have to make sure that the status is checked for published or !published there is no reason to introduce another field.

@ErisDS
Copy link
Member

ErisDS commented Mar 10, 2014

👍

@sebgie
Copy link
Contributor Author

sebgie commented Mar 17, 2014

I updated the user responses to follow the JSON API format. See #2362.

@hswolff
Copy link
Contributor

hswolff commented Mar 18, 2014

Overall this looks good, JSON API taking care of many decisions obviously. Few questions though.

  1. How is the pagination data used in the meta object? I kinda dig how Facebook's Graph API handles paging, including the entire URL for the next page:
"paging": {
"cursors": {
  "after": "NzAzMjQyODAx", 
  "before": "MTA4NDM4MTk3NQ=="
}, 
"next": "https://graph.facebook.com/<id>/likes?limit=25&after=NzAzMjQyODAx"
}

It looks like that's somewhat covered by the extending doc on JSON API.
2. I've been confused in the past about featured: 0 and page: 0. At some point I saw those values as true/false and other places as 0/1. Are we settled on 0/1 as the true/false values?

@hswolff
Copy link
Contributor

hswolff commented Mar 18, 2014

Also are we looking to support URLs of the type http://example.com/comments/5,12,17,20 ? That'd be neat.

@sebgie
Copy link
Contributor Author

sebgie commented Mar 18, 2014

@hswolff

How is the pagination data used in the meta object?

The assumption was that limit, offset and total are enough information to calculate the next page and the number of pages available. The facebook cursor makes imo more sense if the post is identified by uuids instead of ids. Inclusion of the URL makes sense to me. If after/before properties are more appealing then offset, we could change the meta object to the following format. total or another indication of how many pages we have is needed for the pagination helper.

meta: {
    cursors: {
        after: 42,
        before: 20,
        limit: 15,
        total: 33,
        href: "https://api.example.com/whatever{?after,before,limit}"
    }
}

Navigation:

Next: https://api.example.com/whatever?after=42&limit=15
Prev: https://api.example.com/whatever?before=20&limit=15

I've been confused in the past about featured: 0 and page: 0.

It depends on what database you are using. mySql will return 0/1 and sqlite will return true/false or vice versa. There were some bugs related to this problem but I hope they are fixed now. fixBools is supposed to convert all booleans to int (base.js L80).

Also are we looking to support URLs of the type http://example.com/comments/5,12,17,20 ?

At the moment there is no URL like that. Do you have a specific use case in mind?

@hswolff
Copy link
Contributor

hswolff commented Mar 18, 2014

If after/before properties are more appealing then offset,

I prefer offset, was just curious about that other implementation.

mySql will return 0/1 and sqlite will return true/false or vice versa

So that's mostly my question: what's the canonical value that the API and models should return? It seems like 0/1. This is off topic from this thread so probably best continued elsewhere.

At the moment there is no URL like that. Do you have a specific use case in mind?

Nope, just curious if we were looking to support it. Unclear if it'd be necessary. Always easy to extend at a later point.

@halfdan
Copy link
Contributor

halfdan commented Mar 18, 2014

@hswolff JSON has a boolean type - so to make client side logic a bit clearer we should have the API return booleans when there's boolean data. The API layer should handle converting those values to whatever the database expects.

{ page: 0} is completely misleading IMHO - Sounds like it's the first page, while in reality it marks the page as non-static.

@hswolff
Copy link
Contributor

hswolff commented Mar 18, 2014

Agreed w/ @halfdan. I'd prefer explicit true/false both from API and from Models.

@sebgie
Copy link
Contributor Author

sebgie commented Mar 18, 2014

@halfdan good point, there is a datatype and therefore we should use it! I'll update the API responses accordingly.

@ErisDS
Copy link
Member

ErisDS commented Mar 18, 2014

Just wanna wade in here with some thoughts about the pagination.

There is an age old issue about pagination here: #110, there are also two open issues about prev/next page/post: #529 and #685 as well as a PR #1545 awaiting performance testing / suggestions for improvement.

The whole thing is a minefield, we want to make it performant but also usable. So I guess my question is, would the 'after' style pagination make it easier to do any of this? Is there a way to return URLs to the next/previous resource in a collection. Are there other implications for how we can implement these features considering that the proposal in #1545 was to always include the previous and next post in any single post 'GET', which I really didn't like but seemed like the only way to really implement the feature at the time.

@ErisDS
Copy link
Member

ErisDS commented Apr 2, 2014

I think we have successfully determined what the response should look like, in line with JSON API 😄

@ErisDS
Copy link
Member

ErisDS commented Apr 8, 2014

I believe the finalised formats are going to be:

Single Post:

{
    posts: [{
        id: 1,
        uuid: "f1e6bf27-fdd5-47a1-85a7-90eb2d05a0e5",
        status: "published",
        title: "Public post",
        slug: "public-post",
        markdown: "my public post",
        html: "<p>my public post</p>",
        image: null,
        featured: 0,
        page: 0,
        language: "en_US",
        meta_title: null,
        meta_description: null,
        author: {...},
        created_at: "2014-03-05T10:25:29.594Z",
        created_by: {...},
        updated_at: "2014-03-05T10:25:32.299Z",
        updated_by: {...},
        published_at: "2014-03-05T10:25:32.299Z",
        published_by: {...},
        tags: [ {...}, {...} ]
    }]
}

Post collection:

{
    posts: [{...}, {...}],
    meta: {
        limit: 15,
        offset: 0,
        total: 33,
        page: 1,
        pages: 3,
        next: 2
    }
}

@ErisDS ErisDS modified the milestones: 0.4 API, 0.6 Apps Apr 16, 2014
tigefa4u pushed a commit to tigefa4u/Ghost that referenced this issue Aug 3, 2022
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
affects:api Affects the Ghost API
Projects
None yet
Development

No branches or pull requests

4 participants