Compare commits

..

10 Commits

10 changed files with 141 additions and 6 deletions

View File

@ -10,7 +10,5 @@ RUN bun install --frozen-lockfile
COPY ./src ./src COPY ./src ./src
COPY ./posts ./posts COPY ./posts ./posts
EXPOSE 3000
USER bun USER bun
CMD [ "bun", "src/index.tsx" ] CMD [ "bun", "src/index.tsx" ]

View File

@ -1,14 +1,14 @@
--- ---
title: Adventures with Bun and Hono title: Adventures with Bun and Hono
description: A blog post wherein I talk about building this blog with Bun and Hono. description: A blog post wherein I talk about building this blog with Bun and Hono.
date: 2024-08-01 date: 2024-08-11
tags: tags:
- development - development
- javascript - javascript
- typescript
- bun - bun
- hono - hono
slug: fullstack-hono slug: fullstack-hono
draft: true
--- ---
## Context and My Blog ## Context and My Blog
@ -223,4 +223,26 @@ sudo certbot revoke --cert-name davesmithhayes.com
At this time Let's Encrypt had rate-limited my many, many failed attempts to get a new certificate for my own domain. Time to wait it out. An hour to be exact. At this time Let's Encrypt had rate-limited my many, many failed attempts to get a new certificate for my own domain. Time to wait it out. An hour to be exact.
So, instead of actually waiting an hour I went to bed and waited for a few days to get back to work. I knew I had previously deployed a really basic Node.js application with Dokku, hence why I decided to keep using it as a platform to deploy applications on.
I realized that application was running, by default, on port `3000` and I think Dokku is expecting all the applications to run on `5000` by default. I can't seem to find any documentation on that but the moment I removed the following line from the `Dockerfile`:
```dockerfile
EXPOSE 3000
```
And set the application environment required an update to the `APP_PORT` envvar that I run the application on.
```shell
dokku config:set davesmithhayes.com APP_PORT=5000
```
_et voila_! Now I could set up certificates with Let's Encrypt and we are good to go.
## Conclusions
Its been a long time since I started and _finished_ a software project, on my time, and I am happy with. Using Hono and Bun, two technologies I am adjacently familiar with, was also a huge plus to me. I enjoyed it so much that I think this PHP project I have been hobbling together for the last couple of months will get a complete rewrite in TypeScript using Hono and Bun.
I really hope that you read this far and took something away about how to do projects on your own, how viable these technologies are, and how to have fun doing basic things. I don't think this blog would have required as much work as I had put into it, but I am happy that I did. Please [feel free to contact me](mailto:me@davesmithhayes.com) about my Bun and Hono experience.
That's it. I'm a convert. Praise Bun.

View File

@ -0,0 +1,83 @@
---
title: HS(D)P
description: Describing the architecture of Handlers, Services, (Domains), and Presentation
date: 2024-08-11
tags:
- development
- javascript
- typescript
- hono
slug: hsdp
draft: true
---
## Handlers, Services, (Domains?), Presentation
In [my last blog post](https://davesmithhayes.com/posts/fullstack-hono) I talked about how I have stopped thinking of my Node.js applications as Model-View-Controller and started thinking of the applications in a new, fun acronym: Handlers, Services, (_sometimes Domains_), and Prfesentation. I thought this would be a good topic for a new blog post. So here it is.
I am going to be talking about this pattern in relationship to JavaScript and why the language lends itself to the pattern when working on applications.
## The Application
Like the previous post, I talked about building this blog with Bun and Hono. I am going to do the same here, however the application we are going to build is the all time classic - The TODO Application.
## JavaScript
One of the important things about JavaScript that alluded me for far too long was how the modules actually worked with Node.js. A module before ES6 was simply a JavaScript file that declares a `module.exports` value with the code you want to expose to other modules with the `require` function. Like so:
```js
function doSomething() {
// ...
}
module.exports = doSomething;
```
_`example.js`_
Then we can pull in `doSomething` with `require`:
```js
const doSomething = require('./example');
```
_`index.js`_
I don't know why I didn't know this, but the code in the `example.js` file is evaluated **and** run during the `require()` call.
In my past life I would try and build a Singleton class for holding onto an application's Connection Pool to a database. But because of how the Modules in Node.js work, we only need to instantiate the Pool and export it. Here's an example of how we can set up a single pool using the MariaDB official package:
```js
const mariadb = require('mariadb');
const config = { ... };
const pool = mariadb.createPool(config);
module.eports = pool;
```
_`pool.js`_
Now the connection pool is only created once and you can require this
Outline:
* Explain what I know about JavaScript
* How does this differ to my experience with PHP
* Describe modules
* Describe how we can set up singletons thinking in modules
* Talk about avoiding classes
* Talk about big express applications
* And how I used to make MVC applications
Different programming languages require different approaches for organizing code.

View File

@ -29,3 +29,8 @@ pre code.hljs {
border-radius: 5px; border-radius: 5px;
background-color: #fafafa; background-color: #fafafa;
} }
main .post-meta {
font-style: italic;
font-size: 12pt;
}

View File

@ -23,6 +23,7 @@ app.get("/", async (c) => {
return c.render(<Home posts={postList} />, { return c.render(<Home posts={postList} />, {
meta: { meta: {
description: "The blog for Dave Smith-Hayes, a dad and developer.", description: "The blog for Dave Smith-Hayes, a dad and developer.",
mastodonUrl: "https://hachyderm.io/@davesh"
}, },
}); });
}); });

View File

@ -24,6 +24,7 @@ export async function handleSinglePost(c: Context) {
} }
const meta: SiteMeta = { const meta: SiteMeta = {
title: post.meta.title,
description: post.meta.description, description: post.meta.description,
tags: post.meta.tags, tags: post.meta.tags,
author: "Dave Smith-Hayes" author: "Dave Smith-Hayes"

View File

@ -3,4 +3,6 @@ export type SiteMeta = {
tags?: string[], tags?: string[],
author?: string, author?: string,
viewport?: string, viewport?: string,
title?: string,
mastodonUrl?: string
}; };

View File

@ -2,13 +2,20 @@ import { Style } from 'hono/css';
import { SiteMeta } from '@blog/models/SiteMeta'; import { SiteMeta } from '@blog/models/SiteMeta';
import { MetaTags } from '@blog/templates/components/MetaTags'; import { MetaTags } from '@blog/templates/components/MetaTags';
function getPageTitle(title: string|undefined): string {
if (!title) {
return "";
}
return title + " - ";
}
export function Page({ children, meta }: { children: any, meta: SiteMeta }) { export function Page({ children, meta }: { children: any, meta: SiteMeta }) {
return ( return (
<html lang="en"> <html lang="en">
<head> <head>
<title>davesmithhayes.com</title> <title>{ getPageTitle(meta.title) }davesmithhayes.com</title>
<MetaTags meta={meta} /> <MetaTags meta={meta} />
<Style />
<link rel="stylesheet" href="/static/main.css" /> <link rel="stylesheet" href="/static/main.css" />
<link rel="stylesheet" href="/static/intellij-light.min.css" /> <link rel="stylesheet" href="/static/intellij-light.min.css" />
</head> </head>

View File

@ -1,10 +1,15 @@
import { Post } from "@blog/models/Post"; import { Post } from "@blog/models/Post";
export function parseDate(date: Date): string {
return date.toISOString().slice(0,10);
}
export function PostPage({ post }: { post: Post }) { export function PostPage({ post }: { post: Post }) {
const html = { __html: post.html ?? '' }; const html = { __html: post.html ?? '' };
return ( return (
<> <>
<h1>{post.meta.title}</h1> <h1>{post.meta.title}</h1>
<div class="post-meta">Posted on {parseDate(post.meta.date)}</div>
<div class="post-content" dangerouslySetInnerHTML={html} /> <div class="post-content" dangerouslySetInnerHTML={html} />
</> </>
); );

View File

@ -36,6 +36,16 @@ export function ViewPort({ meta }: { meta: SiteMeta }) {
return (<meta name="viewport" content={meta.viewport} />); return (<meta name="viewport" content={meta.viewport} />);
} }
export function MeRel({ meta }: { meta: SiteMeta }) {
if (!meta?.mastodonUrl) {
return (
<></>
);
}
return (<link rel="me" href={meta.mastodonUrl} />);
}
export function MetaTags({ meta }: { meta: SiteMeta }) { export function MetaTags({ meta }: { meta: SiteMeta }) {
return ( return (
<> <>
@ -43,6 +53,7 @@ export function MetaTags({ meta }: { meta: SiteMeta }) {
<Tags meta={meta} /> <Tags meta={meta} />
<Author meta={meta} /> <Author meta={meta} />
<ViewPort meta={meta} /> <ViewPort meta={meta} />
<MeRel meta={meta} />
</> </>
) )
} }