Misar IO Docs
Misar.BlogApi Reference

GraphQL API

Query and mutate MisarBlog content using the GraphQL API — articles, profiles, series, and analytics.

Overview

MisarBlog exposes a GraphQL API at api.misar.io/blog/graphql for flexible content queries. GraphQL lets you fetch only the fields you need and combine multiple queries in a single request.

Both GET (for queries) and POST (for queries and mutations) are supported. Use POST with a JSON body for production clients.


Authentication

All requests require a valid API key in the Authorization header:

Authorization: Bearer mbk_your_api_key

Generate keys at misar.blog/dashboard/api-keys.


Endpoint

POST api.misar.io/blog/graphql
Content-Type: application/json
Authorization: Bearer mbk_your_api_key

Schema

Types

type Article {
  id: ID!
  slug: String!
  title: String!
  excerpt: String
  contentHtml: String
  status: String!        # "draft" | "published"
  visibility: String!    # "public" | "subscribers" | "paid" | "private"
  tags: [String!]
  publishedAt: String
  updatedAt: String
  viewCount: Int
  canonicalUrl: String
  featuredImageUrl: String
  author: Profile
  series: Series
}

type Profile {
  id: ID!
  username: String!
  displayName: String
  bio: String
  avatarUrl: String
  articleCount: Int
  subscriberCount: Int
  profileUrl: String
}

type Series {
  id: ID!
  slug: String!
  title: String!
  description: String
  articleCount: Int
}

type Analytics {
  totalViews: Int!
  uniqueVisitors: Int!
  subscriberCount: Int!
  totalRevenueCents: Int!
  period: String!
}

type ArticleConnection {
  nodes: [Article!]!
  totalCount: Int!
  hasNextPage: Boolean!
}

Queries

me — Get your profile

query {
  me {
    id
    username
    displayName
    bio
    avatarUrl
    articleCount
    subscriberCount
    profileUrl
  }
}

article — Get a single article

query {
  article(slug: "my-first-post") {
    id
    title
    excerpt
    contentHtml
    status
    visibility
    tags
    publishedAt
    viewCount
    canonicalUrl
  }
}

articles — List articles

query {
  articles(limit: 20, offset: 0, status: "published", tag: "productivity") {
    totalCount
    hasNextPage
    nodes {
      id
      slug
      title
      excerpt
      tags
      publishedAt
      viewCount
    }
  }
}
ArgumentTypeDefaultDescription
limitInt20Results per page (max 100)
offsetInt0Pagination offset
statusStringFilter by "draft" or "published"
tagStringFilter by tag slug

series — List series

query {
  series {
    id
    slug
    title
    description
    articleCount
  }
}

Pass slug to get a specific series:

query {
  series(slug: "my-series") {
    id
    title
    articleCount
  }
}

analytics — Get analytics

query {
  analytics(days: 30) {
    totalViews
    subscriberCount
    totalRevenueCents
    period
  }
}
ArgumentTypeDefaultDescription
daysInt30Lookback window in days

Mutations

createDraft — Create a new draft

mutation {
  createDraft(
    title: "My New Article"
    content: "<p>Hello world</p>"
    tags: ["writing", "productivity"]
  ) {
    id
    slug
    title
    status
  }
}

updateArticle — Update an article

mutation {
  updateArticle(
    slug: "my-new-article"
    title: "My Updated Title"
    excerpt: "A better summary"
    tags: ["writing", "tips"]
  ) {
    id
    slug
    title
    updatedAt
  }
}

publishDraft — Publish a draft

mutation {
  publishDraft(slug: "my-new-article") {
    id
    slug
    status
    publishedAt
  }
}

deleteArticle — Delete an article

mutation {
  deleteArticle(slug: "my-new-article")
}

Returns true on success, false on failure.


TypeScript client example

async function queryGraphQL<T>(query: string, variables?: Record<string, unknown>): Promise<T> {
  const res = await fetch("https://api.misar.io/blog/graphql", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer mbk_your_api_key",
    },
    body: JSON.stringify({ query, variables }),
  });
  const { data, errors } = await res.json();
  if (errors?.length) throw new Error(errors[0].message);
  return data;
}

// Usage
const { articles } = await queryGraphQL<{ articles: { nodes: { title: string }[] } }>(`
  query ListArticles($limit: Int) {
    articles(limit: $limit, status: "published") {
      nodes { title slug }
      totalCount
    }
  }
`, { limit: 10 });

Errors

GraphQL errors are returned in the errors array alongside data:

{
  "errors": [
    {
      "message": "Unauthorized",
      "locations": [{ "line": 2, "column": 3 }],
      "path": ["me"]
    }
  ],
  "data": { "me": null }
}
MessageCause
UnauthorizedMissing or invalid API key
insert error / update errorDatabase constraint violation