{"id":20603466,"url":"https://github.com/danpoynor/sql-library-book-manager","last_synced_at":"2026-04-14T05:31:11.400Z","repository":{"id":42059142,"uuid":"471147852","full_name":"danpoynor/sql-library-book-manager","owner":"danpoynor","description":"Demo web application for managing a collection of books including pages to list, add, update, and delete books using JavaScript, Node.js, Express, Pug, SQLite and the SQL ORM Sequelize. App also includes pagination and search with filter features.","archived":false,"fork":false,"pushed_at":"2023-02-25T00:26:44.000Z","size":100,"stargazers_count":2,"open_issues_count":6,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-06T16:47:50.265Z","etag":null,"topics":["database","express","javascript","node-js","orm","pug","sequelize","sqlite"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/danpoynor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-03-17T21:27:50.000Z","updated_at":"2023-07-10T22:11:03.000Z","dependencies_parsed_at":"2024-11-16T09:17:29.166Z","dependency_job_id":"ba467e79-1f50-4518-a95c-0ccc09b5129a","html_url":"https://github.com/danpoynor/sql-library-book-manager","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/danpoynor/sql-library-book-manager","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danpoynor%2Fsql-library-book-manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danpoynor%2Fsql-library-book-manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danpoynor%2Fsql-library-book-manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danpoynor%2Fsql-library-book-manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/danpoynor","download_url":"https://codeload.github.com/danpoynor/sql-library-book-manager/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/danpoynor%2Fsql-library-book-manager/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31784251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-14T02:24:21.117Z","status":"ssl_error","status_checked_at":"2026-04-14T02:24:20.627Z","response_time":153,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","express","javascript","node-js","orm","pug","sequelize","sqlite"],"created_at":"2024-11-16T09:17:22.272Z","updated_at":"2026-04-14T05:31:11.380Z","avatar_url":"https://github.com/danpoynor.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SQL Library Book Manager\n\nDemo web application for managing a collection of books including pages to list, add, update, and delete books using JavaScript, Node.js, Express, Pug, SQLite and the SQL ORM Sequelize. App also includes pagination and search with filter features.\n\n---\n\n## Running the App\n\nAssuming you have `node` and `npm` already installed globally on your system - clone this repo, `cd` into the app folder, then install dependencies:\n\n```bash\ngit clone https://github.com/danpoynor/sql-library-book-manager.git\ncd sql-library-book-manager/\nnpm install\n```\n\nOn MacOS or Linux, run the app with this command:\n\n```bash\nnpm run start\n```\n\nor, run in dev mode to view `debug()` messages in your console and have `nodemon` restart the app when file updates are saved:\n\n```bash\nnpm run start:dev\n```\n\nThen load \u003chttp://localhost:3000/\u003e in your browser to access the app.\n\n---\n\n## Reset Database\n\nAfter creating, updating, or deleting entries, you can reset the database if needed by running the following command:\n\n```bash\nnpm run db:reset\n```\n\nThis will run scripts located in `db/migrations` to recreate `Authors`, `Genres`, and `Books` tables in the database then populate them with the default data from `db/seeders`.\n\n---\n\n## Screenshots\n\n\u003cdetails\u003e\n\u003csummary\u003eShow/hide\u003c/summary\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"1252\" title=\"SQL Library Book Manager: Home Page\" alt=\"SQL Library Book Manager: Home Page\" src=\"https://user-images.githubusercontent.com/764270/163472474-f7273aaf-8a50-42ef-9343-6424f43b2755.png\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"1250\" title=\"SQL Library Book Manager: New Book Page\" alt=\"SQL Library Book Manager: New Book Page\" src=\"https://user-images.githubusercontent.com/764270/163472598-33724f8b-26c0-453a-997b-658584a81b0f.png\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"1249\" title=\"SQL Library Book Manager: Book Details Page\" alt=\"SQL Library Book Manager: Book Details Page\" src=\"https://user-images.githubusercontent.com/764270/163472612-1d54cd5f-0d41-4aa6-91d6-57ecc0149592.png\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"1249\" title=\"SQL Library Book Manager: Delete Book Page\" alt=\"SQL Library Book Manager: Delete Book Page\" src=\"https://user-images.githubusercontent.com/764270/163472621-428fed23-db4c-4e64-bf5d-5bd22cfe8579.png\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003cimg width=\"1249\" title=\"SQL Library Book Manager: Search Results Page\" alt=\"SQL Library Book Manager: Search Results Page\" src=\"https://user-images.githubusercontent.com/764270/163472636-96168c3d-ec4e-48d7-9812-ad408dcd6010.png\"\u003e\n\u003c/p\u003e\n  \n\u003c/details\u003e\n\n---\n\n## Tech Stack\n\n- [Express](https://expressjs.com/) - Fast, unopinionated, minimalist web framework for [Node.js](http://nodejs.org/).\n- [node-sqlite3](https://github.com/TryGhost/node-sqlite3) - Asynchronous, non-blocking [SQLite3](https://sqlite.org/) bindings for [Node.js](http://nodejs.org/).\n- [Sequelize](https://sequelize.org/master/manual/getting-started.html) - Modern TypeScript and Node.js ORM for Postgres, MySQL, MariaDB, SQLite and SQL Server, and more.\n- [Pug](https://pugjs.org/api/getting-started.html) - A high-performance template engine heavily influenced by [Haml](http://haml.info/)\nand implemented with JavaScript for [Node.js](http://nodejs.org) and browsers.\n- [Luxon](https://moment.github.io/luxon/#/) - Date and time library (alternative to [Moment.js](https://moment.github.io/luxon/#/why?id=place-in-the-moment-project))\n- [nodemon](https://www.npmjs.com/package/nodemon) - A tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.\n- [http-errors](https://github.com/jshttp/http-errors) - A set of HTTP error classes for Node.js.\n\n---\n\n## Sequelize Features Used Highlights\n\n- [Validations \u0026 Constraints](https://sequelize.org/docs/v6/core-concepts/validations-and-constraints/) - Used to validate data before saving to the database.\n- [Associations](https://sequelize.org/docs/v6/core-concepts/assocs/) - Used to create relationships between models including [advanced-many-to-many](https://sequelize.org/docs/v6/advanced-association-concepts/advanced-many-to-many/) relationships using a junction table.\n- [Eager Loading](https://sequelize.org/docs/v6/advanced-association-concepts/eager-loading/) - Used to load associated models in a single query (e.g. `Book.getAuthor()` creates SQL [joins](https://en.wikipedia.org/wiki/Join_(SQL)) under the hood).\n- [Migrations](https://sequelize.org/docs/v6/other-topics/migrations/) - Used to generate database tables and initialize models instead of [sync()](https://sequelize.org/docs/v6/core-concepts/model-basics/#model-synchronization). More info [1](https://stackoverflow.com/questions/41595755/sequelize-sync-vs-migrations), [2](https://stackoverflow.com/questions/21105748/sequelize-js-how-to-use-migrations-and-sync#answer-29941038)\n- [Seed Data](https://sequelize.org/docs/v6/other-topics/migrations/#creating-the-first-seed-data) - Used to populate the database with initial and repopulate default data in the database.\n- [.sequelizerc](https://sequelize.org/docs/v6/other-topics/migrations/#the-sequelizerc-file) file - Used to customize the Sequelize configuration by overriding the default path to `migrations`, `models`, `seeders` and `config` folder for better project organization.\n- [Sub Queries](https://sequelize.org/docs/v6/other-topics/sub-queries/) - Used to order Authors list by associate Book count.\n- [Virtual fields](https://sequelize.org/docs/v6/core-concepts/assocs/#basics-of-queries-involving-associations) - A field that is created with `type: DataTypes.VIRTUAL` that doesn't exist in the database (it's not an actual column), but it can be used as a real column.\n- [SQLite Specific Dialect](https://sequelize.org/docs/v6/other-topics/dialect-specific-things/#sqlite) - SQLite doesn't require host, username, or password to connect to the database.\n\nThe reason search results aren't paginated: Due to an issue in Sequelize preventing `limit` from working when querying models that have `many-to-many` associations ([StackOverflow](https://stackoverflow.com/questions/65803974/)), I created a special `/search` route that doesn't include the `paginator` template .\n\nInstead of using a two-way `belongsToMany` association for `Books` and `Genres`, I might investigate using `hasMany()` with `separate: true` ([StackOverflow](https://stackoverflow.com/questions/53186006/how-can-i-use-limit-in-include-model-using-sequelize)). Or look into using [Knex](http://knexjs.org/) or [TypeORM](https://typeorm.io/) instead of Sequelize.\n\n---\n\n## Database Structure\n\nThe SQLite database file `library.db` is [normalized](https://en.wikipedia.org/wiki/Database_normalization) to include four relational tables (models) instead of having just one table that contains all the data.\n\n```js\n// Sequelize associations\nAuthor.hasMany(models.Book); \nBook.belongsTo(models.Author);\nBook.belongsToMany(models.Genre, { through: models.BookGenres });\nGenre.belongsToMany(models.Book, { through: models.BookGenres });\n```\n\nTable columns:\n\n- `Books`: Id, Title, Author, Year\n- `Authors`: Id, First Name, Last Name, Birthday, Date of Death\n- `Genres`: Id, Name, Description\n- `BookGenres`: A 'join table' for the two-way many-to-many relationship between books and genres. This table includes foreign keys to the books and genres tables.\n\nSchema screenshot:\n\n\u003cp align=\"center\"\u003e\n\u003cimg width=\"664\" alt=\"sql-library-express-db-schema-danpoynor\" src=\"https://user-images.githubusercontent.com/764270/163473788-10faca7c-4a78-40dc-bfd6-58050b4b2336.png\"\u003e\n\u003c/p\u003e\n\nIn the diagram, the `SequelizeMeta` table is automatically created when using [migrations](https://sequelize.org/docs/v6/other-topics/migrations/) so Sequelize can track which migrations have been applied to the database (version control for databases).\n\n---\n\n## Potential TODOs\n\n- Deleting a genre or author currently also deletes any associated books. Might experiment showing a warning about that before deleting anything, or perhaps set the related fields to `null`.\n- Consider using 'page' slug in url instead of 'limit' and 'offset' in paginator.\n- Do accessibility testing.\n- Do performance testing.\n- Add unit tests.\n- Refactor Search result highlighting in search.js. It's hacky and feels like something that could be done on the Express side.\n- Investigate using [transactions](https://sequelize.org/docs/v6/other-topics/transactions/) to make the code more production ready.\n- Set default options `underscored: true` and `timestamps: false` globally when declaring the `sequelize` object using `const sequelize = new Sequelize(...);` rather than declaring in each model definition ([more info](https://sequelize.org/api/v6/class/src/sequelize.js~sequelize#instance-constructor-constructor)). Couldn't get this to work right off.\n- Figure out how to log search queries to a table in the database to see what folks search for, how many searches are being made, how often, ...\n- In case users edit the `limit` and `offset` in the paginator, redirect them to the first/last page if too low/high.\n- Check if paginator `limit=0\u0026offset=0` causes and error.\n- Check out alternative ORMs such as [KNEX](http://knexjs.org/) and [TypeORM](https://typeorm.io/).\n- Clean up and optimize CSS. Perhaps integrate Dart Sass.\n- Any other thoughts to improve contact danpoynor@gmail.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanpoynor%2Fsql-library-book-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdanpoynor%2Fsql-library-book-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdanpoynor%2Fsql-library-book-manager/lists"}