Introducing Hashnode GraphQL API - Public Beta
At Hashnode, we do our best to roll out the most requested features as fast and as often as possible. We have been discussing the idea of APIs for quite some time now, but unfortunately, we couldn't make significant progress in this area.
In 2019, one of our goals was to ship Hashnode APIs so that our users can build apps based on it. Today, I am happy to announce that we are launching the public beta of Hashnode GraphQL APIs. 🎉🌟🎊
Please note that this is a pretty minimal release. It has a lot of queries and a couple of mutations. We'd appreciate it if you would send us some early feedback so we can incrementally update the APIs in accordance with your comments in the coming days.
Get Started
To get started, go to api.hashnode.com, where you'll see the GraphQL playground. You can view the documentation and schema from the two aptly named buttons on your right. You can read more about the available queries, mutations, outputs, inputs, etc from there. We are working on improving the documentation and building a website which will be available with the 1.0 release.
Authentication
Authentication is based on personal access tokens. You can create/revoke them from Hashnode settings. Treat them as private keys to your Hashnode account. Always keep them confidential and safe.
Queries/Mutations which require authentication expect an Authorization
header with personal access token as its value.
Making Requests
Playground
- Fetching Hashnode's global stories feed
Query:
{
storiesFeed(type: GLOBAL){
title
}
}
If you need more properties of post object, you can specify them along with title
. For example, if you need title
and cuid
of the fetched posts, your query should look something like this:
{
storiesFeed(type: GLOBAL){
title
cuid
}
}
- Getting your personalized feed
Before making the query, make sure you have the personal access token in Authorization
header.
Query:
{
storiesFeed(type: FOR_ME){
title
}
}
Header:
{
"Authorization": "<your token here>"
}
The auth header can be added in the "HTTP Headers" section at the bottom of the playground.
- Getting details of a post
To get the details of a post, you can make the following query:
query {
post(cuid: "cjyc4klzr0031wds1yp4b2xn0") {
title
cuid
content
}
}
The result will look something like this:
- Getting the comments of a particular post
Query:
{
post(cuid: "cjzid61ek002b9ys19laotbwm"){
responses{
reactions{
reaction{
image
name
}
count
}
}
}
}
- Creating a new story
Mutation:
mutation {
createStory(
input: {
title: "The hashnode GraphQL API is here",
contentMarkdown: "<h1> Ahoy </h1>"
tags: [
{
_id: "56744721958ef13879b94c7e",
name: "General Programming",
slug: "programming"
}
]
}
) {
message
post{
title
}
}
}
Header:
{
"Authorization": "<your token here>"
}
Curl
Since GraphQL requests can be made over HTTP, you can use curl to start testing the API from the command line. You can also use any HTTP library you prefer in your programming language of choice
- Fetching Hashnode's global stories feed
Request:
curl -H "Content-Type: application/json" -X POST https://api.hashnode.com --data '{"query": "{ storiesFeed(type: FEATURED) { title }}" }'
Response:
{"data":{"storiesFeed":[{"title":"Design Patterns - Template Method"},{"title":"Getting started with Hugo and deploying to Netlify."},{"title":"Git first time setup"},{"title":"Thermal a free, open-source, cross-platform Git GUI application"},{"title":"JS Strings - Properties and Methods"},{"title":"Step 2: Single-node Docker Swarm and Smalltalk"},{"title":"Node.js Experience"},{"title":"Jasmine Testing Tutorial"},{"title":"How I attempted Image Classification in the browser using ml5.js and React"},{"title":"Demeter's Law: Don't talk to strangers!"},{"title":"Pro tip - two minute read"},{"title":"Unix, BSD, Minix, Linux - What, Who and When"},{"title":"Challenge Accepted: Build TensorFlow C Binding for Raspberry Pi in 2019"},{"title":"What's wrong with Promise.allSettled() and Promise.any()❓"},{"title":"Dockerless, Part 1: Which tools to replace Docker with and why"},{"title":"Getting started with Docker and Smalltalk!"},{"title":"React Visualization Libraries in 2019"},{"title":"Build a chatbot in 20 minutes using Flutter and Dialogflow"},{"title":"Dialogs and Alerts on Android using Anko and Kotlin"},{"title":"What getting back on Facebook taught me"},{"title":"Why we split the management of Admin Users and End Users"},{"title":"Solution for high CPU usage with Webpack/Laravel Mix"},{"title":"This project gave me an A++ in college 💯🎓 & this is prolly my last post 😭"},{"title":"Design Patterns - Strategy Pattern in JavaScript"},{"title":"A Ghost Demo: How to Go Headless with Ghost CMS [Tutorial]"},{"title":"Intro to Stackbit: Build a Custom JAMstack Site in Minutes"},{"title":"Use PostgreSql with Heroku CLI in Alpine container"},{"title":"CSS selectors and it's Specificity(Priority to override other)"},{"title":"Of Pushback tractors (Bit#2)"},{"title":"Build a school timetable with python"}]}}
- Creating a new Post
Request:
curl -H "Authorization: <your token here>" -H "Content-Type: application/json" -X POST https://api.hashnode.com --data '@query.json'
$ cat query.json
{
"query": "mutation createStory($input: CreateStoryInput!){ createStory(input: $input){ title } }",
"variables": {
"input": {
"title": "What are the e2e testing libraries you use ?",
"contentMarkdown": "I was wondering what e2e testing libaries do you use",
"tags": [
{
"_id": "56744723958ef13879b9549b",
"slug": "testing",
"name": "Testing"
}
],
"coverImage": "https://cdn.hashnode.com/res/hashnode/image-dev/upload/v1562665620141/tc-h-erqF.jpeg",
}
}
}
Response:
{"data":{"createStory":{"title":"What are the e2e testing libraries you use ?"}}}
Javascript
- Fetching Hashnode's global stories feed
Code:
const fetch = require('node-fetch')
fetch('https://api.hashnode.com', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': "<your token here>"},
body: JSON.stringify({
query: 'query { storiesFeed(type: GLOBAL){ title } }'
}),
})
.then(res => res.json())
.then(res => console.log(JSON.stringify(res)))
Result:
{"data":{"storiesFeed":[{"title":"Design Patterns - Template Method"},{"title":"Getting started with Hugo and deploying to Netlify."},{"title":"Git first time setup"},{"title":"Thermal a free, open-source, cross-platform Git GUI application"},{"title":"JS Strings - Properties and Methods"},{"title":"Step 2: Single-node Docker Swarm and Smalltalk"},{"title":"Node.js Experience"},{"title":"Jasmine Testing Tutorial"},{"title":"How I attempted Image Classification in the browser using ml5.js and React"},{"title":"Demeter's Law: Don't talk to strangers!"},{"title":"Pro tip - two minute read"},{"title":"Unix, BSD, Minix, Linux - What, Who and When"},{"title":"Challenge Accepted: Build TensorFlow C Binding for Raspberry Pi in 2019"},{"title":"What's wrong with Promise.allSettled() and Promise.any()❓"},{"title":"Dockerless, Part 1: Which tools to replace Docker with and why"},{"title":"Getting started with Docker and Smalltalk!"},{"title":"React Visualization Libraries in 2019"},{"title":"Build a chatbot in 20 minutes using Flutter and Dialogflow"},{"title":"Dialogs and Alerts on Android using Anko and Kotlin"},{"title":"What getting back on Facebook taught me"},{"title":"Why we split the management of Admin Users and End Users"},{"title":"Solution for high CPU usage with Webpack/Laravel Mix"},{"title":"This project gave me an A++ in college 💯🎓 & this is prolly my last post 😭"},{"title":"Design Patterns - Strategy Pattern in JavaScript"},{"title":"A Ghost Demo: How to Go Headless with Ghost CMS [Tutorial]"},{"title":"Intro to Stackbit: Build a Custom JAMstack Site in Minutes"},{"title":"Use PostgreSql with Heroku CLI in Alpine container"},{"title":"CSS selectors and it's Specificity(Priority to override other)"},{"title":"Of Pushback tractors (Bit#2)"},{"title":"Build a school timetable with python"}]}}
- Creating a post
Code:
const fetch = require('node-fetch')
fetch('https://api.hashnode.com', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Authorization': "<your token here>" },
body: JSON.stringify({
"query": "mutation createStory($input: CreateStoryInput!){ createStory(input: $input){ title } }",
"variables": {
"input": {
"title": "What are the e2e testing libraries you use ?",
"contentMarkdown": "I was wondering what e2e testing libaries do you use",
"tags": [
{
"_id": "56744723958ef13879b9549b",
"slug": "testing",
"name": "Testing"
}
],
"coverImage": "https://cdn.hashnode.com/res/hashnode/image-dev/upload/v1562665620141/tc-h-erqF.jpeg",
}
}
}),
})
.then(res => res.json())
.then(res => console.log(JSON.stringify(res)))
Result:
{"data":{"createPost":{"title":"What are the e2e testing libraries you use ?"}}}
Client Libraries
Client libraries will provide you with the fastest and most comprehensive way to access the API and can assist in handling auth, formatting your requests and your responses. Learn more about Apollo Client here.
These are just a few examples, feel free to explore the API further. We are very excited to see what you folks are going to build. In case you are looking for some ideas, here are a few off the top of my head:
- A Discord/Slack bot that fetches the top Hashnode stories and posts them periodically
- The best of Hashnode in a different UI -- e.g. A Hackernews style website that showcases
top
,featured
andrecent
stories from Hashnode. - Posting to Hashnode from the comfort of your terminal. This one is for terminal lovers. If you are writing quick tips or a short article and don't want to go to Hashnode to write them, you can always post them by using our API.
We are just getting started. So, feel free to share any bugs, feedback or your cool ideas in our Discord server. We look forward to keeping up with your requests and releasing more features in the coming days!
Update: The following are the features/functionalities we are working on for 1.0, we are targetting last week of August to put things together and cut a release.
Post
Create post- Update post
- Delete post
Get post(s)React to post
Response
Create response- Update response
- Delete response
Get response(s)- React to response
Reply
- Create reply
- Update reply
- Delete reply
- Get reply/replies
- React to reply
Nodes
- Get Node(s)
- Follow a Node
- Top / Recent posts
User
- List users
- Get details of a user
- Get followers of a user
- Follow a user
- Update user profile
- Get stories by a user
Devblog
- Get posts from a blog
Update
Closet🚽 🚹Developer as 🌙🇮🇩
Congrats 🎉
Software Engineer, Technical Consultant & Mentor
Very nice! I love it! Quick questions:
- How will you do API versioning?
- Will you provide official libraries for selected languages (TS, Java, Go, Python, Rust,...)? Will there be a partner-program for maintainers of (unofficial) libs?
- What is the rough scope for the 1.0 release? What about user management, messaging, notifications,... ?
- Will DevBlog and future endeavors also be included in the API?
Hey Marco Alka, Thanks for the thoughtful questions. The following are our plans,
How will you do API versioning?
From 1.0 we'll have some sort of namespacing, the endpoints will be something like graphql/v1
Will you provide official libraries for selected languages (TS, Java, Go, Python, Rust,...)? Will there be a partner-program for maintainers of (unofficial) libs?
We haven't thought of official libraries yet, I think we'll have to rely on the community for those. We'll give an update on this shortly.
What is the rough scope for the 1.0 release? What about user management, messaging, notifications,... ?
I've updated the post to include what we are aiming for in the 1.0 release.
Will DevBlog and future endeavors also be included in the API?
In the long term, we see the API to provide the same functionalities as the web app.
Please let me know if you have any questions. I'm curious to know what you thought.
Thank you for answering all of my questions and being so open about the whole process!
In the long term, we see the API to provide the same functionalities as the web app.
That sounds awesome. Hashnode as an API 😂 I love this idea! It certainly allows you to make radical front-end changes easily, and the community can hook into everything going on, ideally providing new opportunities and possibilities. A living platform.
Student, preparing for a Journey filled with Development
While trying to create a post using the APIs I got some error I am not able to resolve, for more information check this Stack overflow question
Software Engineer
I like that! One question left: how do you fetch threads like AMA and replies from them too?
Product Engineer (Mobile) @ SocialCops
The api looks good.
Aravind One question: How do I get the count of individual reactions?
As of now it's just returning totalReactions
and in reactions just name
and image
.
Front End Developer.
Congratulations on this achievement.
Sadly for me, I still do not understand the basics of APIs. Would be really grateful if anyone can recommend articles for me : (
Articles are the top and below section of your reply were super interesting, I really appreciate your time sir
Technical Lead for CultureMap, InnovationMap, SportsMap, & AutomotiveMap. Python, Django, Elm, & JavaScript.
Nicely done, guys and gals.
Thank youSeth Livingston, Glad you liked it. Looking forward to hearing more from you.
Flutter & full-stack JavaScript enthusiast
This is awesome. I don't see a way to get paged query results, is that in the queue? What's the current limit?
Yea, paging would probably be more efficient.
I don't have any experience with MongoDB, but with SQL you could just say:
SELECT column FROM table LIMIT 30 OFFSET 30;
Student
Great job on the new API ! Can you please point me towards the API docs ? I can't seem to find it in this post. Thanks
Hey Ashwin Shenoy, proper docs will be released along with the stable version. Can you checkout the playground and the accompanying docs in the meantime?
Software Engineer & Consultant
Could you allow us for giving tokens a string name?
Comments (31)