Home - ajsb85/penpot-api-client GitHub Wiki
This document outlines a professional-grade TypeScript client for the Penpot API. The library is designed to provide a robust, maintainable, and ergonomic interface for developers interacting with a Penpot instance. It is environment-agnostic, leveraging native platform APIs to ensure compatibility with Node.js, Deno, and modern browser environments without third-party runtime dependencies.
Features
Library Architecture & Design
- Fluent API Design: The client employs a fluent, instantiated interface. After initializing the main
PenpotClient
once, all API command modules are accessed as properties on the client instance (e.g.,client.teams.list()
,client.files.get()
). This design promotes cleaner, more readable code and eliminates the need to pass the client instance to every function call. - Unified Real-time Client: A single
RealtimeClient
offers support for both WebSocket (/api/ws
) and Server-Sent Events (/api/sse
) protocols. This allows developers to choose the optimal transport for their environment while using a consistent, event-driven API (realtimeClient.on(...)
) for live data synchronization. - Strict Type Safety: Developed entirely in TypeScript, the library ensures strong type safety for all API requests, responses, and real-time event payloads. A comprehensive
RealtimeEventMap
links event names to their specific data interfaces, enabling editor autocompletion and preventing type-related runtime errors. - Structured Error Handling: The client throws custom, typed errors to distinguish between different failure modes. Developers can programmatically handle specific conditions by catching
ApiHttpError
for network status issues orApiRpcError
for API-level rejections. - Semantic Constants: To enhance code clarity and prevent common errors, the library exports constant objects for known API values, such as
RealtimeEvents
,ApiErrorCodes
, andObjectTypes
.
Backend Technology Stack
The Penpot backend, which this library communicates with, is built on a robust and scalable technology stack:
- Primary Language: Clojure, a dynamic, functional dialect of Lisp that runs on the Java Virtual Machine (JVM).
- Database: PostgreSQL is used for primary data persistence.
- Real-time Communication: The backend manages real-time collaboration through a publish-subscribe (pub/sub) message bus, exposing events to clients via WebSockets and Server-Sent Events.
- API Format: The API uses a custom RPC (Remote Procedure Call) system over HTTP, with payloads serialized in a Clojure-compatible, keyword-based JSON format (Transit-JSON).
API Command Coverage
The library provides comprehensive coverage of the Penpot RPC API. The following table summarizes the available commands, grouped by module.
Module | Method | Description |
---|---|---|
auth |
login() |
Authenticates a user with email and password. |
loginWithProvider() |
Authenticates a user via an external provider (LDAP, SAML, etc.). | |
register() |
Registers a new user on the instance. | |
users |
get() |
Retrieves the profile of the currently authenticated user. |
getById() |
Retrieves the public profile of a specific user by their ID. | |
update() |
Updates the current user's profile information. | |
updatePhoto() |
Uploads and sets a new profile photo for a user. | |
teams |
get() |
Retrieves the details of a specific team by its ID. |
list() |
Retrieves all teams the current user is a member of. | |
create() |
Creates a new team. | |
update() |
Updates the name of an existing team. | |
delete() |
Deletes a team. | |
listMembers() |
Lists all members of a given team. | |
projects |
get() |
Retrieves a specific project (folder) by its ID. |
list() |
Retrieves all projects for a given team. | |
create() |
Creates a new project (folder). | |
delete() |
Deletes a project and all its files. | |
files |
get() |
Retrieves the metadata for a specific file. |
create() |
Creates a new file in a given team/project. | |
update() |
Updates a file's metadata, such as its name. | |
delete() |
Deletes a file. | |
duplicate() |
Creates a copy of an existing file. | |
fileContent |
getPage() |
Retrieves the full data for a specific page within a file. |
listSnapshots() |
Lists all named versions (snapshots) of a file. | |
createSnapshot() |
Creates a new named version (snapshot) of a file. | |
import() |
Imports a .penpot binary file. |
|
assets |
upload() |
Uploads a media asset (e.g., an image) to a specific file. |
createFromUrl() |
Imports a media asset into a file from a public URL. | |
comments |
listThreads() |
Retrieves all comment threads for a given file. |
create() |
Adds a new comment or creates a new thread. | |
update() |
Edits the text of an existing comment. | |
webhooks |
list() |
Retrieves all webhooks configured for a team. |
create() |
Creates a new webhook for a team. | |
update() |
Updates an existing webhook. | |
delete() |
Deletes a webhook. |
Real-time Events
The RealtimeClient
allows you to subscribe to the following server-side events. Use the exported RealtimeEvents
constant for type-safe subscriptions.
Constant | Event Name | Description |
---|---|---|
RealtimeEvents.FileChange |
:file-change |
Fired when any data within a file is modified. |
RealtimeEvents.CommentAdded |
:comment-added |
Triggered when a new comment is added to a thread. |
RealtimeEvents.CommentUpdated |
:comment-updated |
Fired when the text content of an existing comment is edited. |
RealtimeEvents.CommentDeleted |
:comment-deleted |
Fired when a comment is deleted. |
RealtimeEvents.CommentThreadUpdated |
:comment-thread-updated |
Triggered when a thread's metadata (status, position) changes. |
RealtimeEvents.UserNotification |
:user-notification |
A generic event for user-specific notifications (e.g., mentions). |
RealtimeEvents.FileLibraryUpdated |
:file-library-updated |
Fired when a shared library used by the current file has been updated. |
RealtimeEvents.UserJoinedFile |
:user-joined-file |
Triggered when a new user joins a real-time file session. |
RealtimeEvents.UserLeftFile |
:user-left-file |
Triggered when a user leaves a real-time file session. |
RealtimeEvents.UserActivity |
:user-activity |
Broadcasts a user's cursor position and selection to other collaborators. |
RealtimeEvents.TeamUpdated |
:team-updated |
Fired when a team's details (like its name) are changed. |
RealtimeEvents.TeamMemberAdded |
:team-member-added |
Triggered when a new member is added to a team you are part of. |
RealtimeEvents.TeamMemberRemoved |
:team-member-removed |
Triggered when a member is removed from a team you are part of. |
Installation and Usage
To install the library, use a standard package manager:
npm install penpot-api-client
Before use, a Personal Access Token must be generated from your Penpot instance.
Client Initialization
import { PenpotClient, ApiRpcError, ApiErrorCodes } from 'penpot-api-client';
const client = new PenpotClient({
baseUrl: '[https://design.penpot.app](https://design.penpot.app)', // Your instance URL
accessToken: 'YOUR_PERSONAL_ACCESS_TOKEN',
});
async function getUserProfile() {
try {
const profile = await client.users.get();
console.log(`Successfully authenticated as: ${profile.fullName}`);
} catch (error) {
if (error instanceof ApiRpcError && error.code === ApiErrorCodes.Unauthorized) {
console.error('Authentication failed. Please verify your access token.');
} else {
console.error('An unexpected error occurred:', error);
}
}
}
getUserProfile();