{"id":25470464,"url":"https://github.com/alvinmdj/notes-app-back-end","last_synced_at":"2026-04-07T09:31:10.076Z","repository":{"id":103637219,"uuid":"469120173","full_name":"alvinmdj/notes-app-back-end","owner":"alvinmdj","description":"Notes App API (Hapi)","archived":false,"fork":false,"pushed_at":"2022-04-08T07:30:31.000Z","size":821,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-24T20:37:54.088Z","etag":null,"topics":["hapijs","jwt","nodejs","postgresql","postman","rabbitmq","redis"],"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/alvinmdj.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-12T15:21:36.000Z","updated_at":"2022-04-04T06:19:39.000Z","dependencies_parsed_at":"2023-05-24T03:45:26.078Z","dependency_job_id":null,"html_url":"https://github.com/alvinmdj/notes-app-back-end","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alvinmdj/notes-app-back-end","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinmdj%2Fnotes-app-back-end","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinmdj%2Fnotes-app-back-end/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinmdj%2Fnotes-app-back-end/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinmdj%2Fnotes-app-back-end/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alvinmdj","download_url":"https://codeload.github.com/alvinmdj/notes-app-back-end/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alvinmdj%2Fnotes-app-back-end/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31507914,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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":["hapijs","jwt","nodejs","postgresql","postman","rabbitmq","redis"],"created_at":"2025-02-18T08:34:37.295Z","updated_at":"2026-04-07T09:31:10.062Z","avatar_url":"https://github.com/alvinmdj.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Notes App Back-end (Hapi)\n\n## Links\n\n- [Notes App API Documentation](https://documenter.getpostman.com/view/16534190/UVyxPsu7)\n- [Hapi](https://hapi.dev/)\n- [ESLint](https://eslint.org/)\n- [Joi](https://joi.dev/api/)\n- [PostgreSQL](https://www.postgresql.org/)\n- [JWT](https://jwt.io/)\n- [RabbitMQ](https://www.rabbitmq.com/)\n- [Redis](https://redis.io/)\n- [Memurai (Redis alt for windows)](https://www.memurai.com/)\n- [Redis Package for JavaScript](https://www.npmjs.com/package/redis)\n- [Hapi-JWT](https://hapi.dev/module/jwt/)\n- [Hapi-Inert](https://hapi.dev/module/inert/api/?v=6.0.5)\n- [Caching in Hapi](https://hapi.dev/tutorials/caching/)\n- [AWS S3 for Node.js](https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property)\n- [AWS-SDK](https://www.npmjs.com/package/aws-sdk)\n- [Authentication with Hapi](https://hapi.dev/tutorials/auth/)\n- [amqplib](https://www.npmjs.com/package/amqplib)\n- [bcrypt](https://www.npmjs.com/package/bcrypt)\n- [node-postgres](https://node-postgres.com/)\n- [node-pg-migrate](https://www.npmjs.com/package/node-pg-migrate)\n- [PostgreSQL Best Practice](https://wiki.postgresql.org/wiki/Don't_Do_This)\n- [PostgreSQL Data Type](https://www.postgresql.org/docs/current/datatype.html)\n- [PostgreSQL Constraints](https://www.postgresql.org/docs/current/ddl-constraints.html)\n- [PostgreSQL Insert](https://www.postgresql.org/docs/current/sql-insert.html)\n- [Postman: writes pre-request scripts](https://learning.postman.com/docs/writing-scripts/script-references/postman-sandbox-api-reference/#sending-requests-from-scripts)\n- [MIME Types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types)\n- [Client app v1](http://notesapp-v1.dicodingacademy.com/)\n- [Client app v2](http://notesapp-v2.dicodingacademy.com/)\n- chrome://flags/#block-insecure-private-network-requests\n\n## Features\n\n- CRUD notes\n- Store data in database with PostgreSQL\n- Data validation with Joi\n- Error handling \u0026 custom exceptions\n- Token-based authentication with JWT\n- Authorization with Bearer Token\n- Notes collaboration with database normalization\n- Export notes with message broker implementation using RabbitMQ\n- Image upload and storage with Amazon S3 (or locally)\n- Server-side caching implementation with Redis\n\n## Requirements\n\n- [Node.js v16.13+](https://nodejs.org/en/)\n- [NPM v8.1+](https://www.npmjs.com/package/npm)\n- [PostgreSQL v13.3+](https://www.postgresql.org/)\n- [RabbitMQ v3.9+](https://www.rabbitmq.com/)\n- [Postman](https://www.postman.com/) (for testing)\n- [notes-app-queue-consumer](https://github.com/alvinmdj/notes-app-queue-consumer) to enable notes export feature\n\n## Installation\n\n- Clone this repository:\n\n```sh\ngit clone https://github.com/alvinmdj/notes-app-back-end.git\n```\n\n- Go to the root directory:\n\n```sh\ncd notes-app-back-end\n```\n\n- Install dependencies:\n\n```sh\nnpm install\n```\n\n- Copy ```.env.example``` and paste as ```.env```:\n\n```sh\ncp .env.example .env\n```\n\n- Create database and setup environment variables in ```.env```:\n\n```sh\n# server configs\nHOST=localhost\nPORT=5000\n\n# node-postgres configs\nPGUSER=\u003cusername\u003e\nPGPASSWORD=\u003cpassword\u003e\nPGDATABASE=\u003cdbname\u003e\nPGHOST=localhost\nPGPORT=5432\n\n# JWT token\nACCESS_TOKEN_KEY=\u003csecure-random-key\u003e\nREFRESH_TOKEN_KEY=\u003canother-secure-random-key\u003e\nACCESS_TOKEN_AGE=\u003cduration-in-seconds\u003e\n\n# Message broker\nRABBITMQ_SERVER=amqp://localhost\n\n# aws-sdk (optional if you want to try Amazon S3, also look at the amazon-s3-implementation branch for the project configuration)\nAWS_ACCESS_KEY_ID=\u003caws-access-key-id\u003e\nAWS_SECRET_ACCESS_KEY=\u003caws-secret-access-key\u003e\nAWS_BUCKET_NAME=\u003caws-bucket-name\u003e\n\n# Redis\nREDIS_SERVER=localhost\n```\n\n- Run database migration:\n\n```sh\nnpm run migrate up\n```\n\n- Run (development):\n\n```sh\n# notes app back-end\nnpm run start-dev\n\n# note: run the notes-app-queue-consumer to enable notes export feature\n# https://github.com/alvinmdj/notes-app-queue-consumer\n```\n\n- Run lint:\n\n```sh\n# ESLint\nnpm run lint\n```\n\n## Testing with Postman\n\n- Make sure the server is running: ```npm run start-dev```.\n\n- Open Postman and import:\n  - ```Notes API Test.postman_collection.json file```\n  - ```Notes API Test.postman_environment.json file```\n\n  **Note:** both files are available inside the ```postman``` folder.\n\n- In Postman:\n  - Set environment to ```Notes API Test collection```.\n  - Click ```Open Music API Test collection``` \u003e ```Run collection``` \u003e ```Run Notes API Test```.\n\n## Personal Notes\n\n- node-pg-migrate:\n\n```sh\n# command available after setup migrate script in package.json\nnpm run migrate ...\n\n# create new migration file\nmigrate create '\u003cmigration name\u003e'\n\n# execute all unrun up migration\nmigrate up\n\n# execute one down migration from current state\nmigrate down\n\n# execute previous migration\n# this will run a down migration followed with an up migration\nmigrate redo\n```\n\n- Nodemon:\n\n```sh\nnpm install nodemon --save-dev\n\n# Setup in package.json: \"start\": \"nodemon ./src/server.js\"\nnpm run start-dev\n```\n\n- ESLint:\n\n```sh\nnpm install eslint --save-dev\n\n# Configure eslint\nnpx eslint --init\n\n# Setup in package.json: \"lint\": \"eslint ./src\"\nnpm run lint\n\n# https://www.dicoding.com/academies/261/tutorials/14757?from=14752\n# or refers to the official documentation: https://eslint.org/docs/user-guide/getting-started\n```\n\n- PostgreSQL:\n\n```sh\n# Check version:\npsql --version\npostgres -V\n\n# login as root user:\n# -U equals --username\npsql -U postgres\n\u003center password\u003e\n\n# login as non-root user with the db:\n# -d equals --dbname\npsql -U \u003cusername\u003e -d \u003cdbname\u003e\n\u003center password\u003e\n# example\npsql -U alvin -d notesapp\n\n# show all tables\n\\dt\n\n# show column names, data types, indexes, constraints, etc. of a table\n\\d+ \u003ctable_name\u003e\n\n# delete table data in database\ntruncate \u003ctable_name_1\u003e, \u003ctable_name_2\u003e, \u003ctable_name_3\u003e, ...;\n# example\ntruncate notes, users, authentications, collaborations;\n\n# After logged in as root user:\n\n# create user:\nCREATE USER \u003cusername\u003e WITH ENCRYPTED PASSWORD '\u003cpassword\u003e';\n\n# create db:\nCREATE DATABASE \u003cdbname\u003e;\n\n# grant privileges to other user:\nGRANT ALL PRIVILEGES ON DATABASE \u003cdbname\u003e TO \u003cusername\u003e;\n\n# quit postgres:\n\\q\n```\n\n- Generate random key with crypto (node REPL):\n\n```sh\n# enter node repl\nnode\n\n# generate random key\nrequire('crypto').randomBytes(64).toString('hex');\n```\n\n- RabbitMQ:\n\n```sh\n# activate rabbitmq_management plugin (first time only):\n# open RabbitMQ Command Prompt (sbin dir) and run the following command:\nrabbitmq-plugins.bat enable rabbitmq_management \n# note: this is for Windows OS\n\n# access RabbitMQ management webpage: \n# http://localhost:15672\n# login as 'guest' by default (both username \u0026 password)\n```\n\n- Memurai:\n\n```sh\n# access memurai-cli\nmemurai-cli\n\n# below commands is usable inside memurai-cli\n\n# test memurai (if it returns 'PONG' == success)\nping\n\n# set (create)\n# syntax: SET \u003ckey\u003e \u003cvalue\u003e [EX expirationInSecond | PX expirationInMilliseconds]\nSET name \"alvin\"\nSET age \"18\" EX 30 # expired in 30 seconds\n\n# get\n# syntax: GET \u003ckey\u003e\nGET name\n\n# set (update) will override old value from the same key\nSET name \"martin\"\n\n# delete\n# syntax: DEL \u003ckey\u003e [key2] [key3] ...\nDEL name\nDEL name age\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvinmdj%2Fnotes-app-back-end","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falvinmdj%2Fnotes-app-back-end","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falvinmdj%2Fnotes-app-back-end/lists"}