Practicing Data Insertion with Sequelize

Understanding the Problem

When working with databases, we often need to add new records in different ways. Think of it like adding new books to a library - sometimes we want to prepare a book's details before putting it on the shelf (build), and other times we want to place it directly (create). We need to understand three main approaches:

First, using build() and save() separately:

// First approach: Build and Save
const puppy = await Puppy.build({
    name: "Trudy",
    ageYrs: 2,
    weightLbs: 38,
    breed: "Brittany Spaniel",
    microchipped: false
});
await puppy.save();

Second, using create() to do both steps at once:

// Second approach: Create
const puppy = await Puppy.create({
    name: "Beans",
    ageYrs: 1.6,
    weightLbs: 42,
    breed: "Bulldog",
    microchipped: true
});

And third, handling data insertion through API endpoints:

// Third approach: API Endpoint
app.post('/puppies/create', async (req, res) => {
    const { name, ageYrs, breed, weightLbs, microchipped } = req.body;
    const newPuppy = await Puppy.create({ 
        name, 
        ageYrs, 
        breed, 
        weightLbs, 
        microchipped 
    });
    res.json({
        message: "Successfully created new puppy!",
        data: newPuppy
    });
});

Understanding the Different Methods

The Build and Save Method

Think of build() and save() like writing a draft before publishing. Here's a complete example with error handling:

// Complete implementation of build and save
const insertPuppyWithBuild = async () => {
    try {
        // First we build the instance
        const puppy = await Puppy.build({
            name: "Trudy",
            ageYrs: 2,
            weightLbs: 38,
            breed: "Brittany Spaniel",
            microchipped: false
        });

        // We can make modifications here if needed
        if (puppy.weightLbs > 30) {
            puppy.size = "large";
        }

        // Then we save to the database
        await puppy.save();
        return puppy;
    } catch (error) {
        console.error("Error inserting puppy:", error);
        throw error;
    }
};

The Create Method

The create() method is like doing everything in one step. Here's a complete example:

// Complete implementation of create
const insertPuppyWithCreate = async () => {
    try {
        const puppy = await Puppy.create({
            name: "Beans",
            ageYrs: 1.6,
            weightLbs: 42,
            breed: "Bulldog",
            microchipped: true
        });
        return puppy;
    } catch (error) {
        console.error("Error creating puppy:", error);
        throw error;
    }
};

API Endpoints for Insertion

When creating API endpoints, we need to handle both the data insertion and the response. Here are complete implementations:

// Build endpoint
app.post('/puppies/build', async (req, res) => {
    try {
        // Extract data from request body
        const { name, ageYrs, breed, weightLbs, microchipped } = req.body;
        
        // Build the puppy instance
        const puppy = Puppy.build({
            name,
            ageYrs,
            breed,
            weightLbs,
            microchipped
        });

        // Save to database
        await puppy.save();

        // Send success response
        res.json({
            message: "Successfully created new puppy using build!",
            data: puppy
        });
    } catch (error) {
        // Handle errors appropriately
        res.status(500).json({
            message: "Error creating puppy",
            error: error.message
        });
    }
});

// Create endpoint
app.post('/puppies/create', async (req, res) => {
    try {
        // Extract and create in one step
        const puppy = await Puppy.create({
            name: req.body.name,
            ageYrs: req.body.ageYrs,
            breed: req.body.breed,
            weightLbs: req.body.weightLbs,
            microchipped: req.body.microchipped
        });

        // Send success response
        res.json({
            message: "Successfully created new puppy!",
            data: puppy
        });
    } catch (error) {
        res.status(500).json({
            message: "Error creating puppy",
            error: error.message
        });
    }
});

When to Use Each Method

Use Build When:

The build() and save() approach is best when you need to:

1. Modify the instance before saving. For example, if you need to calculate some values or make validations:

const puppy = Puppy.build({
    name: "Max",
    weightLbs: 65
});

// Perform calculations or modifications
puppy.size = puppy.weightLbs > 50 ? "large" : "medium";

// Then save
await puppy.save();

2. Perform custom validation before saving:

const puppy = Puppy.build({
    name: "Spot",
    ageYrs: 0.5
});

if (puppy.ageYrs < 0.25) {
    throw new Error("Puppy is too young");
}

await puppy.save();

Use Create When:

The create() method is better when:

1. You have all the data ready and don't need intermediate steps:

// Direct creation when data is ready
await Puppy.create({
    name: "Bella",
    ageYrs: 1.2,
    breed: "Labrador",
    weightLbs: 55,
    microchipped: true
});

2. You're implementing simple API endpoints where you just need to insert data directly:

app.post('/puppies', async (req, res) => {
    const puppy = await Puppy.create(req.body);
    res.json(puppy);
});

Error Handling Best Practices

When inserting data, always implement proper error handling:

// Example of comprehensive error handling
async function insertPuppyWithErrorHandling() {
    try {
        const puppy = await Puppy.create({
            name: "Rex",
            ageYrs: 1.5,
            breed: "German Shepherd",
            weightLbs: 70,
            microchipped: true
        });
        return puppy;
    } catch (error) {
        if (error.name === 'SequelizeValidationError') {
            // Handle validation errors
            console.error("Validation error:", error.errors);
            throw new Error("Invalid puppy data");
        } else if (error.name === 'SequelizeUniqueConstraintError') {
            // Handle unique constraint violations
            console.error("Duplicate entry:", error.errors);
            throw new Error("Puppy already exists");
        } else {
            // Handle other errors
            console.error("Database error:", error);
            throw new Error("Error creating puppy");
        }
    }
}

Testing Your Implementation

To verify your data insertion is working correctly:

1. Test direct database insertion:

// Using command line
sqlite3 db/dev.db "SELECT * FROM Puppies WHERE name = 'Trudy';"

2. Test API endpoints using Postman or curl:

// Example curl command
curl -X POST http://localhost:8000/puppies/create \
  -H "Content-Type: application/json" \
  -d '{"name":"Fred","ageYrs":2.1,"breed":"Boston Terrier","weightLbs":20,"microchipped":true}'

Further Learning

To deepen your understanding of Sequelize data insertion, try:

1. Implementing bulk creation for multiple records

2. Adding custom validation before insertion

3. Handling complex data relationships

4. Implementing transaction support for safer insertions

5. Creating more sophisticated error handling strategies