keithwhor / nodal

API Services Made Easy With Node.js
http://www.nodaljs.com/
MIT License
4.51k stars 210 forks source link

Add support for seeding a database #61

Closed intabulas closed 8 years ago

intabulas commented 8 years ago

I am opening this ticket as a place for discussion around possible ways to implement command line seed support (ala rails db:seed) This need is coming up in a few tickets #58 #48.

So to start, in my situation I was porting an older small app of mine that was backed by mongo. I have a command line node tool to pump in seed data when I stood up new instances. During my port to nodal, which I was doing as a public example of a nodal app, I really wanted a single db cli command (see #50) to prepare, migrate and seed the sample data so that someone could fork the repo and use the built in deploy to heroku button and have a running app with real data

So my first thought is do we have a large json doc that can be used to seed models (like schema.json) or do we actually generate a real module that can be required and run to allow for model validations, model relations etc (ala rails)

so for a quick and dirty comparison of the thoughts (excuse errors, thinking as a I go here)

a seed.json (which has to have proper ordering)

{
  "user": {
    "id": 1,
    "username": "mark",
    "email": "mlussier@gmail.com"
  },
  "tweet": {
    "id": 1,
    "body": "Hey There",
    "user_id": 1
  }
}

or in js (remember, just a rough idea)

  'use strict';

  const Nodal = require('nodal');
  const User = Nodal.require('app/models/user.js');
  const Tweet = Nodal.require('app/models/user.js');

  let markUser;
  User.create({ username: "mark", email: "mlussier@gmail.com"}, (err, model) => {
    markUser = model;
  });

  Tweet.create({ body: "Hey There", user_id: markUser.id}, (err, model) => {

  });

or even

  'use strict';

  const Nodal = require('nodal');
  const User = Nodal.require('app/models/user.js');
  const Tweet = Nodal.require('app/models/user.js');

  User.create({ username: "mark", email: "mlussier@gmail.com"}, (err, model) => {

      Tweet.create({ body: "Hey There", user_id: model.id}, (err, model) => {
      });

  });

Okay seed (no pun intended) laid, thoughts?

intabulas commented 8 years ago

Do we need some sort of find_by or find_or_create_by support? Maybe using a variation of model#query that works like find but takes an queryparam like object vs an id...

keithwhor commented 8 years ago

What I think might be best is if we have a ModelFactory that creates models from POJOs.

basically:

let UserFactory = new ModelFactory(User);
UserFactory.create([
  {id: 1, name: 'Selena'},
  {id: 2, name: 'Adele'},
  {id: 3, name: 'Ellie'}
], (err, models) => {
  /* do something */
});

The inner workflow is ModelFactory creates a ModelArray which runs a .saveAll. Might be the most idiomatic way to set it up.

Then we can have a ./db/seed.json file that's just;

{
  "User": [
    {id: 1, name: 'Justin'},
    {id: 2, name: 'Taylor'},
    {id: 3, name: 'Rihanna'}
  ]
}

And when we run nodal db:seed, it easily parses the json file, runs a model factory for each, and voila.

I wouldn't mind doing this myself, can get to it tonight. :+1:

intabulas commented 8 years ago

Okay that seems alot more cleaner and easier for the new user to grok, works for me. My only thought was if someone manually adds an FK, ordering might be critical, but for 99.99999% of the other apps that wont be an issue ;)

While your doing that, I am going to start to put together changes for #50 (callbacks in the cli commands) so that we can start do to more coarse grain db: commands

intabulas commented 8 years ago

Was thinking in the shower it would be good to either have the seed.json support env's (like db.json)

{
  "development": {
    "User": [
    ]
  },
  "production": {
    "User": [
    ]
  }
}

or if we want to be crazy, make the seed a js file so that we can do nutty stuff like generating fake test data

"use strict";

let faker = require('faker');

module.exports = {
  development: {
  },
  production: {  
  },

  test: { 
    "User": function() {
      let data = []
      for(let i = 0, i < 30, i++) {
        data.push({ name: faker.name.findName()});
      }
      return data;  

    }
  }
};

obviously that js example could be way better, but it illustrates...

keithwhor commented 8 years ago

Added in:

https://github.com/keithwhor/nodal/commit/b4eb007de6d9d71185d2477953a0167fa66bfcbe

:)