├── .gitignore ├── .env.sample ├── .prettierrc ├── package.json ├── src ├── shared │ ├── io.js │ ├── db.js │ ├── sql.js │ ├── schema.sql │ └── dao.js ├── quotes.js └── authors.js ├── README.md └── output ├── authors.json └── quotes.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | output/ 3 | .env 4 | -------------------------------------------------------------------------------- /.env.sample: -------------------------------------------------------------------------------- 1 | PGHOST=localhost 2 | PGPORT=5432 3 | PGDATABASE=database 4 | PGUSER=user 5 | PGPASSWORD=password 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "none", 8 | "bracketSpacing": false, 9 | "arrowParens": "always", 10 | "proseWrap": "never", 11 | "endOfLine": "lf" 12 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-object-graph-with-node-postgres", 3 | "description": "Build JSON object graphs of SQL data with Node Postgres", 4 | "repository": { 5 | "type": "git", 6 | "url": "https://github.com/zwbetz-gh/json-object-graph-with-node-postgres" 7 | }, 8 | "version": "0.1.0", 9 | "license": "UNLICENSED", 10 | "type": "commonjs", 11 | "scripts": { 12 | "authors": "node src/authors.js", 13 | "quotes": "node src/quotes.js" 14 | }, 15 | "dependencies": { 16 | "dotenv": "^8.2.0", 17 | "pg": "^8.2.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/shared/io.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | const outputDir = 'output'; 5 | 6 | function makeDir(dir) { 7 | fs.mkdirSync(dir, {recursive: true}); 8 | } 9 | 10 | function writeJsonFile(filename, json) { 11 | makeDir(outputDir); 12 | 13 | const filePath = path.join(outputDir, filename); 14 | const data = JSON.stringify(json, null, 2); 15 | 16 | console.log(`Writing JSON file to [${filePath}]`); 17 | fs.writeFileSync(filePath, data); 18 | } 19 | 20 | module.exports = { 21 | makeDir, 22 | writeJsonFile 23 | }; 24 | -------------------------------------------------------------------------------- /src/shared/db.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const {Pool} = require('pg'); 3 | 4 | function configurePool() { 5 | return new Pool({ 6 | host: process.env.PGHOST, 7 | port: process.env.PGPORT, 8 | database: process.env.PGDATABASE, 9 | user: process.env.PGUSER, 10 | password: process.env.PGPASSWORD 11 | }); 12 | } 13 | 14 | async function query(pool, text, params) { 15 | try { 16 | const res = await pool.query(text, params); 17 | return res; 18 | } catch (err) { 19 | console.error(err); 20 | process.exit(1); 21 | } 22 | } 23 | 24 | module.exports = { 25 | configurePool, 26 | query 27 | }; 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # json-object-graph-with-node-postgres 2 | 3 | Build JSON object graphs of SQL data with Node Postgres 4 | 5 | ## Setup 6 | 7 | 1. Install NodeJS 8 | 1. Install dependencies 9 | 10 | npm install 11 | 12 | 1. Copy file `.env.sample` to file `.env` 13 | 1. Change the values in file `.env` to match your local Postgres database 14 | 1. Seed your local Postgres database by running the sql in file [`src/shared/schema.sql`](src/shared/schema.sql) 15 | 16 | ## Run 17 | 18 | For sample output see the included [`output`](output) dir 19 | 20 | 1. Build JSON object graph for authors 21 | 22 | npm run authors 23 | 24 | 1. Build JSON object graph for quotes 25 | 26 | npm run quotes 27 | -------------------------------------------------------------------------------- /output/authors.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "firstName": "Carl", 5 | "lastName": "Jung", 6 | "quotes": [ 7 | { 8 | "quote": "There is no coming to consciousness without pain." 9 | }, 10 | { 11 | "quote": "The most terrifying thing is to accept oneself completely." 12 | } 13 | ] 14 | }, 15 | { 16 | "id": 2, 17 | "firstName": "Thomas", 18 | "lastName": "Jefferson", 19 | "quotes": [ 20 | { 21 | "quote": "Do you want to know who you are? Don't ask. Act! Action will delineate and define you." 22 | }, 23 | { 24 | "quote": "The man who reads nothing at all is better educated than the man who reads nothing but newspapers." 25 | } 26 | ] 27 | } 28 | ] 29 | -------------------------------------------------------------------------------- /src/shared/sql.js: -------------------------------------------------------------------------------- 1 | const findAuthorIds = ` 2 | select 3 | id 4 | from 5 | author 6 | `; 7 | 8 | const findQuoteIds = ` 9 | select 10 | id 11 | from 12 | quote 13 | `; 14 | 15 | const findAuthor = ` 16 | select 17 | id, 18 | first_name as "firstName", 19 | last_name as "lastName" 20 | from 21 | author 22 | where 23 | id = $1 24 | `; 25 | 26 | const findQuote = ` 27 | select 28 | id, 29 | quote, 30 | author_id as "authorId" 31 | from 32 | quote 33 | where 34 | id = $1 35 | `; 36 | 37 | const findQuotes = ` 38 | select 39 | id, 40 | quote, 41 | author_id as "authorId" 42 | from 43 | quote 44 | where 45 | author_id = $1 46 | `; 47 | 48 | module.exports = { 49 | findAuthorIds, 50 | findQuoteIds, 51 | findAuthor, 52 | findQuote, 53 | findQuotes 54 | }; 55 | -------------------------------------------------------------------------------- /output/quotes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": 1, 4 | "quote": "There is no coming to consciousness without pain.", 5 | "author": { 6 | "firstName": "Carl", 7 | "lastName": "Jung" 8 | } 9 | }, 10 | { 11 | "id": 2, 12 | "quote": "The most terrifying thing is to accept oneself completely.", 13 | "author": { 14 | "firstName": "Carl", 15 | "lastName": "Jung" 16 | } 17 | }, 18 | { 19 | "id": 3, 20 | "quote": "Do you want to know who you are? Don't ask. Act! Action will delineate and define you.", 21 | "author": { 22 | "firstName": "Thomas", 23 | "lastName": "Jefferson" 24 | } 25 | }, 26 | { 27 | "id": 4, 28 | "quote": "The man who reads nothing at all is better educated than the man who reads nothing but newspapers.", 29 | "author": { 30 | "firstName": "Thomas", 31 | "lastName": "Jefferson" 32 | } 33 | } 34 | ] 35 | -------------------------------------------------------------------------------- /src/shared/schema.sql: -------------------------------------------------------------------------------- 1 | drop table if exists author cascade; 2 | 3 | create table author ( 4 | id serial primary key, 5 | first_name text not null, 6 | last_name text not null 7 | ); 8 | 9 | drop table if exists quote cascade; 10 | 11 | create table quote ( 12 | id serial primary key, 13 | quote text not null, 14 | author_id bigint not null references author (id) 15 | ); 16 | 17 | insert into author (first_name, last_name) values ('Carl', 'Jung'); 18 | insert into author (first_name, last_name) values ('Thomas', 'Jefferson'); 19 | 20 | insert into quote (quote, author_id) values ('There is no coming to consciousness without pain.', 1); 21 | insert into quote (quote, author_id) values ('The most terrifying thing is to accept oneself completely.', 1); 22 | insert into quote (quote, author_id) values ('Do you want to know who you are? Don''t ask. Act! Action will delineate and define you.', 2); 23 | insert into quote (quote, author_id) values ('The man who reads nothing at all is better educated than the man who reads nothing but newspapers.', 2); 24 | 25 | commit; 26 | -------------------------------------------------------------------------------- /src/shared/dao.js: -------------------------------------------------------------------------------- 1 | const sql = require('./sql'); 2 | const db = require('./db'); 3 | 4 | async function getAuthorIds(pool) { 5 | const text = sql.findAuthorIds; 6 | const params = null; 7 | const res = await db.query(pool, text, params); 8 | return res.rows; 9 | } 10 | 11 | async function getQuoteIds(pool) { 12 | const text = sql.findQuoteIds; 13 | const params = null; 14 | const res = await db.query(pool, text, params); 15 | return res.rows; 16 | } 17 | 18 | async function getAuthor(pool, id) { 19 | const text = sql.findAuthor; 20 | const params = [id]; 21 | const res = await db.query(pool, text, params); 22 | return res.rows[0]; 23 | } 24 | 25 | async function getQuote(pool, id) { 26 | const text = sql.findQuote; 27 | const params = [id]; 28 | const res = await db.query(pool, text, params); 29 | return res.rows[0]; 30 | } 31 | 32 | async function getQuotes(pool, id) { 33 | const text = sql.findQuotes; 34 | const params = [id]; 35 | const res = await db.query(pool, text, params); 36 | return res.rows; 37 | } 38 | 39 | module.exports = { 40 | getAuthorIds, 41 | getQuoteIds, 42 | getAuthor, 43 | getQuote, 44 | getQuotes 45 | }; 46 | -------------------------------------------------------------------------------- /src/quotes.js: -------------------------------------------------------------------------------- 1 | const db = require('./shared/db'); 2 | const io = require('./shared/io'); 3 | const dao = require('./shared/dao'); 4 | 5 | let pool = null; 6 | 7 | async function buildObject(id) { 8 | const obj = await dao.getQuote(pool, id); 9 | 10 | const author = await dao.getAuthor(pool, obj.authorId); 11 | obj.author = author ? {...author} : {}; 12 | 13 | return obj; 14 | } 15 | 16 | function massageObject(obj) { 17 | delete obj.authorId; 18 | delete obj.author.id; 19 | } 20 | 21 | async function buildObjects() { 22 | const res = await dao.getQuoteIds(pool); 23 | const ids = res.map((rowObj) => rowObj.id); 24 | const objs = []; 25 | 26 | for (const id of ids) { 27 | const obj = await buildObject(id); 28 | 29 | if (obj) { 30 | massageObject(obj); 31 | objs.push(obj); 32 | } 33 | } 34 | 35 | return objs; 36 | } 37 | 38 | async function main() { 39 | try { 40 | pool = db.configurePool(); 41 | const objs = await buildObjects(); 42 | 43 | const filename = 'quotes.json'; 44 | io.writeJsonFile(filename, objs); 45 | } catch (err) { 46 | console.error(err); 47 | } finally { 48 | pool.end(); 49 | } 50 | } 51 | 52 | main(); 53 | -------------------------------------------------------------------------------- /src/authors.js: -------------------------------------------------------------------------------- 1 | const db = require('./shared/db'); 2 | const io = require('./shared/io'); 3 | const dao = require('./shared/dao'); 4 | 5 | let pool = null; 6 | 7 | async function buildObject(id) { 8 | const obj = await dao.getAuthor(pool, id); 9 | 10 | const quotes = await dao.getQuotes(pool, obj.id); 11 | obj.quotes = quotes ? [...quotes] : []; 12 | 13 | return obj; 14 | } 15 | 16 | function massageObject(obj) { 17 | for (const quote of obj.quotes) { 18 | delete quote.id; 19 | delete quote.authorId; 20 | } 21 | } 22 | 23 | async function buildObjects() { 24 | const res = await dao.getAuthorIds(pool); 25 | const ids = res.map((rowObj) => rowObj.id); 26 | const objs = []; 27 | 28 | for (const id of ids) { 29 | const obj = await buildObject(id); 30 | 31 | if (obj) { 32 | massageObject(obj); 33 | objs.push(obj); 34 | } 35 | } 36 | 37 | return objs; 38 | } 39 | 40 | async function main() { 41 | try { 42 | pool = db.configurePool(); 43 | const objs = await buildObjects(); 44 | 45 | const filename = 'authors.json'; 46 | io.writeJsonFile(filename, objs); 47 | } catch (err) { 48 | console.error(err); 49 | } finally { 50 | pool.end(); 51 | } 52 | } 53 | 54 | main(); 55 | --------------------------------------------------------------------------------