feat: initial implementation of backlinks extension

This commit is contained in:
hermes
2026-04-23 13:34:59 +00:00
commit 23bfd2e2e2
4 changed files with 130 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
# Obsidian Local REST API — Backlinks Extension
This is an extension for the [Obsidian Local REST API](https://github.com/coddingtonbear/obsidian-local-rest-api) plugin. It adds a new endpoint to retrieve backlinks for a specific note.
## Features
- **GET `/extensions/backlinks/{filepath}`**: Returns all notes that link to the specified file.
## Installation
1. Install the [Obsidian Local REST API](https://github.com/coddingtonbear/obsidian-local-rest-api) plugin.
2. Download the `main.js` and `manifest.json` from the `/dist` folder of this repository.
3. Create a folder named `obsidian-local-rest-api-backlinks` in your vault's `.obsidian/plugins/` directory.
4. Place `main.js` and `manifest.json` into that folder.
5. Restart Obsidian or reload plugins.
6. Enable the plugin in **Settings > Community Plugins**.
## Usage Example
```bash
curl -k -H "Authorization: Bearer YOUR_API_KEY" \
"https://127.0.0.1:PORT/extensions/backlinks/MyNote.md"
```
## Development
```bash
npm install
npm run build
```
+9
View File
@@ -0,0 +1,9 @@
{
"id": "obsidian-local-rest-api-backlinks",
"name": "Local REST API — Backlinks Extension",
"version": "1.0.0",
"minAppVersion": "1.0.0",
"description": "Adds a /extensions/backlinks/ route to Obsidian Local REST API to retrieve backlinks for a specific note.",
"author": "Hermes",
"isDesktopOnly": false
}
+18
View File
@@ -0,0 +1,18 @@
{
"name": "obsidian-local-rest-api-backlinks",
"version": "1.0.0",
"description": "Obsidian Local REST API Backlinks Extension",
"main": "main.js",
"types": "main.d.ts",
"dependencies": {
"obsidian": "npm:obsidian@^1.0.0",
"obsidian-local-rest-api": "npm:obsidian-local-rest-api@^2.5.0"
},
"devDependencies": {
"typescript": "^5.0.0",
"esbuild": "^0.19.0"
},
"scripts": {
"build": "esbuild src/main.ts --bundle --outfile=dist/main.js --external:obsidian --platform=node --target=es2020"
}
}
+77
View File
@@ -0,0 +1,77 @@
import { Plugin, TFile, normalizePath } from "obsidian";
import {
LocalRestApiPublicApi,
Route,
Request,
Response,
} from "obsidian-local-rest-api";
interface LinkMetadata {
displayText: string;
link: string;
original: string;
position: { start: unknown; end: unknown };
}
interface BacklinksData {
data: Record<string, LinkMetadata[]>;
}
export default class BacklinksExtension extends Plugin {
async onload() {
console.log("Loading Local REST API Backlinks Extension...");
const api = (this.app as any).plugins.plugins[
"obsidian-local-rest-api"
]?.api as LocalRestApiPublicApi | undefined;
if (!api) {
console.error("Local REST API plugin not found. Please ensure it is installed and enabled.");
return;
}
const routes: Route[] = [
{
path: "/extensions/backlinks/{*}",
method: "get",
handler: async (req: Request, res: Response) => {
const filePath = req.params["*"] ?? "";
const path = normalizePath(filePath);
const file = this.app.vault.getAbstractFileByPath(path);
if (!file) {
return res.status(404).json({
errorCode: 40,
message: `File not found at path: ${filePath}`,
});
}
if (!(file instanceof TFile)) {
return res.status(400).json({
errorCode: 405,
message: `Path is a folder, not a file: ${path}`,
});
}
const backlinks: BacklinksData =
(this.app as any).metadataCache.getBacklinksForFile(file);
const result: Record<string, LinkMetadata[]> = {};
if (backlinks && backlinks.data) {
for (const [sourcePath, links] of Object.entries(backlinks.data)) {
result[sourcePath] = links;
}
}
return res.json({
file: path,
backlinks: result,
count: Object.keys(result).length,
});
},
},
];
api.registerRoutes(routes);
console.log("Backlinks route registered: GET /extensions/backlinks/{*}");
}
}