Creating a Phoenix Elixir Portfolio Blog in 2023
Rebuilding this web site in Phoenix 1.6.5
A while back I had written a new blog with Elixir. For years it ran with no issues and then Heroku did a Salesforce and killed off the free database tier.
Just like that I lost my Postgres database! I must have missed the notifications somehow from Heroku but apparently for months my entire site was down.
My roommate had this great idea to check the WayBack Machine and low and behold my entire site was archived! I quickly downloaded the whole site from Wayback Machine.
Phoenix 1.6.5 or Phoenix 1.7.0?
Instead of upgrading my previous code base, I wanted to see what the new Phoenix hotness was about and boy what a mess!
I’ve found for small projects upgrading sometimes isn’t with the hassle and my old site really only had 3 schemas and limited complexity.
I love Phoenix and Elixir but sometimes things change so much. Because their focus has been LiveView in Phoenix 1.7, they moved to a whole new rendering model. Instead of using Phoenix.View they use components. While that’s all fine and dandy this is the 3rd major change in Phoenix in as many years. They’ve also moved to esbuild which don’t get me wrong is amazing but not without it's issues as well. Even in their 1.7 docs they say large sites should still use 1.6.5 for compatibility. So much for the bleeding edge!
Bootstrap 5 FTW!
So I started building again, in Phoenix 1.6.5. I chose to use Bootstrap 5 because while Tailwind CSS is interesting I find it too slow to get things out the door. I rather have well designed components with a smattering of utilities. Plus your HTML looks like class soup with Tailwind.
Yes Heroku For Deployment Still
While Heroku’s changes with Salesforce are eye opening, it’s still the easiest way to deploy a Phoenix or Rails app.
I experimented with Fly.io and so much of their marketing makes it sound so easy but they are still a long ways behind Heroku in terms of ease of use and dependability.
Most of Heroku still just works. It just costs a bit more now. I’m spending about $12 a month for the site (where it used to be free). Still very affordable.
The new default auth library is fantastic! So east to set up and integrate. Sensible defaults with great tests and very easy to read code. If you are still learning Elixir, it’s a great way to see some excellent code. I had auth running in about 30 minutes.
The basic schema of a portfolio is the project. I’ve outlined the mix command I used below to generate the scaffolding. Scaffolding makes it very easy to get it up and running and provides tests you can further enhance.
mix phx.gen.html Content Project projects title:string subtitle:string slug:string link:string content:text featured:boolean published:boolean published_at:date
The Content Context.
I now love contexts and use this extensively in non Phoenix apps too. Grouping together domain logic and keeping code clean and separated is so easy with Contexts. I planned for both Articles and Projects to be in the Content context as they are essentially very similar.
Image uploading is still a mess in Elixir. The waffle library is out of date and while it works, it takes way too much work to setup.
I wish instead of focusing on LiveView which don’t get me wrong I love, I would love to see more Phoenix support for things like Uploads, Rich text and core features that almost every site needs like you see with ActiveStorage and ActiveText in Rails.
So I just use Cloudinary for image uploading. I only need a single string field on my schemas per image and I get a world class CDN and excellent image transformations. Using the cloudinary Elixir repo plus an ImageHelper module makes short work of images and there is no image processing done at all in the Phoenix app. All via cloudinary. So easy and fast!
Static Markdown Plus Postgres?
I used GitHub pages to make a static blog in Jekyll years ago and I found it really inconvenient to add new content. Creating content by git commit isn’t very user friendly.
But since I lost all my data from Postgres, I thought is there a way I can do both?
Redundancy in content creation. If you think about it, as a developer your portfolio is extremely valuable.
Can I have a single point of storage for my portfolio while still getting the benefits of static markdown pages in case the DB goes pop again?
Markdown + Front Matter
I made a series of markdown files for both projects and articles. It takes YAML front matter for the schema data plus a content section for markdown. It makes it really to easy have a git committed back up of all my writing.
--- title: "HireClub" slug: "hireclub" subtitle: "Find your dream job." skills: "Design, Development, Rails, Product" categories: "Web" published_at: "2018-03-01" published: "true" featured: "true" link: "https://hireclub.com" --- Markdown content here
Importing Markdown Files
I wrote an importer module that takes all the markdown files and imports them into the database which I call in my seeds.exs file.
I also wrote a markdown view that generates markdown from the postgres data.
The next step is to write a task that periodically scans for new projects and articles and commits them to git. I’m doing that part manually for now.
ESBuild + SCSS
Rails asset pipeline people! For non SPA apps it’s just great.
Having written full text search in Elixir before I didn’t feel like doing it again. In another project I incorporated Algolia that make search a breeze. It’s a much better search technology than I could ever write on my own and it costs pennies. I wrote a search controller to show the results from Algolia and its been smooth sailing!
With my AlgoliaService class anytime a project or article is updated, the Algolia index is updated.
I can control how search works from Algolia's amazing web UI. Multiple model search is super easy to integrate as well. I can search across multiple fields with ranking and stemming and typo settings. Honestly it’s amazing and fast.
Just use Algolia for search and don’t try to build your own!
There's a lot more features I added that I'll be discussing in a series of upcoming posts!
7 Likes 1262 words by Ketan Anjaria