Related
- [[unjs_unstorage]]
Background
Continuing from the previous three articles, we believe that xlog's ambition should not be limited to the current web-based blog. It should be a better headlessCMS. In order to publish articles more smoothly, we developed the "sync-to-xlog" plugin to make the synchronization process easier.
If we continue to explore, can I use Nuxt to create my own blog with xlog? Following this idea, I realized that the current solution for accessing xlog can be simplified using "unjs/unstorage" and also add some challenges.
A brief introduction to unstorage, it can be understood as a universal plugin that provides a unified interface. By using different driver adapters, it can smooth out the differences in details.
The usage is actually very simple, similar to using localstorage.
import { createStorage } from "unstorage";
const storage = createStorage(/* opts */);
await storage.getItem("foo:bar");
await storage.hasItem()
// getItem getItems
// getItemRaw
// setItem setItems setItemRaw
// getMeta setMeta removeMeta
// getKeys
Technical Design
It doesn't seem complicated.
First, customize the xLogDriver
.
Implement the corresponding APIs:
- Define getItem('site')
- Define getKeys() - returns a list
- Define getItem('slugOrID')
const storage = createStorage({
driver: xLogStorageDriver({
token: "", // optional
charactorID: "", // required
}),
});
// console.log("list", list);
const list = await storage.getKesy();
console.log("list", list);
Code Implementation
It seems not difficult, I will implement it later. I will update here if there is any progress.
This time we won't use the RESTful API request, we will use the crossbell.js
SDK to complete all operations.
I actually wrote it, but it didn't feel right, mainly because the return value of getKeys
can only be string[]
, so I gave up. I'll come back to it after a few major versions.
It seems that implementing only getItems
is also possible?
After hesitating for a while, I still implemented it. The source code is open source here: https://github.com/Otto-J/unstorage-xlog-driver
You can use it like this:
/* eslint-disable unicorn/prefer-top-level-await */
import { createStorage } from "unstorage";
import { xLogStorageDriver } from "unstorage-xlog-driver";
const OTTO_ID = 53_709;
async function main() {
const storage = createStorage({
driver: xLogStorageDriver({
characterId: OTTO_ID,
ttl: 60 * 60,
}),
});
// const keys = await storage.getKeys();
// console.log(keys);
const info = await storage.getItem("72.md");
// const info = await storage.getMeta("72.md");
console.log(info);
}
main();
Pitfall Experience
Not all APIs need to be implemented
At first, I didn't have a clear understanding of unstorage
. I thought all functions needed to be implemented, but later I found that only a few main methods need to be implemented.
What is the appropriate return value for getKeys?
getKeys
must return string[]
. Through practice, I found that it is better to return ${noteID}.md
here, because it can be configured like this in the nuxt-content module, generating virtual files that are similar to local files, making it more consistent.
export default defineNuxtConfig({
content: {
sources: {
driver: path.resolve(
__dirname,
"node_modules/unstorage-xlog-driver/dist/index.mjs",
),
characterId: 53709,
prefix: "/blogs",
}
}
})
With this magical sources configuration, remote data can be pulled to the local .nuxt/content-cache/parsed/xLog/blogs
directory, and you can see these theoretically virtual files.
I have a good screenshot here, but after thinking about it, I decided not to include it. It's just a bunch of garbled characters and not aesthetically pleasing.
How to design getItem properly
getItem('72.md')
only returns the markdown plain text. In this way, I lose some information, such as slug/update_time, etc.
I came up with a solution that suits me well, which is to intercept the plain text, put the information I want to fill in the frontmatter first.
To understand how, please refer to the code here: https://github.com/Otto-J/unstorage-xlog-driver/blob/main/src/index.ts#L124
I first parse the front-matter, merge it with the original markdown, and then return it. This way, the remote data remains unchanged, and I can handle it myself.
The cache that must be added
When I first logged, I was surprised to find that it triggered countless requests, which was very strange. I went to see the implementation of the github-driver and found that I can add my own cache. I maintained an object mapping table to cache the data. Within a certain period of time, I don't need to request the remote data again.
It triggered countless times, like an attack, it's a bit funny and sad.
Using unjs's unbuild template
Originally, I wanted to use tsup to create a pipeline, but then I found that unjs has an unbuild project that provides templates. It comes with vitest/jiti and other things, which is quite convenient to use.
Template address: https://github.com/unjs/template
I also wrote some vitest unit tests.
If I could start over
If I could start over, I wouldn't hesitate so much. I hesitated for two weeks whether to write it or not. I wrote it at first, but then I decided to give up and deleted the code. Although there wasn't much code, once deleted, it's gone. I can only rewrite it, so I still respect my own code. I really don't want to keep it, I can put the code in the blog.
Outlook
So far, we have implemented the unStorage xLogDriver, and no matter what rendering environment I use in the future, I can use this method.