node_modules
We've learned a lot in this course:
However you've done, however the final will go: we've done a lot together.
Thank you for coming along this journey with me and our TAs.
More SQL commands and database manipulation, including:
The Node.js/SQL Connection with the mysql2
module
Where is the best place to validate user input? Why?
undefined
, and others.phpMyAdmin
(e.g., pokedexdb, or a blogsdb)CREATE TABLE
: SyntaxCREATE TABLE is used to create a new table.
Syntax:
CREATE TABLE table_name(
column1 datatype PRIMARY KEY,
column2 datatype,
column3 datatype,
.....
columnN datatype
);
SQL (template)
Each column must have a defined data type
The following are the most common:
Type | Argument | Description |
---|---|---|
INT | none | An integer number |
VARCHAR(n) | The string can be at most n characters (max of 65,535). | a text string |
DATE | none | Stores dates: YYYY-MM-DD |
DATETIME | none | Stores dates and times for precise time information: YYYY-MM-DD HH:MM:SS |
DECIMAL(d,s) | Has at most d digits, and s digits after the decimal place | A decimal number. Rounds based on the provided precision. |
TEXT | none | A potentially very large text string. |
Prefer VARCHAR to TEXT, and with as small a max limit as you are comfortable setting. This limits the amount of data someone can dump into your table.
The following are very common and useful in CREATE TABLE statements.
These are called constraints - they "constrain" the types of values you can insert in a column.
This reading on constraints is an excellent overview for more details.
Every table should have a column which is used to uniquely identify each row. This improves efficiency and will prove very useful when using multiple tables.
CREATE TABLE students(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL
);
Basic SQL Example
Adding PRIMARY KEY
makes it so that the code will error if that
column ever has duplicates. It will use that column to identify each row quickly.
This is usually an integer id, but can also be other types. Conventionally named id
or prefixed with a letter (e.g. sid
for "student id")
AUTO_INCREMENT
will make it so that, if you provide no input for that
column, it will pick the next unused value. Perfect for making it so you don't have to
worry about what the next id is.
We introduced the WPL queue to teach Forms and validation on the client-side.
What types of data might we want to store in a database (wpldb
) for the WPL Queue?
Text-based
But what about...
CREATE TABLE
exampleWhat would the CREATE TABLE
SQL command look like to create
a table to hold the queue for the WPL example?
CREATE TABLE queue(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255),
email VARCHAR(255),
student_id INT,
length TINYINT, -- e.g. 2 or 10
question VARCHAR(255), -- max of 255 characters per question
creation_time DATETIME DEFAULT NOW()
);
SQL (example)
Can be imported to execute SQL commands, often to create new tables (conventionally starting with setup
in the file name)
-- Author: Melissa Hovik
-- Last updated: 08.13.19
-- Creates a queue table of questions for the WPL database, wpldb.
CREATE TABLE queue(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255),
email VARCHAR(255),
student_id INT,
length TINYINT,
question VARCHAR(255),
creation_time DATETIME DEFAULT NOW()
);
Note: Use --
for comments in SQL (for each line).
To import a .sql file like this in phpMyAdmin, you must use the Import tab with an existing database selected (e.g., wpldb
).
CREATE TABLE
exampleThis is an example using a few different DEFAULT constraints to represent a table for blog posts.
For DATETIME columns, it is convenient to use NOW() or CURRENT_TIMESTAMP as the DEFAULT so that you don't have to calculate the date/time manually
-- Author: Andrew Fitz Gibbon
-- Last updated: 11.22.19
-- Sets up a new posts table in a blog database.
CREATE TABLE `posts` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`timestamp` DATETIME DEFAULT CURRENT_TIMESTAMP,
`author` VARCHAR(200) DEFAULT 'Fitz',
`title` VARCHAR(200) DEFAULT NULL,
`body` text,
PRIMARY KEY (`id`)
);
Note how some of this looks different than the previous CREATE TABLE. Both are valid
DROP TABLE table_name;
deletes a table
DROP TABLE IF EXISTS table_name;
deletes a table only
if it exists, which prevents an error if it doesn't
CREATE TABLE IF NOT EXISTS blog_posts( ... )
IF NOT EXISTS
option is useful if you want the table to be created only if it is not already in a database when importing/executing table creation.To insert a new record into a table, we use the INSERT INTO
keyword:
CREATE TABLE cafemenu(
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
category VARCHAR(100) NOT NULL,
description VARCHAR(256) NOT NULL,
image VARCHAR(100) DEFAULT "food.png",
qty INT DEFAULT 0
);
INSERT INTO cafemenu (name, category, description)
VALUES ("Banana", "Fresh Fruit", "A fresh test banana.");
SQL
First provide the table name, then optionally the list of columns you want to set
(by default it sets all columns). Columns left out will be set to NULL, unless they
have AUTO_INCREMENT
set.
Then provide the values for each column, which must match the column names specified.
let qry = "SELECT * FROM posts WHERE author = 'Fitz'" +
"ORDER BY timestamp DESC" +
"LIMIT 1";
let [rows, fields] = await db.query(qry);
console.log(JSON.stringify(rows));
// [{"id":5,"timestamp":"2019-11-22T20:04:23.000Z","author":"Fitz",
"title":"Yet another post","body":"OMG Another blog post about cats. I just
can't anymore. Where are the dogs?"}]
Node.js
You can find the full solution here which uses a Blog
database and posts
table
that can be populated in phpMyAdmin with this posts.csv
file (refer to yesterday's section on .csv imports)
A full-stack website consists of three layers:
The web server makes requests of the database server much like our client-side JS used AJAX
The mysql2
module will handle most of this for us.
mysql
vs. mysql2
vs. mysql2/promise
mysql
is the most popular module used to connect to a MySQL database in Node.js
Similar to the fs
module functions, mysql
functions are callback-last, with callbacks being error-first
We will use mysql2
This module has a promise version built in, like fs, but we import with: require('mysql2/promise');
Similar to promisifying fs
functions, these promise-returning functions will make it much easier to avoid callback pyramids.
mysql2
db.query(sqlString)
First install the mysql2
module in your project.
npm install mysql2
Then require the promise version with the rest of your modules in your Node.js program.
const mysql = require("mysql2/promise");
mysql.createPool
To connect to a database, you need the following information, which you can find on the MAMP start page:
We will use this configuration information to connect to MySQL and perform queries in Node.js.
/**
* Establishes a database connection to the blog database and returns the database object.
* Any errors that occur during connection should be caught in the function
* that calls this one.
* @returns {Object} - The database object for the connection.
*/
const db = mysql.createPool({
// Variables for connections to the database.
host: "localhost", // fill in with server name
port: "8889", // fill in with a port (will be different mac/pc)
user: "root", // fill in with username
password: "root", // fill in with password
database: "Blog" // fill in with db name
});
JS (example)
db
objectOnce you have the db
object, you can now execute SQL queries with db.query
This function takes a SQL query string and an optional array of options and returns the resulting rows and some metadata about the fields.
let [rows, fields] = await db.query(sqlString, [options])
template
let [rows, fields] = await db.query("SELECT name, category FROM menu ORDER BY name;");
Node.js (example)
The query function returns an array of TextRow
s, which represent
information for each row matching the query. In the below example, we limit at most 2 rows in the result.
let qry = "SELECT name, type FROM pokedex LIMIT 2;";
let [rows, fields] = await db.query(qry);
console.log(rows);
Node.js (example)
[
TextRow { name: 'Bulbasaur', type: 'grass' },
TextRow { name: 'Ivysaur', type: 'grass' }
]
output
TextRow
The column (field) names for each row (record) can be accessed using dot notation (it's just an object!)
...
let qry = "SELECT name, type FROM pokedex LIMIT 2;";
let [rows, fields] = await db.query(qry);
let firstRow = rows[0];
console.log("Name: " + firstRow.name + "(" + firstRow.type + ")");
Node.js (example)
Name: Bulbasaur (grass)
output
Note that only the column names specified in the SELECT statement will be accessible (for this example, name
and type
)
try
/catch
Database connections can have different problems: the database server could be down, the database could be missing or corrupted, the user/password credentials may be incorrect.
You can find a good review of MySQL error codes here - useful when you are debugging and/or want to handle errors
differently (similar to how we can use err.code === "ENOENT"
in our response logic)
try
/catch
helps us catch and
identify when errors occur so we can handle the error correctly.
mysql2
functions
You can try/catch just like you would for fs.readFile()
,
catching any errors that occur in the db.query
function.
try {
let rows = await db.query("SELECT name FROM pokedex"); // error could happen here
// process the result rows somehow
} catch (error) {
res.status(500).send("Something went wrong on the server. Please try again later.");
}
Node.js
What's in an error
anyways?
db.query
Code | Message | Description |
---|---|---|
ECONNREFUSED |
connect ECONNREFUSED 127.0.0.1:8889 | The MySQL server isn't running. Check MAMP to see if your servers are stopped. |
ER_NO_SUCH_TABLE |
Table 'blog.psts' doesn't exist | The table named "psts" doesn't exist in the database named "blog". Check your createPool parameters for
the blog name, the db.query SQL statement for the table name, and check phpMyAdmin to see that they all match. |
ER_BAD_FIELD_ERROR |
Unknown column 'owner' in 'field list' | MySQL couldn't find a column named 'owner'. Check your db.query SQL statement for what columns you're
referencing and check phpMyAdmin to see that they all match. |
ER_PARSE_ERROR |
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'from posts' at line 1 | You have an error in your SQL syntax; check your SQL near 'from posts'. |
Next week, we'll learn how to safely query tables that take user input (e.g. GET/POST parameters) and how to write queries that reference data from multiple tables!