From ae47e8db2cc62199d036f819af910eea3caeea53 Mon Sep 17 00:00:00 2001 From: Ben Halpern Date: Tue, 21 May 2019 12:46:52 -0400 Subject: [PATCH] Add API portal (#2915) * Add API portal * Change title tag --- app/views/pages/api.html.erb | 317 +++++++++++++++++++++++++++++++++++ config/routes.rb | 2 +- spec/requests/pages_spec.rb | 7 + 3 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 app/views/pages/api.html.erb diff --git a/app/views/pages/api.html.erb b/app/views/pages/api.html.erb new file mode 100644 index 000000000..6b2884090 --- /dev/null +++ b/app/views/pages/api.html.erb @@ -0,0 +1,317 @@ +<% title "DEV API" %> + +<%= content_for :page_meta do %> + + API info and guides"> + + + + + API" /> + + + + API"> +<% end %> + +
+
+
+

+ DEV Articles API (beta) +

+

As of May 16th 2019 DEV has released a beta version of a REST API to create or update articles. Check back to this page for all updates related to the API.

+
+
+
+ +

+ + + About the Articles API +

+

"Articles" are all the posts that users create DEV that typically show up in the feed. It can be a blog post, discussion question, help thread etc. but is referred to as article within the code. It is different from listings, comments, podcasts, etc.

+ +

+ + + Getting an access token +

+ +

Authentication for the Articles API requires the DEV API access token. To obtain one, please follow these steps:

+ +
    +
  • Connect to https://dev.to/settings/account +
  • +
  • +

    In the DEV API Access Tokens section create a new token by adding a description and clicking on "Generate Token":

    + +

    +
  • +
  • You'll see the newly generated token in the same view:

  • +
+ +

+ +

+ + + Creating an article through the API +

+ +

Here is an example of how to create an article:

+ +
curl -X POST -H "Content-Type: application/json" \
+  -H "api-key: ACCESS_TOKEN" \
+  -d '{"article": {"title": "Title", "body_markdown": "Body", "published": false, "tags": ["discuss", "javascript"]}}' \
+  https://dev.to/api/articles
+  
+ +

If published is set to true then the article will be live instantly.

+ +

+ + + Successful response +

+ +

In case of a successful response (HTTP 201), the full article resource will be returned to the client:

+ +
{
+    "type_of": "article",
+    "id": 110878,
+    "title": "Title",
+    "description": "Body\n\n...",
+    "cover_image": null,
+    "readable_publish_date": null,
+    "social_image": "http://dev.to/social_previews/article/110878.png",
+    "tag_list": "discuss, javascript",
+    "tags": [
+      "discuss",
+      "javascript"
+    ],
+    "slug": "title-2bp3-temp-slug-5875367",
+    "path": "/rhymes/title-2bp3-temp-slug-5875367",
+    "url": "https://dev.to/rhymes/title-2bp3-temp-slug-5875367",
+    "canonical_url": "https://dev.to/rhymes/title-2bp3-temp-slug-5875367",
+    "comments_count": 0,
+    "positive_reactions_count": 0,
+    "created_at": "2019-05-21T13:34:26Z",
+    "edited_at": null,
+    "crossposted_at": null,
+    "published_at": null,
+    "last_comment_at": "2017-01-01T05:00:00Z",
+    "body_html": "<p>Body</p>\n\n",
+    "ltag_style": [],
+    "ltag_script": [],
+    "user": {
+      "name": "rhymes",
+      "username": "rhymes",
+      "twitter_username": "rhymes_",
+      "github_username": "rhymes",
+      "website_url": null,
+      "profile_image": "https://res.cloudinary.com/practicaldev/image/fetch/s--qhCNe-v6--/c_fill,f_auto,fl_progressive,h_640,q_auto,w_640/https://thepracticaldev.s3.amazonaws.com/uploads/user/profile_image/2693/146201.jpeg",
+      "profile_image_90": "https://res.cloudinary.com/practicaldev/image/fetch/s--IQPhTQnb--/c_fill,f_auto,fl_progressive,h_90,q_auto,w_90/https://thepracticaldev.s3.amazonaws.com/uploads/user/profile_image/2693/146201.jpeg"
+    }
+  }
+  
+

+ + + Create an article on behalf an organisation +

+ +

Just add publish_under_org as true to the JSON request:

+
curl -X POST -H "Content-Type: application/json" \
+  -H "api-key: ACCESS_TOKEN" \
+  -d '{"article": {"title": "Title", "body_markdown": "Body", "published": false, "publish_under_org": true}}' \
+  https://dev.to/api/articles
+  
+

+ + + Using the front matter +

+ +

You can omit most JSON parameters by using the markdown front matter. So for example, if you have markdown article similar to the following:

+
---
+  title: "A sample article about JavaScript"
+  published: false
+  description: "this is a sample article about javascript"
+  tags: discuss, javascript
+  series: intro-to-javascript
+  canonical_url: canonical URL of the article
+  ---
+
+  This is an intro about JavaScript
+  
+ +

you can send it within the body_markdown parameter:

+ +
curl -X POST -H "Content-Type: application/json" \
+  -H "api-key: ACCESS_TOKEN" \
+  -d '{"article": {"body_markdown": "---\ntitle: A sample article about...\npublished: false\n...", "publish_under_org": true}}' \
+  https://dev.to/api/articles
+  
+ +

The front matter will take precedence on other JSON params with the same name.

+ +

+ + + Available JSON parameters +

+ +
title: The title of an article (string, optional)
+  description: Description of the article (string, optional)
+  body_markdown: The Markdown body, with or without a front matter (string, required)
+  published: True if the article should be published right away, defaults to false (boolean, optional)
+  tags: A list of tags for the article (array, optional)
+  series: The name of the series the article should be published within (string, optional)
+  publish_under_org: True if the article should be placed under the organization belonging to the user creating the article, defaults to false (boolean, optional)
+  main_image: URL of the image to use as the cover (string, optional)
+  canonical_url: canonical URL of the article (string, optional)
+  
+

+ + + Error responses +

+ +

Unauthorized

+
{
+    "error": "unauthorized",
+    "status": 401
+  }
+  
+ +

Parameter validation error

+ +
{
+    "error": "Validation failed: Title can't be blank",
+    "status": 422
+  }
+  
+

+ + + Updating an article through the API +

+ +

Here is an example of how to update an article:

+
curl -X PUT -H "Content-Type: application/json" \
+  -H "api-key: ACCESS_TOKEN" \
+  -d '{"article": {"title": "New Title"}}' \
+  https://dev.to/api/articles/ARTICLE_ID
+  
+

+ + + Successful response +

+ +

In case of a successful response (HTTP 200), the full article resource will be returned to the client:

+
{
+    "type_of": "article",
+    "id": 110878,
+    "title": "New Title",
+    "description": "Body\n\n...",
+    "cover_image": null,
+    "readable_publish_date": null,
+    "social_image": "http://dev.to/social_previews/article/110878.png",
+    "tag_list": "discuss, javascript",
+    "tags": [
+      "discuss",
+      "javascript"
+    ],
+    "slug": "title-2bp3-temp-slug-5875367",
+    "path": "/rhymes/title-2bp3-temp-slug-5875367",
+    "url": "https://dev.to/rhymes/title-2bp3-temp-slug-5875367",
+    "canonical_url": "https://dev.to/rhymes/title-2bp3-temp-slug-5875367",
+    "comments_count": 0,
+    "positive_reactions_count": 0,
+    "created_at": "2019-05-21T13:34:26Z",
+    "edited_at": null,
+    "crossposted_at": null,
+    "published_at": null,
+    "last_comment_at": "2017-01-01T05:00:00Z",
+    "body_html": "<p>Body</p>\n\n",
+    "ltag_style": [],
+    "ltag_script": [],
+    "user": {
+      "name": "rhymes",
+      "username": "rhymes",
+      "twitter_username": "rhymes_",
+      "github_username": "rhymes",
+      "website_url": null,
+      "profile_image": "https://res.cloudinary.com/practicaldev/image/fetch/s--qhCNe-v6--/c_fill,f_auto,fl_progressive,h_640,q_auto,w_640/https://thepracticaldev.s3.amazonaws.com/uploads/user/profile_image/2693/146201.jpeg",
+      "profile_image_90": "https://res.cloudinary.com/practicaldev/image/fetch/s--IQPhTQnb--/c_fill,f_auto,fl_progressive,h_90,q_auto,w_90/https://thepracticaldev.s3.amazonaws.com/uploads/user/profile_image/2693/146201.jpeg"
+    }
+  }
+  
+

+ + + Notes about the front matter during an update +

+ +

Keep in mind that if the original article was created with the front matter in the markdown then that front matter will still take precedence over the partial updates. So if you need to change parameters present in the front matter, you should send the whole edited body (with the front matter parameter changed) in the body_markdown parameter.

+

+ + + Available JSON parameters +

+
title: The title of an article (optional)
+  description: Description of the article (optional)
+  body_markdown: The Markdown body, with or without a front matter (required)
+  published: True if the article should be published right away, defaults to false (optional)
+  series: The name of the series the article should be published within (optional)
+  publish_under_org: True if the article should be placed under the organization belonging to the user creating the article, defaults to false (optional)
+  main_image: URL of the image to use as the cover (optional)
+  canonical_url: canonical URL of the article (optional)
+  
+

+ + + Error responses +

+ +

Unauthorized

+
{
+    "error": "unauthorized",
+    "status": 401
+  }
+  
+ +

Parameter validation error

+ + +
{
+ "error": "Validation failed: Title can't be blank",
+ "status": 422
+ }
+
+

+
+
+

+ Rate limiting
+

+ +

There is a limit of 10 articles created each 30 seconds by the same user.

+ +

There are no limits on the updates.

+ +

+ + + Additional resources +

+ + + +
+
+
diff --git a/config/routes.rb b/config/routes.rb index 0d63538da..978d87a4e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -212,6 +212,7 @@ Rails.application.routes.draw do # You can have the root of your site routed with "root get "/about" => "pages#about" + get "/api", to: "pages#api" get "/privacy" => "pages#privacy" get "/terms" => "pages#terms" get "/contact" => "pages#contact" @@ -223,7 +224,6 @@ Rails.application.routes.draw do get "/rly" => "pages#rlyweb" get "/code-of-conduct" => "pages#code_of_conduct" get "/report-abuse" => "pages#report_abuse" - get "/infiniteloop" => "pages#infinite_loop" get "/faq" => "pages#faq" get "/live" => "pages#live" get "/swagnets" => "pages#swagnets" diff --git a/spec/requests/pages_spec.rb b/spec/requests/pages_spec.rb index e38bff7ce..ac8a156a3 100644 --- a/spec/requests/pages_spec.rb +++ b/spec/requests/pages_spec.rb @@ -16,6 +16,13 @@ RSpec.describe "Pages", type: :request do end end + describe "GET /api" do + it "has proper headline" do + get "/api" + expect(response.body).to include("DEV Articles API") + end + end + describe "GET /privacy" do it "has proper headline" do get "/privacy"