Home Hello World

Last updated: 2020-12-15

How do I make one of these?

Why'd you make one of these?

I think there are too many apps and tools, all of which accomplish slightly overlapping tasks with no interoperability between them.

I'm at the point in my career where it makes more sense to shapen the tools in my toolbox instead of focusing on adding more. When I set out on starting my own blog, I wanted to engage is a manner that would add value in this direction (See: Axioms of Development).

Why not just use one of (<medium> <wordpress> <blogger> <wix> <ghost> <tumblr> <etc>)?

Because I want something that will fit within my workflow with minimal deviations. I don't need the ability to write posts on my phone. I don't want a fancy UI that slows down the user experience. I don't want to write in a special box and worry about my internet connection. I want control over what happens and how.

Show me how!

This website is powered by the magic of vimwiki + fastapi.

Vimwiki is a wonderful tool that integrates into my existing workflow. Love the ability to jump into and out of a link without having to use my mouse while typing. Lets me use all my vim shortcuts while editing and keeps the mental overload of using tools to a minimal. I can also write these offline and push changes when i'm ready. I don't really care to write this stuff on my phone. I'm sure other people find value in it, but how often can you really write good content on your phone that you just have to publish immediately? Just use Twitter for that idea and move on with life.

Fastapi because I wanted a python webserver that's easy to use and performant.

Steps to reproduce:

  1. Get VIM (optional)
  2. Install vimwiki (optional)
  3. Configure your html generation to use your template + output to your git managed folder.
  4. Create your git project where you'll host the webserver and the generated html pages.
  5. Create your webserver (example app.py shown below) and spin it up.
  6. Once you have the flow down, move your code to a server that can run 24/7.
    • I pay ~$2/mo for the cheapest EC2 instance.
  7. Setup git hooks so you can auto re-deploy on new commits.
  8. Start writing!

But what about a database? Don't need one. The content is processed into raw html. No need to keep any persistant state.

But what if I want to save information on how many people are reading my posts? Track it all in memory and periodically write to a file. Do you really care if you occasionally lose 5 minutes worth of information? When you start up, load it back into memory.

  1. Open up vim.
  2. Hit <leader>+w+w (opens vimwiki)
  3. Write.
  4. Hit <leader>+w+h (triggers shortcut to export to HTML using my custom template).
  5. Commit.
  6. Go to #1 or #2.


let blog = {}
let blog.path = '~/blog/'
let blog.path_html = '/path/to/git/templates/'
let blog.template_path = '/path/to/git/vimwiki/'
let blog.template_default = 'theme'
let blog.template_ext = '.html'
let blog.nested_syntaxes = {'python': 'python', 'c++': 'cpp'}

let g:vimwiki_list = [blog]


<!DOCTYPE html>
        <link rel="Stylesheet" type="text/css" href="/static/css/template.css">
        <link rel="Stylesheet" type="text/css" href="/static/css/style.css">


        <meta http-equiv="Content-Type" content="text/html; charset=%encoding%">
    <body style="margin: 0px">
        <div class="topnav">
            <a style="font-style: normal; text-transform: capitalize;" class="active" href="/">Home</a>
            <a style="font-style: normal; text-transform: capitalize;" href="/%title%">%title%</a>
        <div class="container">
            <p style="text-align: right; font-weight: bold;">Last updated: %date%</p>


from fastapi import (

from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import RedirectResponse

app = FastAPI(docs_url=None, redoc_url=None)
app.mount("/static", StaticFiles(directory="blog/static"), name="static")

templates = Jinja2Templates(directory="blog/templates")

async def favicon():
    return RedirectResponse('static/favicon.ico')

async def home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

async def get_entry(entry: str, request: Request):
    # You probably want to sanitize inputs here
    return templates.TemplateResponse(f'{entry}', {"request": request})