Ensure NodeJS is installed. Download and follow instructions here if you don’t have Node installed on your computer. I use pnpm as my package manager. You can use npm or yarn if you prefer. Install pnpm globally using the following command.
Note that we have included all primevue components in the components section. Typically you use include and exclude to be selective about the components to be included for the project.
Next, we will include some custom styling. Since I am lazy, I will get inspired by a free template provided by PrimeVue called SakaiVue
Create a file assets/styles.scss -
1
@import"layout/layout.scss";
Create a new file styles/layout/layout.scss and add below content.
The individual files will have some custom styling elements - you will find them in the repo here. Again, all the circus with styling is not quite necessary for the simple app we are building, but is simply included to force a structured approach.
Start server with the command pnpm run dev and you should see the following screen.
Home Page & Navigation
Add a navigation bar to the app. Create a new file components/NavbarAnon.vue and add below content. I used Anon suffix to denote that the toolbar is for anonymous user - I have found it easier to have a separate toolbar for logged-in users (not quite relevant here).
The page will use the default layout. The individual components are not much to speak about - you will find them in the repo.
You should now see a nice looking home page at http://localhost:3000.
I did include a second layout for the logged-in user in the repo, but am not discussing that here (useful in cases where I need a different toolbar, sidebar navigation for the actual app, for example).
Setup Drizzle & Data Migrations
Let us setup Drizzle ORM and Drizzle Kit. The latter is used for migrations and other admin tasks.
1
2
pnpm i drizzle-orm better-sqlite3
pnpm i -D drizzle-kit
Create a new file drizzle.config.js and add below content.
The schema file is going to be used by default by Drizzle CLI. While we do the actual migrations, all we need to do is pnpm exec drizzle-kit generate:sqlite and the migration files are automatically created in the out folder. We will see this in action in a second.
First, let us create a simple table. Create a new file ./data/schema_user.js and add below content.
You could also suffix --out data/migrations --schema data/schema_user.js if you want to be explicit about the output folder and schema file.
Run the migration.
1
pnpm exec drizzle-kit push:sqlite
You can see browse the SQLite database and see the table created using the Drizzle tool (pnpm exec drizzle-kit studio & point the browser at the URL), or using a tool like DB Browser for SQLite.
Creating the Server API
Nuxt is great for the frontend, but also features a full-fledged node server / framework based on h3.
We will use this server to create a test API.
Create a new file server/api/hello.js with below content -
Go ahead and enter any email id and you are registered! We will not be adding a full-fledged login function in this post (we need to use some sort of auth library like Lucia to do that).
You may be wondering on why registration and login functions are combined and not two separate pages. You cannot go wrong with either, but the common page is advantageous when you support different types of authentication (e.g., Google, Facebook, custom etc.).
Add Todo API
Create two new APIs to handle get and post. We will use “proper” conventions this time around.
First, create a new file server/api/todo.get.js and add below content.
todo.get.js will handle requests to get all todo’s. If there was a need to get a specific todo, I would create a similar file server/api/[id].get.js and handle the request there.
Let us also code to handle post - create a new file server/api/todo.post.js and add below content.
const newTodo = { ...body } may be useless in this situation, but is more relevant when using types or performing validations on the incoming message
The insert function returns a response with lastInsertRowid - we use that to get the newly created todo and return it to the caller. Calling function in client will use the data to update the UI. Again, the relevance may be lost here, but is a good practice since database commit may populate fields, add timestamps etc. and the client needs the updated info
Add Todo Page
Create a new file pages/dashboard.vue and add below content.
This page will allow user to create new todo’s and also query the API for todo’s.
Note that-
We have used onBeforeMount to execute the getTodos function on page load. While onMounted is the more common way to do this, onBeforeMount gets triggered for page refreshes as well
DataView and DataTable in PrimeVue makes it quite easy to display list data. I have used DataView here, but DataTable can be more beneficial for larger data sets
Typical apps need some validation on the forms - both on the page and on the server. I have not included that here for brevity
And, that’s a wrap for today, folks.
Conclusion
Nuxt can easily be a full-stack app
I cannot get myself to like Nuxt as the go-to full stack app
I am spoilt by the ease-of-use of a server framework - middleware, routes, controller/service included
My typical servers include functions that are not covered here and would need separate treatment anyway - e.g., batch processing / scheduled jobs
Server/client architecture looks cleaner to read / code
I perceive the typical server/client segregation to be easier to maintain without forcing a hard push for all concerns
Also in other news-
I have come to admire Drizzle but cannot get over the intuitiveness of knex. Also, I have come to fear the sometimes drastic changes happening in smaller libraries across versions
PrimeVue will probably be my new library of choice (was Vuetify earlier)
I keep looking for “one perfect way” to get things done faster (I am more of a casual developer), but that is still in the future I guess.