Backend with Fastify - Part 4 (Seeding Database with Knex)

Search for a command to run...

No comments yet. Be the first to comment.
In this series, I will explore the concepts of fastify and see how we can build a production ready backend service with it.
Now that we have our database set up and seeded in part 4, it's crucial to grasp some key concepts of Fastify before diving into application development. You can find the complete code for this part here Fastify Concepts Lifecycle and Hooks Fastify o...
Now that we have our database set up and seeded in part 4, it's crucial to grasp some key concepts of Fastify before diving into application development. You can find the complete code for this part here Fastify Concepts Lifecycle and Hooks Fastify o...

Introduction Recently, at work, I encountered the following scenario: A static website was uploaded to an S3 bucket with a filepath similar to the following: - index.html - page1 - index.html - page2 - index.html This S3 bucket file was ...

Continuing from Part 2, our next step is to set up the database for our application. To follow along, you can clone this branch. The complete code for this part can be found here. To keep things straightforward for our purpose, we'll create two table...

In part 1, we have already set up our project. In this part, we will familiarize ourselves with the basic concepts of Fastify, which are essential for creating REST APIs. If you wish to follow along, you can clone the Part I branch. For the complete ...

Continuing from part 3, we will explore how to seed data using Knex.
To follow along, you can use the part-3 branch from this repo. The full code for this post is in the part-4 branch.
Seed files allow us to populate the database with default data, ensuring a consistent starting point for development, testing, or demo environments. They play a crucial role in maintaining a clean and controlled database state.
Similar to migrations, Knex provides commands for seeding. Let's begin by adding two scripts to our package.json:
{
scripts: {
// previous
"seed:make": "knex seed:make",
"seed:run": "knex seed:run"
}
}
These scripts, just like migration commands, enable us to create seeding files and run them. To create a new seed file, execute:
npm run seed:make insert_users
This will generate a file at ./seeds/development/insert_users.ts. Exclude seed files from TypeScript compilation by modifying tsconfig.json:
{
"exclude": [//previous,"seeds/**/*.ts"]
}
Also, add seeds to .eslintignore.
Before diving into seeding, let's set up a utility function for password hashing. Create a new file generate_hash.ts inside src/utils:
import * as crypto from 'crypto'
import * as util from 'util'
const pbkdf2 = util.promisify(crypto.pbkdf2)
export const generateHash = async (password: crypto.BinaryLike, salt?: crypto.BinaryLike) => {
if (!salt) {
salt = crypto.randomBytes(16).toString('hex')
}
const hash = (await pbkdf2(password, salt, 1000, 64, 'sha256')).toString('hex')
return { salt, hash }
}
Now, we're ready to hash passwords securely.
In our seeding file (insert_users.ts), we'll insert two default users:
import { Knex } from 'knex'
import { generateHash } from '../../src/utils/generate_hash'
export async function seed(knex: Knex): Promise<void> {
// Check if users already exist
const user1Exists = await knex('users')
.where('email', 'example1@favmov.com')
.first()
const user2Exists = await knex('users')
.where('email', 'example2@favmov.com')
.first()
// If both users do not exist, insert them
if (!user1Exists) {
const { salt, hash } = await generateHash('password1')
await knex('users').insert({
email: 'example1@favmov.com',
password: hash,
salt: salt,
})
}
if (!user2Exists) {
const { salt, hash } = await generateHash('password2')
await knex('users').insert({
email: 'example2@favmov.com',
password: hash,
salt: salt,
})
}
}
According to Knex's official docs:
"Seed files are executed in alphabetical order. Unlike migrations, every seed file will be executed when you run the command. You should design your seed files to reset tables as needed before inserting data."
So, I chose to check for the user and insert them if not already present.
Now, to run the seed:
npm run seed:run
With this, we have default users. In the next part, we will return to Fastify and explore how to perform authentication and create authenticated routes.