Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save ironheart122/22a67056b0bcc77411b9f2e87b72dfed to your computer and use it in GitHub Desktop.
Save ironheart122/22a67056b0bcc77411b9f2e87b72dfed to your computer and use it in GitHub Desktop.

Revisions

  1. @michaelmiller2116 michaelmiller2116 created this gist Jun 6, 2018.
    380 changes: 380 additions & 0 deletions express-knex-checklist.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,380 @@
    Node.js Express JSON API - CRUD Stickers
    We'll be using:

    Postgres for our database

    knex.js for our database migrations, seeds and queries.

    express.js for our JSON routes

    Mocha, Chai and SuperTest to test our routes

    Prerequisites (Mac OS Commands)

    Latest version of Node.js
    brew install node
    Postgres
    brew install postgres
    brew services start postgres
    createdb
    Express Generator
    npm install -g express-generator
    Knex installed globaly
    npm install -g knex
    Server Check List
    Create a server folder

    (example directory ~/dev/CRUD)

    mkdir server then cd into server

    Generate Express App

    express
    initialize git repo

    git init
    You also need a .gitignore file
    gitignore node
    In package.json edit:
    "name": "crud-stickers",
    "version: "1.0.0",
    Now we need to add and commit
    git add .
    git commit -m "initial commit"
    Create database

    createdb name-this-database (this will hold stickers and all the things)
    Initialize knex project *Remain in the server directory

    npm install --save knex pg *This should also install a package-lock.json and also add knex and pg dependencies in your package.json file

    Install knex and pg

    Create knexfile.js

    Sets up the configurationg to connect knex to the database
    knex init
    In the knexfile.js delete everything except development. Should look like this:
    module.exports= {
    development: {
    client: 'pg',
    connection: 'postgres://localhost/named-database'
    },
    };
    Create sticker table migration

    Watch video for creating the table sticker to paste in readme
    Now we can create the migration, this will create the migration folder and inside will be the migration:
    knex migrate:make create-sticker
    In the migrations folder:
    exports.up = function)knex, Promise) {
    return knex.schema.createTable('sticker', (table) =>
    table.increments()
    table.text('title')
    table.text('description')
    table.float('rating')
    table.text('url')
    })
    }

    exports.down = function(knex, Promise) {
    return knex.schema.dropTable('sticker')
    }
    Run the migration
    knex migrate:latest
    Go into psql to make sure it worked
    psql named-database
    This will take you to the database. Now list it to see:
    \dt
    \d sticker
    Seed sticker table with sample data

    Best practice to use numbers to begin your seed names because they run in alphabetical order.
    In larger projects you'll have multiple seeds and will want them to run in order.
    knex seed:make 01_sticker
    This created the seed folder and the seed file, in that file make the edits follwing.
    The idea behind seeding is that everytime you run the seed the database will go back to the same state
    exports.seed = function(knex, Promise) {
    return knex('sticker').del()
    .then(function () {
    return knex('sticker').insert(stickers)
    })
    }
    In your stickers.js file, you want to make sure that your data is an object that looks like the table you created. Should have module.exports = at the top.
    In your stickers file, you need to pull that array of objects in. When we run the seed, it will drop the rows and then insert the array of stickers. It should look like:
    const stickers = require('../stickers')
    Then run:
    knex seed:run
    While in the database next to the named database you should be able to run:
    select * from sticker
    NOTE: \x will do an expanded display if you run select again.
    Convert Express App to JSON API

    Remove view rendering

    When an express app is generated by default, it comes with a "view" engine (jade / handlebars). Navigate into the app.js file and remove the view engine setup:
    // view engine setup
    app.set('views', path.join(__dirname, 'views'))
    app.set('view engine', 'jade')
    Delete "views" folder since it's not needed
    Remove routes folder

    Delete the 'routes' folder because we will make our own
    In app.js remove these lines where the routes are used:
    var index = require('./routes/index')
    var users = require('./routes/users')

    app.use('/', index)
    app.use('./users', users)
    Remove static serve and public folder

    In the app.js file, remove line 19 or the line that says:
    app.use(express.static(path.join(__dirname, 'public')))
    We delete this because it serves static files from the public folder.
    Delete the 'public' folder.
    Update error handler

    At the bottom of app.js by default, it's trying to render the error page, instead we want to send back some json. Remove: `res.render('error')
    Add:
    res.json({
    message: err.message,
    error: req.app.get('env') === 'development' ? err : {}
    })
    Now you can remove the 2 lines above:
    res.locals.message = err.message
    res.locals.error = req.app.get('env') === 'development' ? err : {}
    Now whenever the server has an error it will send it back as json
    To verify everything is working run npm install to make sure it has the latest dependencies.
    If you are not using a view engine, uninstall it by npm uninstall jade. This removes it from package.json
    run npm start to make sure it starts
    It should be working if you go to localhost:3000 in the browser and see the message not found object since there are no more routes.
    Add api folder and create/mount router

    This is for our crud routes and our stickers
    Inside the server folder, create a new folder named 'api'
    Inside of the api folder, create a new file named 'stickers.js'
    Inside of the stickers.js file we need to create an express router:
    const express = require('express')

    const router = express.Router()

    // This does a simple route to make sure its working
    router.get('/', (req, res) => {
    res.json({
    message: 'working'
    })
    })

    module.exports = router
    Now we need to mount the router at this url '/api/v1/sticker' so every route inside of this file begins with this url.
    Inside app.js:
    // Change all of the cars to const from var express down to var app
    // Bring in stickers after const app = express()
    const stickers = require('./a[i/stickers')
    This is where we now mount the route. NOTE: It has to be exactly between:
    app.use(logger('dev'))
    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extedned: false})
    app.use(cookieParser())

    // PUT THE MOUNT HERE:
    app.use('/api/v1/stickers', stickers)

    app.use(function(req, res, next) {
    const err -new Error('Not Found')
    err.status = 404
    next(err)
    })
    Now when you make a request from the mount, it's going to go into the router filer (sitckers.js) and runs the request handler
    Install nodemon as a development dependency npm install nodemon --save-dev
    FUN FACT: nodemon allows you to start up your server and watch it for files changes and restart it everytime a file changes
    In package.json add it in the "scripts" object under "start" like: "dev": "nodemon"
    In the terminal start up nodemon npm run dev
    If all is correct, in the browser you can add the stickers url we made. localhost:3000/api/v1/stickers and you should see the message we created
    Connect to the database'

    This will require any module in our app and make queires against the database

    Create database connection file

    Create a folder in our server folder named 'db' and now create a file inside of db named 'knex.js'
    In the knex.js file we need to get the environment. This code, wherever it's running, will be connecting to a different database so when it connects on your machine, it will use the development connection. When you're on production their will be a different way to connect.
    const environment = process.env.NODE_ENV || 'development'
    Now add the knex config
    const config = (require('../knexfile')
    const environmentConfig = config[environment]
    const knex = require('knex')
    const connection = knex(environmentConfig)
    module.exports = connection
    Create a queries file

    This will create one place where our queires can go
    In the DB folder, create a new file named 'queries.js'.
    Inside queries.js we need to require our connection to the db. So we require the knex file:
    const knex = require('./knex') // the connection

    module.exports = {
    getAll(){
    // this is a function that will get all the stickers from the sitcker table
    }
    }
    List all records with GET /api/v1/stickers

    Create query

    Create route

    Now we will create a get route to list all of our stickers in our database
    Go into the api folder and go into stickers.js:
    const express = require('express')

    const router = express.Router()

    // require our queries
    const queries = require('../db/queries')

    // This is the route where we want to list all of the stickers. The actual query to do that will be in another file so we need to bring that into here
    router.get('/', (req, res) => {
    queries.getAll().then(stickers => {
    res.json(stickers)
    })
    })

    module.exports = router
    In our queries.js file, we need to write out the getAll function
    const knex = require('./knex) //the connection!

    module.exports = {
    getAll() {
    return knex('sticker') // this selects all the rows in the sticker table
    }
    }
    If we now enter localhost:3000/api/1/stickers We should get our data from our database and sending it back as json.
    Recap: queries.js creates the getAll method, the router file calls that method, we take the data through the promise and send it back as json
    Setup tests

    Install mocha, chai and supertest

    This command will save the tests as development dependencies because when the app is in production, we don't need the testing libraries.

    npm install --save-dev mocha chai supertest

    Add a test database connection

    Head to knex.js file

    module.exports= {
    development: {
    client: 'pg',
    connection: 'postgres://localhost/named-database'
    },
    development: {
    client: 'pg',
    connection: 'postgres://localhost/test- named-database'
    }
    };
    ```
    When we run these tests, they will run against a different database

    Add npm test script

    Create test directory and test file

    In server folder mkdir test cd test touch app.test.js

    In app.test.js:

    describe('CRUD Stickers', () => {

    })
    In the package.json add "test": "NODE_ENV=test mocha" to the "scripts" object.

    If you think back to the db connection we created in knex.js, we're using the test object in the knexfile.js file instead of the development connection because we utilized NODE_ENV in the knex.js file.

    Drop/Create database

    Before mocha runs, however, we want to drop the database and create it from scratch

    So continue to add to this test value:

    "test": "(dropdb --if-exists test-named-database && createdb test-named-database) && NODE_ENV=test mocha"

    If we run dropdb --help we can use the option --if-exists because the first time you run it, it won't exist, so it won't try dropping something that doesn't exist.

    Create before

    In app.test.js:

    // bring in our database connection
    const knex = require('')

    describe('CRUD Stickers', (../db/knex) => {
    // this function will run before any of the test below it are defined.
    before(() => {
    // run migrations
    // run seeds
    })
    })
    Run migrations/seeds on test db
    In app.test.js:
    // bring in our database connection
    const knex = require('')

    describe('CRUD Stickers', (../db/knex) => {
    // this function will run before any of the test below it are defined.
    before(() => {
    // run migrations
    knex.migrate.latest()
    .then(() => {
    // run seeds
    return knex.seed.run()
    })

    })
    })
    Make sure the tests are working!

    We need to tell mocha when the migration and seeds are done running using the built in done function we can call.

    // bring in our database connection
    const knex = require('')

    describe('CRUD Stickers', (../db/knex) => {
    // this function will run before any of the test below it are defined.
    // add built in mocha done function
    before((done) => {
    // run migrations
    knex.migrate.latest()
    .then(() => {
    // run seeds
    return knex.seed.run()
    //invoke done function
    }).then(() => done())
    })
    // have basic test to check if the before is creating the tables and seeding the databse

    })
    List all records with GET /api/v1/stickers
    Add test
    Show one record with GET /api/v1/stickers/:id
    Validate id
    Create query
    Create route
    Add test
    Create a record with POST /api/v1/stickers
    Create route
    Validate sticker!
    Create query
    Add test
    Update a record with PUT /api/v1/stickers/:id
    Create route
    Validate id
    Validate updates
    Create query
    Add test
    Delete a record with DELETE /api/v1/stickers/:id
    Create route
    Validate id
    Create query
    Add test