Skip to main content
ArcHive provides powerful organization features to help you keep your digital knowledge base structured and easily accessible.

Content Structure

Each content item in ArcHive follows a well-defined schema:
export interface IContentItem extends Document {
  userId: Types.ObjectId;
  type: ContentType;           // "text" | "link" | "code"
  title?: string;              // Up to 200 characters
  description?: string;        // Up to 1,000 characters
  content: string;             // Up to 100,000 characters
  url?: string;                // Valid URL for links
  tags: string[];              // Array of tag strings
  previewImageUrl?: string;    // Cloudinary URL for screenshots
  platform?: string | null;    // Platform identifier
  createdAt: Date;
  updatedAt: Date;
}
Source: backend/src/db/models/ContentItem.ts:9-21

Tagging System

Tags are a powerful way to categorize and find your content:

Automatic Tag Normalization

All tags are automatically normalized for consistency:
tags: {
  type: [String],
  default: [],
  set: (v: string[]) =>
    v
      .map((tag) => tag.trim().toLowerCase())
      .filter((tag) => tag.length > 0),
}
Source: backend/src/db/models/ContentItem.ts:56-63
Tags are automatically converted to lowercase and trimmed, ensuring “JavaScript”, “javascript”, and ” JavaScript ” are all treated as the same tag.

Manual Tagging

You can add custom tags when creating or editing content:
1

During Content Creation

Add tags when initially saving content:
{
  "type": "link",
  "url": "https://example.com",
  "tags": ["tutorial", "javascript", "web-dev"]
}
2

Edit Existing Content

Update tags on existing items via the PUT endpoint:
await updateContent(userId, contentId, {
  tags: ["updated", "tags", "list"]
});

Automatic Tag Suggestions

For links, ArcHive automatically generates tag suggestions by analyzing the page content using NLP. These suggestions are processed asynchronously and appear after the initial save.

Content Types

Organize content by type for focused browsing:

Type Filtering in the Mobile App

The mobile app provides visual filters for content types:
const contentTypes = ["All", "Link", "Text", "Code"];

// Filter implementation
findCriteria.type = type;
UI Flow:
  1. Tap the search icon in the header
  2. Filter buttons appear below the search bar
  3. Select a type to filter (All, Link, Text, Code)
  4. Content list updates in real-time
Source: archive/app/(tabs)/index.tsx:40-160

Links

Web URLs with metadata and screenshots

Text

Notes, thoughts, and written content

Code

Code snippets and examples

Updating Content

You can update any aspect of your saved content:
contentRoutes.put(
  "/:id",
  apiRateLimiter,
  validate("json", updateContentSchema),
  async (c) => {
    const userId = c.get("user")?._id;
    const contentId = c.req.param("id");
    const updates = c.req.valid("json") as UpdateContentInput;

    const updatedContent = await updateContent(userId, contentId, updates);
    return c.json({
      message: "Content item updated successfully!",
      content: updatedContent.toObject(),
    });
  },
);
Source: backend/src/routes/content.ts:186-211

Update Validation

Content type cannot be changed after creation. Attempting to change a link to text or vice versa will result in a validation error.
if (existing && existing.type !== updates.type) {
  throw new ValidationError(
    `Content type cannot be changed. Expected ${existing.type}, got ${updates.type}.`,
  );
}
Source: backend/src/services/content.service.ts:216-220

Updatable Fields

  • title: Change the title (max 200 characters)
  • description: Update the description (max 1,000 characters)
  • tags: Modify the tag list
  • content: Edit text or code content (max 100,000 characters)
  • url: Update the URL (links only)
  • previewImageUrl: Change the preview image
  • platform: Modify platform categorization

Deleting Content

Remove content you no longer need:
contentRoutes.delete("/:id", apiRateLimiter, async (c) => {
  const userId = c.get("user")?._id;
  const contentId = c.req.param("id");

  await deleteContent(userId, contentId);
  return c.json(
    {
      message: "Content item deleted successfully!",
    },
    200,
  );
});
Source: backend/src/routes/content.ts:214-232
Deletion only works for content you own. Attempting to delete another user’s content will result in a 404 error.

Full-Text Search Index

ArcHive maintains a full-text search index for fast retrieval:
ContentItemSchema.index(
  {
    title: "text",
    description: "text",
    content: "text",
    url: "text",
    tags: "text",
  },
  {
    name: "ContentItemTextIndex",
    weights: {
      title: 10,
      tags: 5,
      description: 3,
      content: 1,
      url: 1,
    },
  },
);
Source: backend/src/db/models/ContentItem.ts:80-98
The search index is weighted, meaning matches in titles (weight: 10) are ranked higher than matches in content (weight: 1).

Platform Index

For efficient platform-based browsing, ArcHive maintains a compound index:
ContentItemSchema.index({ userId: 1, platform: 1 });
Source: backend/src/db/models/ContentItem.ts:100 This enables fast queries like “Show me all my GitHub repositories” or “Find my YouTube videos.”

Pagination

All content queries support pagination to handle large collections:
const skip = (page - 1) * limit;

let contentsQuery = ContentItem.find(findCriteria)
  .sort({ createdAt: -1 })
  .skip(skip)
  .limit(limit);
Source: backend/src/services/content.service.ts:165-170 Default Pagination:
  • Default page size: 20 items
  • Maximum page size: 100 items
  • Sort order: Most recent first (createdAt descending)

Mobile App Organization Features

Infinite Scroll

The mobile app uses TanStack Query for optimized infinite scrolling:
const {
  data,
  fetchNextPage,
  hasNextPage,
  isFetchingNextPage,
  refetch,
  isRefetching,
} = useInfiniteQuery({
  queryKey: ["contents", debouncedSearchQuery, selectedContentType],
  queryFn: async ({ pageParam = 1 }) => {
    const response = await getContent(
      debouncedSearchQuery,
      pageParam,
      10,
      selectedContentType === "All" ? undefined : selectedContentType.toLowerCase(),
    );
    return response;
  },
  getNextPageParam: (lastPage) => {
    if (lastPage.meta.page < lastPage.meta.totalPages) {
      return lastPage.meta.page + 1;
    }
    return undefined;
  },
  initialPageParam: 1,
});
Source: archive/app/(tabs)/index.tsx:52-82

Pull to Refresh

All content lists support pull-to-refresh for manual updates:
<ContentList
  contentItems={content || []}
  onRefresh={refetch}
  refreshing={isRefetching}
  // ...
/>

Next Steps

Search

Learn about powerful search capabilities

Platform Categorization

Browse content by platform

Build docs developers (and LLMs) love