{"id":22352367,"url":"https://github.com/bgoonz/express_js_notes","last_synced_at":"2026-04-12T09:10:33.363Z","repository":{"id":171469336,"uuid":"647976211","full_name":"bgoonz/EXPRESS_JS_NOTES","owner":"bgoonz","description":"Notes on node, express, sql, Sequelize, MongoDB, authentication, graphQL","archived":false,"fork":false,"pushed_at":"2024-08-31T15:33:17.000Z","size":1292,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-31T13:16:07.467Z","etag":null,"topics":["authentication","express","graphql","mongodb","nodejs","sequelize","sql"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"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/bgoonz.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}},"created_at":"2023-06-01T00:02:02.000Z","updated_at":"2024-08-31T15:33:11.000Z","dependencies_parsed_at":"2024-03-31T17:30:58.883Z","dependency_job_id":"309a6e75-bc7a-41c0-b5ad-a7f8fdc81b43","html_url":"https://github.com/bgoonz/EXPRESS_JS_NOTES","commit_stats":null,"previous_names":["bgoonz/nodejs_notes"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bgoonz%2FEXPRESS_JS_NOTES","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bgoonz%2FEXPRESS_JS_NOTES/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bgoonz%2FEXPRESS_JS_NOTES/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bgoonz%2FEXPRESS_JS_NOTES/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bgoonz","download_url":"https://codeload.github.com/bgoonz/EXPRESS_JS_NOTES/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245650495,"owners_count":20650105,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["authentication","express","graphql","mongodb","nodejs","sequelize","sql"],"created_at":"2024-12-04T12:18:19.188Z","updated_at":"2026-04-12T09:10:33.320Z","avatar_url":"https://github.com/bgoonz.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NodeJS\n\n\u003e Remove node modules:\n\n```bash\nfind . -name 'node_modules' -type d -prune -exec rm -rf '{}' +\n```\n\n**Main Core Modules**\n\n- http - create a web server, send requests, etc.\n- https - create a secure web server, send requests, etc.\n- fs - file system, read/write files, etc.\n- path - path to files, etc.\n- os - operating system, etc.\n  **http.createServer()** - create a web server\n\n```js\nconst http = require(\"http\");\nconst server = http.createServer((req, res) =\u003e {\n  console.log(req);\n});\n```\n\n**server.listen()** - listen to a port\n\n```js\nconst http = require(\"http\");\nconst server = http.createServer((req, res) =\u003e {\n  console.log(req);\n});\nserver.listen(3000);\n```\n\n- the server.listen() method takes a port number as an argument and will keep the server running while it listens for requests on that port number.\n\n  **Node.js Program Lifecycle**\n\n- Node.js is a single-threaded application, but it can support concurrency via the concept of event and callbacks.\n- the **Event Loop** is a single thread that performs all I/O operations asynchronously.\n\n- The function we pass to createServer is an event listener, and the server object will emit events when a request is made, but it will not execute the event listener function right away. Instead, it will wait for the event loop to be free, and then it will execute the event listener function.\n\n  `process.exit();` - exit the event loop... this is not recommended, but it is possible, and will terminate the program.\n\n  `response.setHeader( \"Content-Type\", \"text/html\" );` - set the header of the response, this represents the type of data that is being sent back to the client.\n\n  `response.write( \"\u003ch2\u003e Hello \u003c/h2\u003e\" );` - write data to the response, this is the data that will be sent back to the client.\n\n  `response.end();` - end the response, this will send the response back to the client.\n  **Complete Code To Send Basic HTML to Client**\n\n```js\nconst http = require(\"http\");\nconst server = http.createServer((request, response) =\u003e {\n  console.log(request.url, request.method, request.headers);\n  response.setHeader(\"Content-Type\", \"text/html\");\n  response.write(\"\u003chtml\u003e\");\n  response.write(\"\u003chead\u003e\u003ctitle\u003eMy First Page\u003c/title\u003e\u003c/head\u003e\");\n  response.write(\"\u003cbody\u003e\u003ch1\u003eHello from my Node.js Server!\u003c/h1\u003e\u003c/body\u003e\");\n  response.write(\"\u003c/html\u003e\");\n  response.end();\n});\nserver.listen(3000);\n```\n\n\u003e Shortcut to open developer tools in Chrome: `Ctrl + Shift + I`\n\u003e How to view the request in the network tab of the developer tools in Chrome:\n\u003e ![developer tools network tab](./0-images/2023-06-01-12-24-14.png)\n\n- as you can see there are some default headers set by the browser in addition to the headers we set in our code.\n\n- If we view the response we see the response body is\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003eMy First Page\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello from my Node.js Server!\u003c/h1\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n[Available Headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)\n**GET Requests** - GET requests are the most common type of request, and they are used to fetch data from a server.\n\n**POST Requests** - POST requests are used to send data to a server.\n\n**PUT Requests** - PUT requests are used to send data to a server to create or update a resource.\n\n**DELETE Requests** - DELETE requests are used to delete a resource from a server.\n\n**PATCH Requests** - PATCH requests are used to update a resource.\n\n**OPTIONS Requests** - OPTIONS requests are used to fetch information about a server.\n\n\u003e In the example below:\n\n```js\nresponse.write(\n  \"\u003cbody\u003e\u003cform action='/message' method='POST'\u003e\u003cinput type='text' name='message'\u003e\u003c/input\u003e\u003cbutton type='submit\u003e\u003c/button\u003e\u003c/form\u003e\u003c/body\u003e\",\n);\n```\n\n- The `name` set on the input does not have to be `message` , it will add any input data to the request and make it accessible via the assigned name.\n- the form action is set to `/message` which means that when the form is submitted, the data will be sent to the `/message` route.\n\n##### Streams \u0026 Buffers\n\n- Streams are a way to read data from a source or write data to a destination in a continuous fashion, they break the data up into chuncks so we can start operating on some of the data while the rest is still streaming in.\n- Buffers are a way to temporarily store data in a stream.\n- Streams are used to read or write data in chunks, and they are used to handle large amounts of data in a memory-efficient way.\n- A buffer is a data structure that allows you to hold multiple chuncks of a stream in memory and operate on them.\n\n**In the following code**\n\n```js\nconst parsedBody = Buffer.concat(body).toString();\n```\n\n- the `Buffer` object is available globally in Node.js, and it is used to construct a buffer from an array of chuncks.\n\nIn the code below:\n\n```js\nreq.on(\"end\", () =\u003e {\n  const parsedBody = Buffer.concat(body).toString();\n  console.log(parsedBody);\n  //parsedBody: message=Hello+all\n  const message = parsedBody.split(\"=\")[1].replace(/\\+/g, \" \");\n  //here the write file sync exicutes after the code that comes after it.\n  fs.writeFileSync(\"message.txt\", message);\n});\n```\n\n- the annonymous function passed to the `req.on()` method is an event listener, and it will be executed when the `end` event is emitted.\n\n- When the code reaches req.on it registers the event listeners but doesn't exicute the code in them until the event is emitted.\n\n**Blocking \u0026 Non-Blocking Code**\n\n- **Blocking code**, also known as synchronous code, executes one operation at a time and blocks further execution until the current operation is completed. When a blocking operation is encountered, the code pauses until the operation finishes, and during this time, the entire program is essentially \"blocked\" from executing other tasks.\n\n- **Non-blocking code**, also known as asynchronous code, allows multiple operations to be executed concurrently without blocking the execution of the entire program. Instead of waiting for an operation to complete, non-blocking code delegates the task to another component (e.g., the operating system or a callback function) and continues executing other tasks.\n\n**fs.writeFile vs fs.writeFileSync:**\n\n- `fs.writeFile` is an asynchronous function that allows you to write data to a file. It takes the file path, data to be written, an optional encoding (default is UTF-8), and a callback function that will be invoked once the operation is completed. Here's an example:\n\n```js\nconst fs = require(\"fs\");\n\nfs.writeFile(\"file.txt\", \"Hello, world!\", \"utf8\", (err) =\u003e {\n  if (err) {\n    console.error(err);\n    return;\n  }\n  console.log(\"Data written to file.txt\");\n});\n```\n\n`fs.writeFileSync`, on the other hand, is a synchronous function that writes data to a file. It takes the file path, data to be written, and an optional encoding. Unlike fs.writeFile, it doesn't require a callback function and returns undefined. Here's an example:\n\n```js\nconst fs = require(\"fs\");\n\ntry {\n  fs.writeFileSync(\"file.txt\", \"Hello, world!\", \"utf8\");\n  console.log(\"Data written to file.txt\");\n} catch (err) {\n  console.error(err);\n}\n```\n\nThe choice between `fs.writeFile` and `fs.writeFileSync` depends on the requirements of your application. If you need to perform other tasks or handle other events while the file is being written, then `fs.writeFile` is recommended because it is non-blocking and allows your code to continue executing. On the other hand, if writing the file is a critical operation and you want to ensure it completes before moving on, `fs.writeFileSync` can be used, but be aware that it will block the execution of further code until the file write is finished.\n\n---\n\n### Single Thread, Event Loop \u0026 Blocking Code\n\n- nodeJs uses only one thread to exicute all the code.\n- The event loop automatically starts up when a Node.js process is launched, and it is responsible for executing the code, collecting and processing event call-backs, and executing queued sub-tasks.\n- Operations that take a long time like file system operations are sent to a worker pool, which runs on different threads than your code.\n\n- The event loop keeps the nodeJs process running and handles all the callbacks and has a certain order (pending callbacks, pending timers, pending I/O, idle, prepare, poll, check, close callbacks) and it keeps looping through this order.\n\n##### Final Code for the server\n\n\u003e routes.js\n\n```js\nconst fs = require(\"fs\");\nfunction requestHandler(req, res) {\n  const url = req.url;\n  const method = req.method;\n  if (url === \"/\") {\n    res.write(\"\u003chtml\u003e\");\n    res.write(\"\u003chead\u003e\u003ctitle\u003eEnter Message\u003c/title\u003e\u003chead\u003e\");\n    res.write(\n      '\u003cbody\u003e\u003cform action=\"/message\" method=\"POST\"\u003e\u003cinput type=\"text\" name=\"message\"\u003e\u003cbutton type=\"submit\"\u003eSend\u003c/button\u003e\u003c/form\u003e\u003c/body\u003e',\n    );\n    res.write(\"\u003c/html\u003e\");\n    return res.end();\n  }\n  if (url === \"/message\" \u0026\u0026 method === \"POST\") {\n    const body = [];\n    req.on(\"data\", (chunk) =\u003e {\n      console.log(chunk);\n      body.push(chunk);\n    });\n    return req.on(\"end\", () =\u003e {\n      const parsedBody = Buffer.concat(body).toString();\n      console.log(parsedBody);\n      //parsedBody: message=Hello+all\n      const message = parsedBody.split(\"=\")[1].replace(/\\+/g, \" \");\n      //here the write file sync exicutes after the code that comes after it.\n      fs.writeFileSync(\"message.txt\", message);\n      res.statusCode = 302;\n      res.setHeader(\"Location\", \"/\");\n      return res.end();\n    });\n  }\n  res.setHeader(\"Content-Type\", \"text/html\");\n  res.write(\"\u003chtml\u003e\");\n  res.write(\"\u003chead\u003e\u003ctitle\u003eMy First Page\u003c/title\u003e\u003chead\u003e\");\n  res.write(\"\u003cbody\u003e\u003ch1\u003eHello from my Node.js Server!\u003c/h1\u003e\u003c/body\u003e\");\n  res.write(\"\u003c/html\u003e\");\n  res.end();\n}\nmodule.exports = { handler: requestHandler };\n//Or alternatively:\n// module.exports.handler = requestHandler;\n```\n\n\u003e app.js\n\n```js\nconst http = require(\"http\");\nconst routes = require(\"./routes\");\n\nconst server = http.createServer(routes.handler);\n\nserver.listen(3000);\n```\n\n###### Closing Notes\n\n- Node.js runs non-blocking code and uses the event loop for running your logic.\n- A node program exits as soon as there is no more work to do.\n- The `createServer()` event never finishes by default.\n\n### Useful resources:\n\n- Official Node.js Docs: \u003chttps://nodejs.org/en/docs/guides/\u003e\n\n- Full Node.js Reference (for all core modules): \u003chttps://nodejs.org/dist/latest/docs/api/\u003e\n\n- More about the Node.js Event Loop: \u003chttps://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/\u003e\n\n- Blocking and Non-Blocking Code: \u003chttps://nodejs.org/en/docs/guides/dont-block-the-event-loop/\u003e\n\n---\n\n---\n\n## Express.js\n\n[Express.js Docs](https://expressjs.com/en/4x/api.html)\n[Express API Notes](./00-notes/express.md)\n\n- Express.js is useful for middleware, routing, and templating.\n- in each middleware you can either send a response `res.send()` or call the next middleware `next()`.\n\n**To use a middleware:**\n\n```js\napp.use((req, res, next) =\u003e {});\n```\n\n- the function you pass to app.use will be exicuted for every incoming request.\n- the next function which gets passed as the third argument to the function you pass to app.use has to be called to allow the request to move on to the next middleware.\n\n**Res.send() vs Res.write():**\n\n- `res.send()` is often used with frameworks like Express.js and provides a higher-level abstraction for sending responses. It simplifies the process of setting headers and handling different response types.\n- `res.write()` is a lower-level method and is typically used in raw Node.js HTTP server implementations. It requires more manual work to set headers and handle various response aspects.\n\n```js\napp.listen(3000);\n```\n\n\u003e is the same as:\n\n```js\nconst server = http.createServer(app);\n\nserver.listen(3000);\n```\n\n\u003e If you call `res.send` ... that is a pretty good indication that you do not want to call the `next()` function. Because if you call `res.send` that will send a response to the client and the client will not wait for the next middleware to be executed.\n\n`res.redirect()` is used to redirect the user to another page.\n\n**How to serve shop.html from the views folder**\n\n```js\nconst express = require(\"express\");\nconst path = require(\"path\");\nconst router = express.Router();\n\nrouter.get(\"/\", (req, res, next) =\u003e {\n  res.sendFile(path.join(__dirname, \"../\", \"views\", \"shop.html\"));\n});\n\nmodule.exports = router;\n```\n\n##### How to serve static files\n\n```js\napp.use(express.static(path.join(__dirname, \"public\")));\n```\n\n---\n\n## Templating Engines\n\n`app.set()` is used to set a global configuration value, which is then stored in Express and can be retrieved using `app.get()`.\n\n### Pug\n\n- Indentation matters in pug.\n\n\u003e How to render pug (in shop.js)\n\n```js\nconst path = require(\"path\");\n\nconst express = require(\"express\");\n\nconst rootDir = require(\"../util/path\");\nconst adminData = require(\"./admin\");\n\nconst router = express.Router();\n\nrouter.get(\"/\", (req, res, next) =\u003e {\n  console.log(\"shop.js\", adminData.products);\n  res.render(\"shop\", {\n    prods: adminData.products,\n    pageTitle: \"Shop\",\n    path: \"/\",\n    hasProducts: adminData.products.length \u003e 0,\n    activeShop: true,\n    productCSS: true,\n  });\n});\n\nmodule.exports = router;\n```\n\n##### We don't need to specify the extension of the file in the render method because we already set the view engine to pug in app.js\n\n- to output dynamic data in pug we use `#{}`\n\n```pug\nhead\n        meta(charset=\"UTF-8\")\n        meta(name=\"viewport\", content=\"width=device-width, initial-scale=1.0\")\n        title #{docTitle}\n        link(rel=\"stylesheet\", href=\"/css/main.css\")\n        link(rel=\"stylesheet\", href=\"/css/product.css\")\n    body\n```\n\n### [Converting HTML to PUG](./03-templating-engines/NOTES.md)\n\n**Using Layouts in Pug**\n\n- Layouts are used to avoid repeating code in pug files.\n- We start by creating a generalized html page in out `views/layouts/main-layout.pug` file.\n\n```pug\ndoctype html\nhtml(lang=\"en\")\n    head\n        meta(charset=\"UTF-8\")\n        meta(name=\"viewport\", content=\"width=device-width, initial-scale=1.0\")\n        title Page Not Found\n        link(rel=\"stylesheet\", href=\"/css/main.css\")\n        block styles\n    body\n        header.main-header\n            nav.main-header__nav\n                ul.main-header__item-list\n                    li.main-header__item\n                        a(href=\"/\") Shop\n                    li.main-header__item\n                        a(href=\"/admin/add-product\") Add Product\n\n        block content\n```\n\n- And then we can use the layout to (for example) create our 404.pug file.\n\n```pug\nextends layouts/main-layout.pug\n\nblock content\n    h1 Page Not Found!\n```\n\n- We can also use it like so to create our add-product.pug file.\n\n```pug\nextends layouts/main-layout.pug\n\n\nblock styles\n    link(rel=\"stylesheet\", href=\"/css/main.css\")\n    link(rel=\"stylesheet\", href=\"/css/product.css\")\n\nblock content\n    main\n        form.product-form (action=\"/admin/add-product\", method=\"POST\")\n            div.form-control\n                label (for=\"title\") Title\n                input (type=\"text\", name=\"title\")#title\n            button.btn (type=\"submit\") Add Product\n```\n\n---\n\n### Setting Up Express Handlebars\n\n**Installing Express Handlebars**\n\n```bash\nnpm install --save express-handlebars@3.0\n```\n\n\u003e In app.js\n\n```js\nconst expressHbs = require(\"express-handlebars\");\napp.engine(\"handlebars\", expressHbs());\n\napp.set(\"view engine\", \"handlebars\");\n```\n\n**The way you pass data into templates does not change with respect to the template engine you are using**\n\n\u003e So the following works for any template engine:\n\n```js\nres.render(\"add-product\", {\n  pageTitle: \"Add Product\",\n  path: \"/admin/add-product\",\n});\n```\n\n- Handle bars works with normal html and the way we pass values from the route into the template is as follows.\n\n```html\n\u003ctitle\u003e{{pageTitle}}\u003c/title\u003e\n```\n\n---\n\n### EJS\n\n- Ejs is a templating engine... that (like pug) works out of the box.\n\n**How we get dynamic route parameters in EJS**\n\n```ejs\n\u003ctitle\u003e\u003c%=pageTitle %\u003e\u003c/title\u003e\n```\n\n- You can write regular javascript in ejs like so:\n\n```ejs\n\u003cmain\u003e\n    \u003c% if(prods.length \u003e 0) { %\u003e\n    \u003cdiv class=\"grid\"\u003e\n      \u003carticle class=\"card product-item\"\u003e\n        \u003cheader class=\"card__header\"\u003e\n          \u003ch1 class=\"product__title\"\u003eGreat Book\u003c/h1\u003e\n        \u003c/header\u003e\n        \u003cdiv class=\"card__image\"\u003e\n          \u003cimg src='...'/\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"card__content\"\u003e\n          \u003ch2 class=\"product__price\"\u003e$19.99\u003c/h2\u003e\n          \u003cp class=\"product__description\"\u003eA very interesting book about so many even more interesting things!\u003c/p\u003e\n        \u003c/div\u003e\n        \u003cdiv class=\"card__actions\"\u003e\n          \u003cbutton class=\"btn\"\u003eAdd to Cart\u003c/button\u003e\n        \u003c/div\u003e\n      \u003c/article\u003e\n    \u003c/div\u003e\n    \u003c% }else{ %\u003e\n\n    \u003ch1\u003eNo products found\u003c/h1\u003e\n    \u003c% } %\u003e\n  \u003c/main\u003e\n```\n\n**How to include partials using EJS**\n\n```html\n\u003c%- include('includes/head.ejs') %\u003e\n\n  \u003c/head\u003e\n\n  \u003cbody\u003e\n    \u003cheader class=\"main-header\"\u003e\n      \u003cnav class=\"main-header__nav\"\u003e\n        \u003cul class=\"main-header__item-list\"\u003e\n          \u003cli class=\"main-header__item\"\u003e\u003ca  href=\"/\"\u003eShop\u003c/a\u003e\u003c/li\u003e\n          \u003cli class=\"main-header__item\"\u003e\n            \u003ca href=\"/admin/add-product\"\u003eAdd Product\u003c/a\u003e\n          \u003c/li\u003e\n        \u003c/ul\u003e\n      \u003c/nav\u003e\n    \u003c/header\u003e\n    \u003ch1\u003ePage Not Found!\u003c/h1\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n\n```\n\n- The code will render the html from head.ejs into the html file.\n\n---\n\n---\n\n## Model View Controller (MVC)\n\n- The view is the UI that your user interacts with.\n- The model is a representation of the data in your code (allows you to work with your data... i.e. fetch \u0026 save)\n- The controllers connect models and views... they contain the in between logic.\n  - controllers are often split across middleware functions.\n  - Controllers are a type of middleware.\n\nThe following is an example of controller logic:\n\n```js\nrouter.get(\"/\", (req, res, next) =\u003e {\n  const products = adminData.products;\n  res.render(\"shop\", {\n    prods: products,\n    pageTitle: \"Shop\",\n    path: \"/\",\n    hasProducts: products.length \u003e 0,\n    activeShop: true,\n    productCSS: true,\n  });\n});\n```\n\n#### Models:\n\n- A model is a representation of a data structure. A model contains the data, logic, and rules of the application. Unlike the view, it also knows about the database.\n\n\u003e The product model:\n\n```js\nconst products = [];\n\nmodule.exports = class Product {\n  constructor(title) {\n    this.title = title;\n  }\n  save() {\n    products.push(this);\n  }\n\n  static fetchAll() {\n    return products;\n  }\n};\n```\n\n- In the save method, `this` refers to the object that is created from the class.\n- The static keyword defines a static method for a class. Static methods are called without instantiating their class and cannot be called through a class instance. Static methods are often used to create utility functions for an application.\n\n\u003e products.js controller refactored to use product model:\n\n```js\nconst Product = require(\"../models/product\");\n\nexports.getAddProduct = (req, res, next) =\u003e {\n  res.render(\"add-product\", {\n    pageTitle: \"Add Product\",\n    path: \"/admin/add-product\",\n    formsCSS: true,\n    productCSS: true,\n    activeAddProduct: true,\n  });\n};\n\nexports.postAddProduct = (req, res, next) =\u003e {\n  const product = new Product(req.body.title);\n  product.save();\n  res.redirect(\"/\");\n};\n\nexports.getProducts = (req, res, next) =\u003e {\n  const products = new Product.fetchAll();\n  res.render(\"shop\", {\n    prods: products,\n    pageTitle: \"Shop\",\n    path: \"/\",\n    hasProducts: products.length \u003e 0,\n    activeShop: true,\n    productCSS: true,\n  });\n};\n```\n\n**Refactoring Save method to use file storage**\n\n```js\nsave(){\n        const filePath = path.join(rootDir, 'data', 'products.json');\n        fs.readFile(filePath, (error, fileContent)=\u003e{\n            let products = [];\n            if(!error){\n                console.log(fileContent);\n                products = JSON.parse(fileContent);\n            }\n            products.push(this);\n            fs.writeFile(filePath, JSON.stringify(products), (error)=\u003e{\n                console.log(error);\n            });\n        })\n    }\n```\n\n- In order for products.push(this) to refer to the correct object we need to use an arrow function.\n\n**How to support decimal numbers in the price input (using step)**\n\n```html\n\u003cdiv class=\"form-control\"\u003e\n  \u003clabel for=\"price\"\u003ePrice\u003c/label\u003e\n  \u003cinput type=\"number\" step=\"0.01\" name=\"price\" id=\"price\" /\u003e\n\u003c/div\u003e\n```\n\n---\n\n---\n\n## Dynamic Routes \u0026 Advanced Models\n\n**How to add a unique id to our products when the Model class is instantiated**\n\n```js\n  save() {\n    //this way is not gaurenteed to be unique but it will work for now.\n    this.id = Math.random().toString()\n    getProductsFromFile(products =\u003e {\n      products.push(this);\n      fs.writeFile(p, JSON.stringify(products), err =\u003e {\n        console.log(err);\n      });\n    });\n  }\n```\n\n```html\n\u003ca href=\"/products/\u003c%=product.id%\u003e\" class=\"btn\"\u003eDetails\u003c/a\u003e\n```\n\n---\n\n#### Dynamic Route Parameters:\n\n- When defining routes in your application, you can specify dynamic parameters in the path. These parameters are prefixed with a colon (:) and can match any value in their position within the URL. Here's an example using Express.js:\n\n```js\nrouter.get(\"/products/:productId\", (req, res) =\u003e {\n  // You can access :productId via req.params.productId\n  // This route will match any path like /products/1, /products/abc, etc.\n});\n```\n\n###### Order of Route Definitions Matters\n\n- When you have both dynamic routes and static routes that could potentially match the same URL pattern, the order in which you define your routes is crucial. Routes are evaluated in the order they are defined, and the first match will handle the request.\n\n```js\n// Dynamic route\nrouter.get(\"/products/:productId\", (req, res) =\u003e {\n  // Handles any /products/{anything} pattern\n});\n\n// Static route\nrouter.get(\"/products/delete\", (req, res) =\u003e {\n  // Intended to handle a specific case: /products/delete\n});\n```\n\n- In this setup, the route `router.get('/products/delete')` will never be reached if it's defined after the dynamic route router.get('/products/:productId'). This is because the dynamic route will match any /products/{anything} pattern, including /products/delete, and it is evaluated first.\n  - To ensure that the static route gets the chance to handle its specific case, you should define it before the dynamic route:\n\n---\n\n#### Query Parameters:\n\n- created in a url using a `?` and a key value pair seperated by an `=` sign...\n- Query parameters are components of a URL that are used to sort, filter, or customize the content or results returned by a web server. They follow the question mark (?) in a URL and are separated by ampersands (\u0026) when multiple parameters are used. Each parameter consists of a key-value pair, connected by an equals sign (=). For example, in the URL `http://example.com/posts?sort=asc\u0026category=science`, `sort=asc` and `category=science` are query parameters where `sort` and `category` are keys, and `asc` (ascending order) and `science` are their respective values. These parameters instruct the server to return posts in ascending order from the science category, allowing for a dynamic and customized user experience based on their needs or preferences.\n\n\n---\n---\n\n## SQL\n\n#### Choosing a Database(SQL vs NoSQL):\n\n- SQL is structured as tables where you have fields (columns) and we have rows or records where our data is stored.\n- SQL has a strong data schema where all data in a table has to conform to the data type of it's field.\n- SQL has relationships between data (one to one, one to many, many to many)\n\n![Tables](./images/2024-03-28-15-45-11.png)\n\n\n- NoSQL has collections with documents\n- Documents are of a similar format to a js object.\n- In NoSQL we don't have relations between collections, instead we duplicate the data.\n- Documents do not has a strictly enforced schema.\n\n\n![NoSQL](./images/2024-03-28-16-01-31.png)\n \n#### Comparing SQL \u0026 NoSQL (Horizontal vs Vertical Scaling):\n\n![Horizontal vs Vertical Scaling](./imges/2024-03-29-11-15-42.png)\n![SQL vs NoSQL](./images/2024-03-29-11-20-04.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbgoonz%2Fexpress_js_notes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbgoonz%2Fexpress_js_notes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbgoonz%2Fexpress_js_notes/lists"}