DANIEL
STOKES

Porting my website from laravel to nuxt

November 5, 2024 at 11:00 AM

Nuxt, vue's go to full stack framework, couldn't be easier to get started with. I chose nuxt with the Pruvious cms module to replace my aging laravel with twill cms stack.

This project has been a long time coming. Professionally I have been programming with vue for the last 3.5 years and I have barely touched any php code in that time.

But the thought of taking on this migration was a headache. The previous version of my site was built off of laravel with the Twill CMS toolkit. That is a powerful combo and finding another CMS to rival twill was not so common. Some of twill's critical features for me were it's content blocks, repeater fields, and record fields. I even considered keeping laravel for this CMS and using it just as an api but luckily I found Pruvious.

Pruvious is a nuxt module that adds a cms dashboard with a simple api for collections customization and a very vue friendly way for defining custom blocks. The typesafety of pruvious will actually blow your mind and with every field type that I could need built in!

This site is my first nuxt project. I typically develop with vue as a spa using just vite. Getting up and running with nuxt and pruvious for the first time was dead easy and I was able to recreate my collections, blocks and page layouts in a couple days of free time.

PostsListBlock.vue
<script setup lang="ts">
import { recordsField, textField } from '#pruvious';

const props = defineProps({
    heading: textField({
        required: true,
    }),
    posts: recordsField({
        collection: 'posts',
        required: true,
        fields: {
            title: true,
            description: true,
            publishDate: true,
            path: true,
            image: true,
        },
        populate: true,
        sortable: true,
    }),
    buttonText: textField(),
    buttonLink: textField(),
});
</script>

Take a look at that code snippet of how you define a custom content block. I just think this is amazing how it ties in with vues defineProps api. There is incredible type safety when using these field prop function helpers too.

Process

My first step was to recreate the collections definition and then the content blocks definition. Pruvious has a collection helper called pageLikeCollection which made setting up routes and page rendering a breeze. The most time consuming part is doing the data entry but thankfully for me I didn't have too much to go through so I did it manually.

One major pain point with my old laravel site was the scattered files and code for defining a blocks fields, html markup, javascript and css styles. With Pruvious all of that is in one vue component and you get the benefit of not needing an additional build step and you can use vue's scoped styles feature to keep things organized.

Deployment

My site is hosted on a vps where I also host a bunch of other things. I do this by using docker and a some special helper docker images for handling the reverse proxy and ssl certificates. You can read more about that here. Getting this nuxt app deployed was as easy as adding some docker configuration and pulling the git repo onto the vps.

docker-compose.yml
version: '3'
services:
  nuxt-app:
    build: 
      context: .
      dockerfile: Dockerfile
    container_name: nuxt3-app-prod
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - NUXT_HOST=0.0.0.0
      - NUXT_PORT=3000
      - VIRTUAL_HOST=yourdomain.com
      - LETSENCRYPT_HOST=yourdomain.com
      - VIRTUAL_PORT=3000
      - HTTPS_METHOD=redirect
    volumes:
        - .uploads:/app/.uploads
        - .uploads:/app/public/uploads
        - ./pruvious.db:/app/pruvious.db
    networks:
      - net
networks:
  net:
    external: true
Dockerfile
FROM node:alpine as BUILD
WORKDIR /build
COPY package.json ./
RUN npm install
COPY ./ ./
RUN npm run build

FROM node:alpine
WORKDIR /app
EXPOSE 3000
COPY --from=BUILD /build/.output /app
CMD ["node", "./server/index.mjs"]
© Daniel Stokes 2024