technicalPublish your atomic notes with Sekund

Use Sekund to create your own blog

4 minutes read

Sekund is of course a great tool for sharing content and gathering feedback on your notes in private. Particularly in the context of a PKM system, having a sense of intimacy is important. We may express some doubts about a particular idea, or throw out some crazy idea. The last thing we want is everybody knowing about those things.

But blogs are here to stay, and wouldn’t it be nice to be able to just stay on Obsidian to do that?

You guessed it, that’s how this blog came to be. And here’s a quick breakdown of how it was done.

The Sekund website runs on nextjs, but you can probably adapt the concepts for your framework of choice. The idea is very simple.

Dependencies

In addition to nextjs, Sekund uses Realm at the backend. So that’s a needed dependency:

npm i realm-web

… and in your code:

import * as Realm from 'realm-web';

Logging in

To access your notes, you need to log in.

export async function logIn() { if (process.env.REALM_APP_USER_ID && process.env.REALM_APP_USER_PASSWORD && process.env.REALM_APP_ID) { const creds = Realm.App.Credentials.emailPassword(process.env.REALM_APP_USER_ID, process.env.REALM_APP_USER_PASSWORD); return await new Realm.App(process.env.REALM_APP_ID).logIn(creds); } else { throw new Error("Missing environment"); } }

Don’t worry, this function is called on the server side, so there is no risk of your credentials getting compromised.

This blog is as simple as it can get: there is an index page, and one page per post. Everything is statically generated for maximum runtime performance.

Apart from a post’s content, we do need a tiny bit of metadata to display our list of posts. This information will also come in handy for the social metadata that we need to display nice previews on Twitter and [add your favourite social network here].

FrontMatter to the rescue

Without further ado, here is all we need:

--- category: technical imageUrl: https://sekund-sekund-assets.s3.amazonaws.com/misc/ben-white-W8Qqn1PmQH0-unsplash.jpg description: Use Sekund to create your own blog ---

The index page

For statically generated pages, nextjs relies on a special function called getStaticProps. Here’s what it will look like for our blog’s index page.

export const getStaticProps: GetStaticProps = async () => { const client = await logIn(); const notes: Note[] = await client.functions.callFunction("notesDir", `blog`); const posts = notes.map((note) => { const content = fm(note.content); const { category, imageUrl, description } = content.attributes as any; const readingStats = readingTime(content.body); const minutes = Math.round(readingStats.minutes); return { title: note.title, href: `/blog/${note._id.toString()}/${slugify(noteTitle)}`, category: { name: category, href: "#" }, description, date: "" + new Date(note.created).toDateString(), datetime: new Date(note.created).toISOString().split("T")[0], imageUrl, readingTime: `${minutes} ${minutes > 1 ? "minutes" : "minute"}`, author: { name: client.customData.name, href: "#", imageUrl: client.customData.image, }, } as Post; }); return { props: { posts, }, revalidate: 10, }; };

You will notice that right after logging in, we are calling a function called… callFunction. What it does is calling a function that is defined in Sekund’s backend called notesDir. This function should probably be named dirNotes, but anyway, you probably guessed what it does.

There are three other ancillary functions in this short piece of code: fm, slugify and readingTime, which come with the aptly named frontmatter, slugify and reading-time npm libraries.

The rest is, I think, self-explanatory.

Oh, for those unfamiliar with nextjs, the revalidate field in the function’s return value enables what nextjs calls ‘Incremental Static Regeneration’, or ISR. Read more about it here.

For brevity, I will not include the templating, which really is a matter of personal preferences.

The blog post page

For each blog posts to be statically pre-generated, nextjs needs to know all the paths that we want a blog post for. For that, there is the getStaticPaths function. And here’s how it looks:

export async function getStaticPaths() { const client = await logIn(); const notes: Note[] = await client.functions.callFunction("notesDir", "blog"); const paths = notes.map((note) => { return { params: { id: note._id.toString(), slug: slugify(note.title), }, }; }); return { paths, fallback: true }; }

Similarly to the index page, we now need to generate the props for the post itself:

export const getStaticProps: GetStaticProps = async ({ params }) => { const { id } = (params as unknown) as any; const client = await logIn(); const fullNote = await client.functions.callFunction("getNote", id); const content = fm(fullNote.content); const { category, imageUrl, description } = content.attributes as any; const readingStats = readingTime(content.body); const minutes = Math.round(readingStats.minutes); const title = fullNote.title.replace(".md", ""); const { body } = content; return { props: { imageUrl, category, title, description, body, date: "" + new Date(fullNote.created).toDateString(), datetime: new Date(fullNote.created).toISOString().split("T")[0], readingTime: `${minutes} ${minutes > 1 ? "minutes" : "minute"}`, url: `https://www.sekund.io/blog/${fullNote._id.toString()}/${slugify(title)}`, }, revalidate: 10, }; };

That’s all. I realize that all this would probably require a much more in-depth article, but the idea is that Sekund, while it will never be able to serve as a full-fledged headless CMS, is nevertheless capable to serve some basic needs for content creators.