Paperless-ngx extension for Raycast
Developing a Raycast Plug-In to search my Paperless-instance
Since I have switched to Mac I have been using Raycast as my default application launcher, replacing the stock Spotlight solution. What is great about Raycast is the possibility to extend it with free plug-ins; this makes it easy to e.g. translate text using Deepl directly from within the launcher or view deployment status on Render, and is less disruptive to my workflow than searching the web or using additional 3rd-party applications. Another Spotlight replacement with similar functionality is Alfred.
Raycast offers many extensions I heavily use, and also offers the possibility to create so-called Quicklinks. Creating a quicklink https://www.hellofresh.de/recipes/search?q={Query} allows me to quickly initiate a search for recipes in the HelloFresh database. I have created similar quicklinks to easily search SAP product documentation or support notes. One solution where this approach did not work, however, is my self-hosted instance of the document management solution Paperless-ngx. Paperless is built with Python and Django and allows to search my database of documents from the UI. It hides the search parameters from the URL however, so I could not create a Quicklink to the search results.
The Paperless API can be directly queried however, and returns search results in JSON-format. Since my use case was simple enough, I decided to dive into React and TypeScript (which is what is used to create Raycast extensions) and write my first custom extension!
Getting Started with TypeScript and React
The best place to get started creating extensions for Raycast might be the developer guide on their homepage: A lot of valuable information can be found there, including but not limited to the React Getting Started guide and the TypeScript handbook. Even better though: While Raycast is closed-source, its extensions are not. I therefore took great inspiration from some already existing extensions on how to structure my extension (which was really simple, in the end).
Calling the API
What makes creating Raycast extensions relatively simple is that a lot of features are provided out of the box by the Raycast API. Integrated functionality can be imported to offer configuration features to users (which I needed to allow users to manually specify custom domains and access tokens for their own Paperless instance). These configuration variables are instantiated in a package.json-file and can then easily be consumed as variables in the application scripts:
import { getPreferenceValues } from '@raycast/api'
export interface Preferences {
paperlessURL: string;
apiToken: string;
}
const { paperlessURL }: Preferences = getPreferenceValues();
const { apiToken }: Preferences = getPreferenceValues();
Presenting the Results in the Frontend
To properly read the JSON response from my API calls, I needed to parse the object into its different properties. Luckily, this was rather easy by modeling the response model in a separate TypeScript file as such:
export type paperlessFetchResponse = paperlessResultModel;
export interface paperlessResultModel {
count: number
next?: string
previous?: string
results: paperlessResults[]
}
export interface paperlessResults {
id: number
correspondent?: string
document_type?: string
title: string
...
}
The response model was then used as an interface in my React view to display the search results natively within Raycast:
interface DocListItemProps {
result: paperlessResults
}
export const DocListItem = ({
result,
}: DocListItemProps): JSX.Element => {
return (
<List.Item
title={result.title}
icon={Icon.TextDocument}
actions={
<ActionPanel>
<Action.OpenInBrowser
url={`http://${paperlessURL}/documents/${result.id}`}
title="Open in Browser"
/>
</ActionPanel>
}
/>
)
}
That was more or less all I had to change from the template I had used (npm-search). Now my search queries are instantly populating within Raycast!
Example
Searching the Plug-In for scanned documents concerning my JobRad:
Further ideas / backlog
There is much more that could be added to the extension, like showing and updating correspondents, showing thumbnail previews, directly downloading documents or have a detail view for each document. This could also be published to the Raycast store. For now, it is working for me however.
The extension can be found on Github - to use it yourself simply create an empty extension using the Raycast “Create extension” option and copy the Github contents into the newly created extension folder.