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:
- Log in to the frontend
- Open browser dev tools → Application → Local Storage
- Copy the access token
- 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
| Type | Description |
|---|---|
User | Platform users |
Discussion | Forum discussions |
DiscussionChannel | Discussion-channel join |
Event | Events with dates/locations |
EventChannel | Event-channel join |
Comment | Comments on content |
Channel | Forum channels |
Issue | Moderation issues |
ServerConfig | Server configuration |
Permission-Controlled Mutations
Many mutations require authentication and specific permissions:
| Mutation | Required Permission |
|---|---|
createDiscussion... | canCreateDiscussion |
createEvent... | canCreateEvent |
createComments | canCreateComment |
archiveComment | canHideComment |
suspendUser | canSuspendUser |
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
}
}
}
Channel Search
query SearchChannels($searchInput: String) {
getSortedChannels(searchInput: $searchInput) {
uniqueName
displayName
description
channelIconURL
}
}
Error Handling
GraphQL errors include:
UNAUTHENTICATED- Not logged inFORBIDDEN- Insufficient permissionsNOT_FOUND- Resource doesn't existBAD_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
});