Models

Post

{
  title: String,
  url: String,
  user: 'username',
  score: Number,
  date: Number,
  content: String,
  type: String,
  tags: Array('postTag')
}

PostTag

postTag: {
  tag: 'tag',
  post: 'post',
  score: Number
}

PostTagVote

postTagVote: {
  tag: 'tag',
  post: 'post',
  user: 'username',
  score: Number
}

Tag

tag: {
  name: String,
  count: Number
}

Comment

comment: {
  id: String
  time: Number,
  post: 'post',
  parent: 'comment'
  childCount: Number,
  content: String,
  user: 'username',
  upvotes: Number,
  downvotes: Number,
  threadUpvotes: Number,
  threadDownvotes: Number
}

CommentVote

commentVote: {
  comment: 'comment',
  post: 'post',
  user: 'user',
  date: Date,
  positive: Boolean
}

Posts API

GET /posts

Returns 100 posts.

Parameters

?offset=NUMBER

Offsets the responses by a set number.

?sort=popular|top|new

Returns posts sorted by a particular algorithm.

  • popular default

    Posts are sorted by score, within a time span between now and the requesting user's last login, or 24 hours, whichever is longer.

  • new

    Posts are sorted by date. Newest first.

  • top

    Posts are sorted by score. Highest first.

?time=all|day|week|month|year

Only returns posts that were created within a specific time period.

  • all default

    No time constraints.

  • day

    Only returns posts created in the last day.

  • week

    Only returns posts created in the last week.

  • month

    Only returns posts created in the last month.

  • year

    Only returns posts created in the last year.

?tag=TAG

Only returns results that match a specific tag. Multiple tags can be listed by separating tags with a +. If more than one tag is listed, it will return posts that match any of the tags. As an example:

/posts?tag=funny+wtf

will return 100 posts that either have a tag of funny or wtf.

?user=USER

Only returns results posts that were made by a specific user.

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Example response

{
  posts: [
    {
      "id": 2,
      "title": "test post",
      "user": "wombodombo",
      "time": "1577171754000",
      "url": "this_is_crazy",
      "link": "", //optional
      "content": "{\"ops\":[{\"insert\":\"This is a cool photo.\\n\"}]}", //quilljs formatted content
      "type": "image",
      "score": 148,
      "commentCount": 21,
      "width": 1920, //will appear for video and image content
      "height": 1080, //will appear for video and image content
      "tags": [
        {
          "tag": "funny",
          "score": 10,
        },
        {
          "tag": "meme",
          "score": 7,
        },
        {
          "tag": "image",
          "score": 4,
        }
      ]
    },

    // ...etc

  ]
}


GET /post/view/:url

Returns the data for a single post, referenced by its url. The date for the tags is the date the postTag association was first created.

Requirements

None. Should be available publicly

Example response

{
  "id": 2,
  "title": "test post",
  "user": "wombodombo",
  "time": "1577171754000",
  "url": "this_is_crazy",
  "link": "", //optional
  "content": "{\"ops\":[{\"insert\":\"This is a cool photo.\\n\"}]}", //quilljs formatted content
  "type": "image",
  "score": 148,
  "commentCount": 21,
  "width": 1920, //will appear for video and image content
  "height": 1080, //will appear for video and image content
  "tags": [
    {
      "tag": "funny",
      "score": 10,
    },
    {
      "tag": "meme",
      "score": 7,
    },
    {
      "tag": "image",
      "score": 4,
    }
  ]
}

POST /post/create

Accepts a JSON payload to create a new post in the database

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Example payload

{
  "title": "My Cool Post!",
  "url": "my-neato-post-wow",
  "content": "This is the long form content for this post...",
  "type": "blog"
}

Example response

{
    "title": "My Cool Post!",
    "user": "lapubell",
    "time": "1577171754000",
    "url": "my-neto-post-wow",
    "tags": []
}

GET /post/url-is-available/:url

Returns a boolean based on if the requested URL is available

Requirements

None. Should be available publicly

Example response

true

Tags API

GET /tags

Should return 100 tags, sorted by number of postTags for that tag that have been created between now and the requesting user's last login, or one month, whichever is longer. The number of postTags for that tag should be populated into the "count" field.

Example response

{
  "tags": [
      {
          "tag": "art",
          "count": 4849,
      },
      {
          "tag": "funny",
          "count": 382,
      },

      // ...etc
    }
  ]
}

PostTags API

POST/posttag/create

Adds a tag to a post.

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Example payload

{
  "post": "a-cool-painting",
  "tag": "art",
}

Example response

{
  "post": "a-cool-painting",
  "tag": "art",
  "score": 1
}

POST/posttag/add-vote

Adds a vote for a posttag.

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Example payload

{
  "post": "a-cool-painting",
  "tag": "art",
}

Example response

{
  "post": "a-cool-painting",
  "tag": "art",
  "score": 41
}

POST/posttag/remove-vote

Removes an existing vote for a posttag. If it is the only vote for it, this action also removes the posttag.

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Example payload

{
  "post": "a-cool-painting",
  "tag": "art",
}

Example response

{
  "post": "a-cool-painting",
  "tag": "art",
  "score": 40
}

Comments API

GET /comments/post/:post

Should return 100 comments for a specific post.

Parameters

?offset=NUMBER

Offsets the responses by a set number.

?sort=popular|new

Returns comments sorted by a particular algorithm.

  • popular default

    Comments are sorted by threadUpvotes. Highest first.

  • new

    Comments are sorted by date. Newest first.

Example response

{
  "comments": [
    {
      "id": "YPGpbT",
      "time": "1577171754000",
      "post": "this_is_crazy", 
      "parent": null,
      "childCount": 4,
      "content": "I really like this",
      "user": "bojackson",
      "upvotes": 144,
      "downvotes": 38,
      "threadUpvotes": 188,
      "threadDownvotes": 22
    },
    {
      "id": "qc33cG",
      "time": "1577171754000",
      "post": "this_is_crazy", 
      "parent": null,
      "childCount": 0,
      "content": "How neat!",
      "user": "mamadisco",
      "upvotes": 42,
      "downvotes": 7,
      "threadUpvotes": 42,
      "threadDownvotes": 7
    },

    ...etc

  ]
}

GET /comments/user/:user

Should return 100 comments for a specific user.

Parameters

?offset=NUMBER

Offsets the responses by a set number.

?sort=popular|new

Returns comments sorted by a particular algorithm.

  • popular default

    Comments are sorted by (upvotes - downvotes). Highest first.

  • new

    Comments are sorted by date. Newest first.

Example response

{
  "comments": [
    {
      "id": "YPGpbT",
      "time": "1577171754000",
      "post": "this_is_crazy", 
      "parent": null,
      "childCount": 4,
      "content": "I really like this",
      "user": "jjcm",
      "upvotes": 144,
      "downvotes": 38,
      "threadUpvotes": 188,
      "threadDownvotes": 22
    },
    {
      "id": "qc33cG",
      "time": "1577171754000",
      "post": "rickroll", 
      "parent": null,
      "childCount": 0,
      "content": "my fav song",
      "user": "jjcm",
      "upvotes": 42,
      "downvotes": 7,
      "threadUpvotes": 42,
      "threadDownvotes": 7
    },

    ...etc

  ]
}

GET /comments/comment/:comment

Should return 100 replies for a specific comment.

Parameters

?offset=NUMBER

Offsets the responses by a set number.

?sort=popular|new

Returns comments sorted by a particular algorithm.

  • popular default

    Comments are sorted by threadUpvotes. Highest first.

  • new

    Comments are sorted by date. Newest first.

Example response

{
  "comments": [
    {
      "id": "YPGpbT",
      "time": "1577171754000",
      "post": "this_is_crazy", 
      "parent": "GhB83c",
      "childCount": 4,
      "content": "I really like this",
      "user": "bojackson",
      "upvotes": 144,
      "downvotes": 38,
      "threadUpvotes": 188,
      "threadDownvotes": 22
    },
    {
      "id": "qc33cG",
      "time": "1577171754000",
      "post": "this_is_crazy", 
      "parent": "GhB83c",
      "childCount": 0,
      "content": "How neat!",
      "user": "mamadisco",
      "upvotes": 42,
      "downvotes": 7,
      "threadUpvotes": 42,
      "threadDownvotes": 7
    },

    ...etc

  ]
}

POST /comment/create

Allows the creation of a comment on a specific post. Can either be a top level comment or a reply to an existing comment.

Example payload

{
  post: "this_is_crazy",
  parent: null,
  content: "Hey this is a comment!",
}

Example response

{
  "id": "qc33cG",
  "time": "1577171754000",
  "post": "this_is_crazy", 
  "parent": null,
  "childCount": 0,
  "content": "How neat!",
  "user": "jjcm",
  "upvotes": 0,
  "downvotes": 0,
  "threadUpvotes": 0,
  "threadDownvotes": 0
}

POST /comment/update/:comment

Allows for the update of a specific comment. Comment must be made by the user updating it.

Example payload

{
  post: "this_is_crazy",
  parent: null,
  content: "Hey this is a *cool* comment!",
}

Example response

{
  "id": "qc33cG",
  "time": "1577171754000",
  "post": "this_is_crazy", 
  "parent": null,
  "childCount": 0,
  "content": "How neat!",
  "user": "jjcm",
  "upvotes": 0,
  "downvotes": 0,
  "threadUpvotes": 0,
  "threadDownvotes": 0
}

POST /comment/delete/:comment

Deletes a specific comment. Comment must be made by the user deleting it.

Example response

true

User API

POST /user/login

Logs the user in. Returns a JWT.

Example payload

{
  "email": "jjcm@non.io",
  "password": "hunter2"
}

Example response

{
  "accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFzZGZAYXNkZi5jb20iLCJleHBpcmVzQXQiOjE1ODMxNDc1NDh9.okbK22xZYZFk19K_NwLd7g48mxQZfWR5filMadonSe8",
  "refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpAampjbS5vcmciLCJleHBpcmVzQXQiOjE3NDQ2NzE2MjksInR5cGUiOiJyZWZyZXNoIn0.wBnYbLB6kGinMRnM1ODqGmV1cv-VYMBzPZUNGsRDGqU",
  "roles": ["admin"], // typically empty, right now this is only used for admins
  "username":"jjcm"

}

POST /user/refresh-access-token

Refreshes the access token. Returns a new access token and a new refresh token.

Example payload

{
  "refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpAampjbS5vcmciLCJleHBpcmVzQXQiOjE3NDQ2NzE2MjksInR5cGUiOiJyZWZyZXNoIn0.wBnYbLB6kGinMRnM1ODqGmV1cv-VYMBzPZUNGsRDGqU",
}

Example response

{
  "accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpAampjbS5vcmciLCJleHBpcmVzQXQiOjE3MDg5ODg2NzIsInR5cGUiOiJhY2Nlc3MifQ.m1vCcqE41C11us1WZnn2suxCMmRh5kcrzd200BaCips",
  "refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpAampjbS5vcmciLCJleHBpcmVzQXQiOjE3NDQ2NzE4NzIsInR5cGUiOiJyZWZyZXNoIn0.D_NunlFZBMR73ryqlcKBzLZaZnGyP3LyzW6m0JQy0nM",
}

POST /user/register

Creates a new user. Returns the registered info along with an access token and a refresh token.

Example payload

{
  "username": "jjcm",
  "email": "jjcm@non.io",
  "password": "hunter2",
}

Example response

{
  "accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImFzZGZAYXNkZi5jb20iLCJleHBpcmVzQXQiOjE1ODMxNDc1NDh9.okbK22xZYZFk19K_NwLd7g48mxQZfWR5filMadonSe8",
  "refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpAampjbS5vcmciLCJleHBpcmVzQXQiOjE3NDQ2NzE2MjksInR5cGUiOiJyZWZyZXNoIn0.wBnYbLB6kGinMRnM1ODqGmV1cv-VYMBzPZUNGsRDGqU",
  "username":"jjcm"
}

GET /user/username-is-available/:username

Returns a boolean based on if the requested username is available

Requirements

None. Should be available publicly

Example response

true

Notifications API

Model

{
    "id": Number,
    "comment_id": Number,
    "date": Number,
    "post": String, //refers to the post's url
    "post_title": String,
    "post_type": String,
    "content": String, //rich text in Quill format
    "user": String,
    "upvotes": Number,
    "downvotes": Number,
    "parent": Number,
    "parent_content": String, //rich text in Quill format
    "edited": Boolean,
    "read": Boolean
}

GET /notifications

Should return 100 notifications, sorted by most recent.

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Parameters

?unread=true|false

Returns only unread notifications if set to true

  • true

    Only returns notifications that are unread.

  • false default

    Returns all notifications.

Example response

{
  "notifications": [
    {
        "id": 1,
        "comment_id": 121100,
        "date": 1691296535000,
        "post": "nonio-changelog-9",
        "post_title": "Nonio update - Notifications and guest contributions",
        "post_type": "blog",
        "content": "{\"ops\":[{\"insert\":\"Nice work!\\n\"}]}",
        "user": "avocado",
        "upvotes": 1,
        "downvotes": 0,
        "parent": 0,
        "parent_content": "",
        "edited": false,
        "read": true
    },
    {
        "id": 2,
        "comment_id": 121101,
        "date": 1691297044000,
        "post": "avo-coffeeshop",
        "post_title": "Avocado the dog",
        "post_type": "image",
        "content": "{\"ops\":[{\"insert\":\"Thank you Avo, very cool!\\n\"}]}",
        "user": "kate",
        "upvotes": 1,
        "downvotes": 0,
        "parent": 0,
        "parent_content": "",
        "edited": false,
        "read": true
    },
    // ...etc
  ]
}

GET /notifications/unread-count

Gets the count of unread notifications for the user.

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Example response

4

GET /notification/mark-read

Marks a specific notification ID as read.

Requirements

  • Authentication

    With no auth, should return
    401 Unauthorized
    

Example payload

{
  "id": 1
}

Example response

true