Skip to main content

API Documentation

Multiforum uses GraphQL for its API. This guide covers how to explore and use the API.

GraphQL Playground

Accessing the Playground

When running the backend in development mode, the GraphQL Playground is available at:

http://localhost:4000/graphql

The playground provides:

  • Interactive query editor
  • Schema documentation
  • Query history
  • Response explorer

Setting Up Authentication

For authenticated queries:

  1. Log in to the frontend
  2. Open browser dev tools → Application → Local Storage
  3. Copy the access token
  4. In Playground, click HTTP Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}

Query Examples

Fetching Discussions

query GetDiscussions($channelUniqueName: String!, $limit: Int, $offset: Int) {
getDiscussionsInChannel(
channelUniqueName: $channelUniqueName
limit: $limit
offset: $offset
) {
id
title
body
createdAt
Author {
username
profilePicURL
}
DiscussionChannels {
id
weightedVotesCount
Comments {
id
}
}
}
}

Variables:

{
"channelUniqueName": "general",
"limit": 10,
"offset": 0
}

Fetching Events

query GetEvents($channelUniqueName: String!, $startDate: DateTime, $endDate: DateTime) {
events(
where: {
EventChannels_SOME: {
Channel: { uniqueName: $channelUniqueName }
}
startTime_GTE: $startDate
startTime_LTE: $endDate
}
) {
id
title
description
startTime
endTime
locationName
address
Poster {
username
}
}
}

Fetching a Single Discussion with Comments

query GetDiscussionWithComments($discussionId: ID!, $channelUniqueName: String!) {
discussions(where: { id: $discussionId }) {
id
title
body
createdAt
Author {
username
profilePicURL
}
Tags {
text
}
}

getCommentSection(
discussionId: $discussionId
channelUniqueName: $channelUniqueName
) {
id
text
createdAt
CommentAuthor {
... on User {
username
profilePicURL
}
... on ModerationProfile {
displayName
}
}
ChildComments {
id
}
}
}

Fetching User Profile

query GetUser($username: String!) {
users(where: { username: $username }) {
username
displayName
bio
profilePicURL
pronouns
location
createdAt
commentKarma
discussionKarma
}
}

Mutation Examples

Creating a Discussion

mutation CreateDiscussion($input: DiscussionCreateInputWithChannels!) {
createDiscussionWithChannelConnections(input: $input) {
id
title
body
createdAt
DiscussionChannels {
id
channelUniqueName
}
}
}

Variables:

{
"input": {
"discussionCreateInput": {
"title": "My New Discussion",
"body": "This is the content of my discussion."
},
"channelConnections": ["general"]
}
}

Creating a Comment

mutation CreateComment($input: [CommentCreateInput!]!) {
createComments(input: $input) {
comments {
id
text
createdAt
}
}
}

Variables:

{
"input": [{
"text": "This is my comment",
"isRootComment": true,
"DiscussionChannel": {
"connect": {
"where": {
"node": {
"id": "discussion-channel-id"
}
}
}
}
}]
}

Creating an Event

mutation CreateEvent($input: EventCreateInputWithChannels!) {
createEventWithChannelConnections(input: $input) {
id
title
startTime
endTime
EventChannels {
id
channelUniqueName
}
}
}

Upvoting a Discussion

mutation UpvoteDiscussion($discussionChannelId: ID!) {
upvoteDiscussionChannel(discussionChannelId: $discussionChannelId) {
id
weightedVotesCount
}
}

Reporting Content

mutation ReportDiscussion($discussionId: ID!, $channelUniqueName: String!, $reason: String!) {
reportDiscussion(
discussionId: $discussionId
channelUniqueName: $channelUniqueName
reason: $reason
) {
id
issueNumber
title
}
}

Schema Exploration

Key Types

TypeDescription
UserPlatform users
DiscussionForum discussions
DiscussionChannelDiscussion-channel join
EventEvents with dates/locations
EventChannelEvent-channel join
CommentComments on content
ChannelForum channels
IssueModeration issues
ServerConfigServer configuration

Permission-Controlled Mutations

Many mutations require authentication and specific permissions:

MutationRequired Permission
createDiscussion...canCreateDiscussion
createEvent...canCreateEvent
createCommentscanCreateComment
archiveCommentcanHideComment
suspendUsercanSuspendUser

Introspection Query

Get the full schema:

query IntrospectionQuery {
__schema {
types {
name
description
fields {
name
type {
name
}
}
}
}
}

Custom Queries

Site-Wide Discussion List

query GetSiteWideDiscussions($searchInput: String, $limit: Int, $offset: Int) {
getSiteWideDiscussionList(
searchInput: $searchInput
limit: $limit
offset: $offset
) {
id
title
body
createdAt
Author {
username
}
DiscussionChannels {
Channel {
uniqueName
displayName
}
}
}
}

User Contributions

query GetUserContributions($username: String!, $limit: Int, $offset: Int) {
getUserContributions(
username: $username
limit: $limit
offset: $offset
) {
... on Discussion {
id
title
createdAt
}
... on Comment {
id
text
createdAt
}
... on Event {
id
title
startTime
}
}
}
query SearchChannels($searchInput: String) {
getSortedChannels(searchInput: $searchInput) {
uniqueName
displayName
description
channelIconURL
}
}

Error Handling

GraphQL errors include:

  • UNAUTHENTICATED - Not logged in
  • FORBIDDEN - Insufficient permissions
  • NOT_FOUND - Resource doesn't exist
  • BAD_USER_INPUT - Invalid input data

Example error response:

{
"errors": [
{
"message": "You do not have permission to perform this action",
"extensions": {
"code": "FORBIDDEN"
}
}
]
}

Rate Limiting

Currently, there's no rate limiting, but be considerate:

  • Batch related queries when possible
  • Use pagination for large data sets
  • Cache responses client-side

Frontend Integration

Using Apollo Client

import { useQuery, useMutation } from "@vue/apollo-composable";
import { GET_DISCUSSIONS } from "@/graphQLData/discussion/queries";

const { result, loading, error } = useQuery(GET_DISCUSSIONS, {
channelUniqueName: "general",
limit: 10,
});

const discussions = computed(() => result.value?.getDiscussionsInChannel ?? []);

Type Safety

Import generated types:

import type { Discussion, Comment, User } from "@/__generated__/graphql";

Best Practices

Query Optimization

  • Request only needed fields
  • Use pagination for lists
  • Batch related queries

Caching

Apollo Client caches by default:

  • Queries are cached
  • Mutations update the cache
  • Use cache policies appropriately

Error Handling

Always handle errors:

const { error, onError } = useQuery(MY_QUERY);

onError((err) => {
console.error("Query failed:", err);
// Show user-friendly message
});