Overview
Pagemark API is a lightweight bookmark management service built with Flask. It provides a structured REST API for saving URLs, organizing them with tags and collections, and performing full-text searches across your saved content.
The Digital Filing Cabinet
Managing a growing list of bookmarks often leads to "link rot" and difficulty finding specific resources. Pagemark solves this by providing a centralized, searchable, and categorizable API that can be integrated into custom dashboards, browser extensions, or personal knowledge management tools.
Core Concepts
- Bookmark: The primary entity representing a saved URL. It includes a title, description, and metadata, along with a status (active, archived, or trashed).
- Tag: Flexible, color-coded labels that can be attached to any number of bookmarks for cross-cutting organization.
- Collection: Named groups used to cluster related bookmarks into a single logical container.
- Service Layer: The
BookmarkServiceacts as a single point of entry for business logic, ensuring that operations like "delete tag" correctly clean up references across all bookmarks. - In-Memory Storage: By default, the system uses an in-memory repository and search index, making it extremely fast but ephemeral.
How It Works
The application follows a strict layered architecture to separate concerns:
- Routes: Flask blueprints (e.g.,
app.routes.bookmarks) handle HTTP serialization and status codes. - Orchestration: The
BookmarkServicevalidates incoming data and coordinates between the repository, search index, and cache. - Search Index: An inverted index (
SearchIndex) provides full-text search capabilities by tokenizing titles and descriptions. - Caching: An
LRUCachestores frequently accessed bookmarks to minimize repository lookups. - Repository: The
BookmarkRepositoryabstracts data access, allowing the underlying storage to be swapped without changing business logic.
Use Cases
Save and Tag a Bookmark
Create a new entry with metadata and associate it with existing tags.
from app.services.bookmark_service import BookmarkService
service = BookmarkService()
bookmark, error = service.create_bookmark({
"url": "https://flask.palletsprojects.com/",
"title": "Flask Documentation",
"description": "The official documentation for the Flask web framework.",
"tags": ["dev-tools", "python"]
})
if not error:
print(f"Saved: {bookmark.id}")
Full-Text Search
Find bookmarks based on keywords in their title or description.
from app.services.bookmark_service import BookmarkService
service = BookmarkService()
# Returns a list of Bookmark objects matching 'python'
results = service.search(query="python", limit=5)
for b in results:
print(f"{b.title}: {b.url}")
Organize into Collections
Group related bookmarks for project-based organization.
from app.services.bookmark_service import BookmarkService
service = BookmarkService()
# Create a collection and add a bookmark to it
collection, _ = service.create_collection({"name": "Reading List"})
success = service.add_to_collection(collection.id, "bookmark_id_123")
Expectations & Compatibility
When to use
- Prototyping: Ideal for building front-end bookmarking tools without setting up a heavy database.
- Personal Tools: Perfect for small-scale, local-first applications where speed is a priority.
- Educational Reference: A clean example of how to implement the Service and Repository patterns in Flask.
When not to use
- Persistent Storage: Since the default repository is in-memory, all data is lost when the server restarts.
- Large Datasets: The search index and repository are not optimized for millions of records.
- High Concurrency: The in-memory implementation does not include row-level locking or transaction support.
Stack Compatibility
- Language: Python 3.8+
- Framework: Flask 3.0+
- Dependencies:
python-dotenvfor configuration management. - Deployment: Can be run as a standard WSGI application (e.g., with Gunicorn).
Getting Started Pointers
- Explore the API Reference to see the full REST specification.
- Check
app/config.pyto switch betweenDevelopmentConfigandProductionConfig. - Review
app/models/bookmark.pyto understand the data structure of a bookmark.
Limitations & Assumptions
- No Authentication: The current version does not include user authentication or multi-tenancy; all bookmarks are shared.
- Soft Deletion: Deleting a bookmark via the API moves it to the
trashedstatus rather than removing it from memory immediately. - Manual Indexing: The search index is updated incrementally on writes but must be rebuilt if the underlying repository is modified externally.
FAQ
Does it support nested collections? No, collections are currently flat. You can group bookmarks into a collection, but you cannot put a collection inside another collection.
How do I persist my data?
You would need to implement a new version of BookmarkRepository (e.g., using SQLAlchemy or Motor) and inject it into the BookmarkService.
Is the search case-sensitive?
No, the SearchIndex tokenizes and searches in lowercase to ensure case-insensitive matching.
Can I add custom metadata to bookmarks?
Yes, the Bookmark model includes a metadata dictionary field that accepts arbitrary key-value pairs.
What happens if I delete a tag that is in use?
The BookmarkService.delete_tag method automatically removes that tag ID from all bookmarks before deleting the tag itself.