generated from btholt/next-course-starter
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
126 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
root = true | ||
|
||
[*] | ||
end_of_line = lf | ||
insert_final_newline = true | ||
charset = utf-8 | ||
indent_style = space | ||
indent_size = 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
lessons/05-build-a-project-with-nodejs-and-sqlite/A-the-example-app.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
> 🚨 Go clone the example [app repo from GitHub][app]. Once cloned, run `npm install` to install all the necessary files and then run `npm run dev` to start the development server. Please use Node.js version 20+. | ||
We are going to build an app frontend to the Chinook database. Since SQLite is file-based, I went ahead and included a fresh copy for you to use. | ||
|
||
This app uses [Fastify][fastify], [HTMX][htmx], and [Handlebars][handlebars] to make an app to render invoices for users. You won't need to touch the HTMX nor Handlebars at all, and you'll only need to write a minimal amount of Fastify. | ||
|
||
The only file you'll be working on is invoice.js. This has a bare minimal amount of Fastify code to get you started working on the route. You'll be able to try the frontend code at [http://localhost:8080](http://localhost:8080) and to hit the route directly at [http://localhost:8080/invoice?id=1](http://localhost:8080/invoice?id=1). You do not need to modify any other files. | ||
|
||
I have also included my code at invoice-complete.js. You can see the result of that function call at [http://localhost:8080/invoice-complete?id=1](http://localhost:8080/invoice-complete?id=1). | ||
|
||
> I would strongly suggest you attempt to write the code yourself first before you look how I did it. I know it can be a struggle but I find that I learn the most in those moments of struggle where I know my destination and I'm unsure of how to navigate it and I have to chart the course myself. | ||
[app]: https://github.com/btholt/sqlite-app | ||
[fastify]: https://fastify.dev/ | ||
[htmx]: https://htmx.org/ | ||
[handlebars]: https://handlebarsjs.com/ |
94 changes: 94 additions & 0 deletions
94
lessons/05-build-a-project-with-nodejs-and-sqlite/B-sqlite3.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
--- | ||
title: "sqlite3" | ||
--- | ||
|
||
There are so many libraries for connecting to SQLite. It's a very ubiquitious piece of software and so lots of people have opinions on how to build with it. | ||
|
||
I am going to show you the standard one, the first one, the one that everyone knows. I will say that when I build stuff with SQLite, it is _not_ the one I normally choose. However I think it's good for you to get exposure to the OG and then you can branch out and choose other libraries once you know what you're getting away from. | ||
|
||
[Click here][sqlite3] to go to the sqlite3 docs. | ||
|
||
> Why is it sqlite3? It's because it's for version 3 of SQLite, the third major version. | ||
## Connecting to the database | ||
|
||
Connecting to a SQLite database from Node.js is a snap. Import the the sqlite3 library and create a `new` database. | ||
|
||
```javascript | ||
import sqlite3 from "sqlite3"; | ||
|
||
const db = new sqlite3.Database("./my-database.db"); | ||
``` | ||
|
||
Again, keep in mind that SQLite databases are files. You need to give it a file path to be able to open the database and to start writing to it. | ||
|
||
## Querying the database | ||
|
||
Once you have your `db` Database instance, you can then start running queries. There's a few functions to be aware of. We'll talk about the parameters to the functions after this. | ||
|
||
- `db.run(sql, params, callback)` – This will run a query and not care about the results. You generally use this to run `UPDATE` and `DELETE` commands as you're just updating and not necessarily caring about what the database has to say back to you. | ||
- `db.get(sql, params, callback)` – Runs a query and only gives you back the first one. Some times all you need is one result (like when you're querying a unique ID). This simplifies your code a bit because you don't need an array of one result. | ||
- `db.all(sql, params, callback)` – Like get, but it gives you all results back that match the query instead of just one. It always returns an array. If you got no results, you get an empty array | ||
- `db.each(sql, params, callback, complete)` – Like all, but instead of one big array of results, your callback will get called once for each row in the set. Then the complete function will be called to let you know it's done. This is nice if you have some action you want to take on each row as it's basically a `.map()` of the result set instead of an array. | ||
|
||
## Node.js style callbacks (a.k.a. "Nodebacks") | ||
|
||
Some of you whippersnappers may be too young to remember writing JavaScript before promises, async-await, generators, etc. Before the magical time of ES2015 / ES6, we only had one way to deal with asynchronous code, callbacks. These are just functions that are invoked when something async completes. JS has since long moved past this (nearly a decade as of writing) and it's to the point that it feels weird to write callbacks as we've been writing promises so long. | ||
|
||
sqlite3 never updated to use promises so you still have to use Node.js-style callbacks (or Nodebacks, as they were sometimes called.) They are Node.js callbacks as they always have the signature of `function myCallback(error, data)`. If that `error` is populated with anything, it means that something went wrong. Otherwise it succeeded. That's it. That's what makes a "Nodeback". | ||
|
||
So with `.all()`, your code will look like this | ||
|
||
```javascript | ||
db.rows( | ||
`SELECT * FROM Track WHERE name LIKE '%live%'`, | ||
[], // we'll talk about this array in a sec | ||
function (err, rows) { | ||
if (err) { | ||
// do error stuff here | ||
return; | ||
} | ||
|
||
// do stuff here with rows | ||
} | ||
); | ||
``` | ||
|
||
## Parameters and SQL Injection | ||
|
||
Let's take this example: | ||
|
||
```javascript | ||
const id = getIdFromUser(); | ||
db.get(`SELECT * FROM Track WHERE TrackId=${id}`, [], function (err, row) { | ||
// do stuff | ||
}); | ||
``` | ||
|
||
What's wrong with this? There's a **major** problem with this. If that id is coming from a user, it means they can put _anything_ there, right? So they could in theory put valid SQL in there. What if they put `15; DROP TABLE Customer`? Then your SQL statement would be `SELECT * FROM Track WHERE id=15; DROP TABLE Customer`. Uh oh, good bye to all your customer data. They could also do even more nefarious things like steal your data. This is called SQL injection. This is why you **never, ever, ever, ever** put user input directly into a SQL statement. You _always_ let the library do it for you. No exceptions. | ||
|
||
So how do we fix this? sqlite3 gives us that array to handle just that. | ||
|
||
```javascript | ||
const id = getIdFromUser(); | ||
db.get(`SELECT * FROM Track WHERE TrackId=?`, [id], function (err, row) { | ||
// do stuff | ||
}); | ||
``` | ||
|
||
Done! By replacing it with the question mark and providing the user input in the array, sqlite3 will guarantee you that it's safe to use that no matter where it came from. You can also use an object notation. | ||
|
||
```javascript | ||
const id = getIdFromUser(); | ||
db.get( | ||
`SELECT * FROM Track WHERE TrackId=$id`, | ||
{ $id: id }, | ||
function (err, row) { | ||
// do stuff | ||
} | ||
); | ||
``` | ||
|
||
Both are fine. Question marks rely on order, objection notation relies on names matching. | ||
|
||
This should be enough of an intro for you to write your sample app. |
Empty file.
3 changes: 3 additions & 0 deletions
3
lessons/05-build-a-project-with-nodejs-and-sqlite/D-alternatives-to-sqlite3.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
title: "Alternatives to sqlite3" | ||
--- |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
{ | ||
"title": "Build a Project with Node.js and SQLite", | ||
"icon": "circle-nodes" | ||
} |