← Return to all blogs

Welcome to my Website!

04/24/2024

Welcome

Thank you for checking out my website! I’ve wanted to make a personal portfolio for a little while and finally, after lots of itterations of trying to find a perfect tech-stack I finally settled on using AstroJS. I wanted to write this initial blog post for a couple of reasons:

  • I wanted to have a place to write a personal blog. I’m not sure who, if anyone will read these, but I want to get into writing out my thoughts more and want to use this as a kind of a technical-diary of sorts for my personal thoughts on new technologies that come out
  • This website went through many itterations and I wanted to explain my thought process for why I chose to go with Astro over some other popular web frameworks
  • I wanted to use this to introduce myself a bit!

Who am I?

My name is Will Migdol. I’m a Software Engineer with two-years of experience working for CodeHS, doing full-stack web development. I’m a serial hobby collector, especially as of late, picking up playing ukulele and cooking. I grew up in California (having lived in San Jose, South Lake Tahoe and Sonoma) and am now living in Seattle, Washington.

How did I make this site?

This site was built using Astro JS. I didn’t really need a backend setup, as all of the blogs are handled in Astro using its content collections for managing blog posts and projects on the site. I used Tailwind CSS with Daisy UI for styling components and vanilla JavaScript to handle project filtering and theme switching.

Pretty much all of my code is broken down into two folders: static and src. The static folder is used to store any assets for the project, including images and project bundles. The src folder itself is broken down into five subdirectories:

  • styles
  • lib
  • components
  • pages
  • content

The styles folder currently just has one CSS file in it, globals.css. This contains the simple boilerplate to get tailwind to work

@tailwind base;
@tailwind components;
@tailwind utilities;

The lib folder and components folder are the core of the project, with these two folders making up the code that builds up the pages on the site.

The lib folder is used to manage all typescript logic and types that I want to be accessible throughout the entire site. Right now though, there are only two files inside the lib directory. The first of these is contentManagers.ts. I’m gonna cycle back to this when we hit the content directory. The other file included is projectTag.ts. This file contains all of the logic that I use to manage tagging for projects, including the types of tags, the type definition for tags, the constant values for tags available for projects and a couple of miscellaneous utility methods.

The components folder is used for any custom components made for the site. There are two general components I have in the top-level directory that can be used anywhere, one for a project tag bar and one for my layout that all pages on the site follow. I also have two subdirectories in here, one for page specific components (made up in itself of more subdirectories to organize page specific components by page) and one folder to build out my topnav.

The pages folder is fairly straightforward. It contains all the pages for my site and uses file-based routing to automatically handle navigation.

The content folder contains all of the … for lack of a better word, content that I have on the site. This contains all of the information that I have for blog posts and projects, which Astro then uses to build out the site.

How does Astro’s Collection System work?

You can read about Astro’s collection system here, but in summary you can define a schema using Zod to represent a type of “Collection”. I have two different types of collection, one for my blogs and one for my projects.

Let’s start by looking at the collection I’ve defined for my blogposts

const blogCollection = defineCollection({
  type: "content", // v2.5.0 and later
  schema: z.object({
    title: z.string(),
    description: z.string(),
    date: z.string(),
    isDraft: z.boolean().optional(),
  }),
});

The first thing to take note of is the type being used here. Since this is a content that tells Astro that this will be a markdown file and should be treated as such. This is important because it will let us render the file from markdown to HTML when we include it on the site.

The schema is then pretty straightforward. Each blogpost has a title, a description, a date and an isDraft field to indicate if it should show in navigation or not.

These pages are still actually accessible if you know the URL slugs which you can find pretty easily in the codebase but… please don’t

The other collection I have defined is for my projects.

const projCollection = defineCollection({
  type: "data",
  schema: z.object({
    title: z.string(),
    description: z.string(),
    embedLink: z.string().optional(),
    // we use string here so we can include units
    embedHeight: z.string().optional(),
    embedWidth: z.string().optional(),
    github: z.string(),
    tags: z.array(z.string()).optional(),
    sortIndex: z.number(),
  }),
});

This collection as we can see is actually a bit more complex.

Let’s start by looking at the type. Since this is using a data type instead of a content type, we are telling Astro we want these instances to be treated as JSON.

The projects section on my site isn’t something that I actually wanted to necessarily be automatically generated the same way my blogposts were. I have side projects that I’ve done in various different frameworks and I wanted to be able to host them in variable ways without the constraint needing to stay consistent like I did for my blog posts.

This is also why so many of these fields are optional. Let’s start by looking at the fields that are required. Title, description and github are all self-explanatory. The one slight-regret I have with naming the field, github instead of codeSource is that it isn’t totally accurate. This portfolio is actually hosted on GitLab, which means that the github field I set on this is technically a lie.

The other required field I have, “sort value” is there to help prioritize which projects should be shown first. Right now, they’re all using multiples of 10000. This way it’s a tiny bit easier later if I add more projects and want them to be betweent two existing ones.

Next I have three embed properties defined for if we want the project to be an embed, rather than hosted on the site. These are embedLink, embedWidth and embedHeight. I feel that these are all also self-explanatory.

Lastly I have the tags saved as an array of strings. This is fine for now, but one thing that I’d like to be able to do at one point (though I’m not sure if this is possible with Zod) is to include some type-safety to enforce that all of the tag names are valid.

What about the alternatives?

Django w/ Django Templates

Django is probably my favorite web framework that’s out there. I use it for work and I was very heavily considering it. I decided against using it for a couple of reasons though. While I really like Django’s ORM, it wasn’t necessary for me to include a database. I was considering using it to save my Blogs and Projects in a SQL database, but I prefered using Astro Collections to manage blogs from a single folder and be able to add them easily.

With the approach of adding the blogs to a SQL database I would’ve needed to define a schema, setup a SQL database and manually add blogs to the site, either through Django Admin or a custom UI tool, which would’ve required setting up a custom login and Django admin account. Having my blogs/projects saved in the codebase instead was a lot simpler of a process and by using Astro instead I avoided reinventing the wheel. In addition, while adding views in Django is a very easy process, I prefer file-based routing. It keeps the components a lot more organized in my opinion and since I didn’t need to setup an API to make calls to, I didn’t want to have to make a lot of new methods that would be doing pretty much the same thing.

If I was going to use Django, I would be using django templates. I was also considering using a frontend framework (either React or Svelte). To me this would’ve felt like two different apps though (one for the frontend and one for the backend) and I preferred to keep it all together. There were a couple of other backend frameworks I was considering using with this setup of having one app in another language for the backend and using React or Svelte for the frontend (such as Flask, Express or Spring) but decided against them for the same reason.

What about NextJS?

I was also heavily considering using NextJS for my site and it was my second-choice behind Astro. The main reason why I chose to go with AstroJS over NextJS is that I wanted to be able to use its framework templates.

If I were using NextJS, I would only be able to have side projects hosted directly on the site if they were built using React, which while I do use fairly often, I have several other projects on the site not using React that this would’ve excluded. I could’ve still hosted these elsewhere and embedded them on my site using an iframe (which I do use in some cases), I didn’t want to have that restriction.