Home / Blog / Database Seeds with the Nuxt Task Runner
Database Seeds with the Nuxt Task Runner

Database Seeds with the Nuxt Task Runner

Daniel Kelly
Daniel Kelly
Updated: December 3rd 2024

Drizzle ORM provides type-safe database operations with minimal boilerplate. It’s perfect for interacting with an SQLite database with Nuxt. Nitro tasks provide the icing on the cake making it easy to run seeders directly from the Nuxt devtools. In this article, I’ll show you how to get setup with drizzle ORM in your Nuxt project and you’ll be migrating and seeding your database in no time!

Install Drizzle ORM In Nuxt and Point it to a Database

Install Drizzle ORM for database operations and Drizzle Kit for migration tools.

npm i drizzle-orm @libsql/client dotenv
npm i -D drizzle-kit tsx

Then create a .env variable to configure where the SQLite database file will live. Let’s add it to a folder called .data as that’s already in a Nuxt project’s .gitignore by default.

DB_FILE_NAME=file:.data/local.db

Finally, create the database file.

mkdir ./.data && touch ./.data/local.db 

The Drizzle Config File in a Nuxt App

The drizzle config file is used by Drizzle Kit and contains all the information about your database connection, migration folder, and schema files. Set it up as follows:

// drizzle.config.ts
import 'dotenv/config';
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  out: './drizzle',
  // a file defining our data structure we'll create in a minute
  schema: './server/db/schema.ts',

  // the type of database we're using
  dialect: 'sqlite',

  // the file where our database will be stored
  dbCredentials: {
    url: process.env.DB_FILE_NAME!,
  },
});

An Nuxt Utility Function for Interacting with the Database

Drizzle Kit will be used during the migration process to connect to the database. You’ll also need to connect to the database when running the Nuxt application and the Nitro tasks. Create a utility function to accomplish this within the server/utils directory.

// @/server/utils/db.ts
import { drizzle } from 'drizzle-orm/libsql';

export function useDb() {
    return drizzle(import.meta.env.DB_FILE_NAME!);
}

Define Drizzle Database Schema in Nuxt

With setup now complete you can start modeling what data will look like. Define your database structure using Drizzle's type-safe schema builder. This example creates a users table that stores basic user data. Notice the filename matches up with the schema option we set in drizzle.config.ts in the previous step.

// server/db/schema.ts
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core";
export const usersTable = sqliteTable("users", {
    id: int().primaryKey({ autoIncrement: true }),
    name: text().notNull(),
    age: int().notNull(),
    email: text().notNull().unique(),
});

Drizzle Migrations Setup in Nuxt

Migrations help you version control database changes. Add these scripts to manage them.

{
  "scripts": {
      // Creates migration files from schema changes
    "db:generate": "drizzle-kit generate",     
    // Applies migrations to database
    "db:migrate": "drizzle-kit migrate",
    // Directly apply changes to your database
    // convenient method for quickly testing new schema designs in dev
    "db:push": "drizzle-kit push"
  }
}

Now we can apply our database schema to the database in development with:

npm run db:push

Migration Workflow with Drizzle ORM

Follow these steps when making changes to your database structure.

For quick iteration during development:

  1. Update schema in schema.ts
  2. Push the migration with npm run db:push

For scalable database management for prod with a record of db migrations stored in the git repo:

  1. Update schema in schema.ts
  2. Generate migration: npm run db:generate - Creates SQL migration files
  3. Apply migration: npm run db:migrate - Executes changes on database

Database Seeding in Nuxt with Nitro Tasks

With the database structure now in place, you can seed it with dummy data. This helps you develop your application with realistic data in place with very little effort. This is a great use-case for a Nitro tasks and Faker.js!

First install Faker.js for easy dummy data generation.

npm install @faker-js/faker --save-dev

Then bootstrap a new nitro task in server/tasks/db/seed.ts

// server/tasks/seed.ts
export default defineTask({
        // give the task a name and a description
    meta: {
        name: 'db:seed',
        description: 'Seed database'
    },
    async run() {
            // provide the logic to seed the database
    }
})

Within the task run function, you can generate as many fake users as you’d like with faker.

// server/tasks/seed.ts
import { usersTable } from "../db/schema"
import { faker } from '@faker-js/faker';

export default defineTask({
    meta: {
        name: 'db:seed',
        description: 'Seed database'
    },
    async run() {
        // initialize an array of users 
                // whose type is based on the structure of 
                // the users table
        const users: typeof usersTable.$inferInsert[] = [];

        // push 1000 fake users generated by faker.js into the array
        for (let i = 0; i < 1000; i++) {
            users.push({
                name: faker.person.firstName(),
                email: faker.internet.email(),
                age: faker.number.int({ min: 3, max: 75 })
            });
        }
    }
})

Then insert those users into the database with the useDb utility function and return a success message.

// server/tasks/seed.ts
import { useDb } from "../utils/db";
import { usersTable } from "../db/schema"
import { faker } from '@faker-js/faker';

export default defineTask({
    meta: {
        name: 'db:seed',
        description: 'Seed database'
    },
    async run() {
        // generate users ...

                // use the db utility function to insert the fake users
        const db = useDb();
        await db.insert(usersTable).values(users) // important to await!

        // return result
        return { 
            result: 'success', 
            message: `${users.length} users seeded` 
          }
    }
})

Finally wrap the full thing in a try catch to handle errors.

// server/tasks/seed.ts
import { useDb } from "../utils/db";
import { usersTable } from "../db/schema"
import { faker } from '@faker-js/faker';

export default defineTask({
    meta: {
        name: 'db:seed',
        description: 'Seed database'
    },
    async run() {
        try {
                // the full seed logic here...
            return { 
                result: 'success', 
                message: `${users.length} users seeded` 
              }
          // handle errors
        } catch (err) {
            if (err instanceof Error) {
                return {
                    error: err.message
                };
            } else {
                return {
                    error: 'Unknown error'
                }
            }
        }

    }
})

The full seed script looks like this:

// server/tasks/seed.ts
import { useDb } from "../utils/db";
import { usersTable } from "../db/schema"
import { faker } from '@faker-js/faker';

export default defineTask({
    meta: {
        name: 'db:seed',
        description: 'Seed database'
    },
    async run() {
        try {
                // initialize an array of users 
                    // whose type is based on the structure of 
                    // the users table
            const users: typeof usersTable.$inferInsert[] = [];

            // push 1000 fake users generated by faker.js into the array
            for (let i = 0; i < 1000; i++) {
                users.push({
                    name: faker.person.firstName(),
                    email: faker.internet.email(),
                    age: faker.number.int({ min: 3, max: 75 })
                });
            }

                        // use the db utility function to insert the fake users
            const db = useDb();
            await db.insert(usersTable).values(users) // important to await!

            // return result
            return { 
                result: 'success', 
                message: `${users.length} users seeded` 
              }
        } catch (err) {
            if (err instanceof Error) {
                return {
                    error: err.message
                };
            } else {
                return {
                    error: 'Unknown error'
                }
            }
        }

    }
})

Finally, in order for tasks to work, you must enable the feature in nuxt.config.ts with the nitro.experimental.tasks option.

// nuxt.config.ts
export default defineNuxtConfig({
  // ...
  nitro: {
    experimental: {
      tasks: true
    }
  }
})

Now the seed is setup and ready to run. You can run the seed anytime you want from the Nuxt devtools. Look for the devtools’ item with the “play” looking icon (it might be under the vertical ellipsis) on the left hand side of the devtools and press the send button to execute your task!

screenshot of the task running in the Nuxt devtools

That’s it! 🎉 You can now easily seed new users anytime you want.

Manage the Database Visually with Drizzle Studio

Now that you have some data in the database, it would be nice to view it. No problem! Setup a script in package.json to run drizzle studio.

"scripts": {
    "db:studio": "drizzle-kit studio"
},

And run it to open up a visual database client in your browser.

npm run db:studio
Screenshot 2024-12-02 at 2.49.09 PM.png

Conclusion

In this article we talked about how to use Nuxt, Nitro, and Drizzle ORM for easily managing common database needs like migration and seeding. If you’d like to get more practice, tips, and tricks using Faker.js to generate fake data for seeding a db, checkout our course Generating Fake Data with Faker.js. If you’d like to dive deeper into developing full stack apps with Nuxt checkout the course Nuxt.js 3 Fundamentals or Mastering Nuxt.

Related Courses

Start learning Vue.js for free

Daniel Kelly
Daniel Kelly
Daniel is the lead instructor at Vue School and enjoys helping other developers reach their full potential. He has 10+ years of developer experience using technologies including Vue.js, Nuxt.js, and Laravel.

Comments

Latest Vue School Articles

Generating Fake Data with Faker.js

Generating Fake Data with Faker.js

Discover the power of Faker.js, the essential JavaScript library for generating realistic fake data, in our new video course created in collaboration with the developers at Vehikl (vehikl.com).
Felipe Flor
Felipe Flor
What Does the Vueniverse Look Like in 2025? Predictions for the Vue Ecosystem in the Year Ahead

What Does the Vueniverse Look Like in 2025? Predictions for the Vue Ecosystem in the Year Ahead

Vue.js enters 2025 from a position of strength, with a mature ecosystem and plenty of resources available to help it’s developers thrive. So what might 2025 look like for Vue developers?
Daniel Kelly
Daniel Kelly

Our goal is to be the number one source of Vue.js knowledge for all skill levels. We offer the knowledge of our industry leaders through awesome video courses for a ridiculously low price.

More than 200.000 users have already joined us. You are welcome too!

Follow us on Social

© All rights reserved. Made with ❤️ by BitterBrains, Inc.