Skip to content

Commit

Permalink
* added: backend_nodejs API server (REST, WS) foundation
Browse files Browse the repository at this point in the history
  • Loading branch information
ilshookim committed Sep 9, 2019
1 parent 39ffc28 commit 98fcbbe
Show file tree
Hide file tree
Showing 20 changed files with 684 additions and 0 deletions.
18 changes: 18 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/bin/www.js",
"console": "integratedTerminal",
"env": {
"NODE_ENV": "staging"
}
}
]
}
30 changes: 30 additions & 0 deletions api/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const express = require('express')
const expressWS = require('express-ws')
const morgan = require('morgan')
const logger = require('../logger')
const bodyParser = require('body-parser')
const serveStatic = require('serve-static')
const users = require('./users')
const ws = require('./ws')

const app = express()
app.websocket = expressWS(app)

app.use(morgan('combined', { stream: logger.stream }))

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
res.header('Access-Control-Allow-Headers', 'content-type')
next()
})

app.use('/api/users', users)

app.use('/', serveStatic(`${__dirname}/public`))
app.ws('/ws', ws)

module.exports = app
23 changes: 23 additions & 0 deletions api/public/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(function () {
const HOST = location.origin.replace(/^http/, 'ws')
const ws = new WebSocket(HOST + '/ws')

const form = document.querySelector('.form')

form.onsubmit = function () {
const input = document.querySelector('.input')
const text = input.value
ws.send(text)
input.value = ''
input.focus()
return false
}

ws.onmessage = function (msg) {
const response = msg.data
const messageList = document.querySelector('.messages')
const li = document.createElement('li')
li.textContent = response
messageList.appendChild(li)
}
}())
15 changes: 15 additions & 0 deletions api/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<title>API server serve chat on WebSocket</title>
</head>
<body>
<h1>API server serve chat on WebSocket</h1>
<form class="form">
<input class="input" autofocus>
<input class="submit" type="submit">
</form>
<ul class="messages"></ul>
<script src="client.js"></script>
</body>
</html>
12 changes: 12 additions & 0 deletions api/users/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const express = require('express')
const ctrl = require('./users.ctrl')

const router = express.Router()

router.post('/', ctrl.creates)
router.get('/', ctrl.readsAll)
router.get('/:id', ctrl.reads)
router.put('/:id', ctrl.updates)
router.delete('/:id', ctrl.deletes)

module.exports = router
147 changes: 147 additions & 0 deletions api/users/users.ctrl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
const logger = require('../../logger')
const config = require('config')
const db = require('../../db/db')

// CRUD: to users

class ErrorCode extends Error {
constructor (code = 'GENERIC', status = 500, ...params) {
super(...params)

if (Error.captureStackTrace) {
Error.captureStackTrace(this, ErrorCode)
}

this.code = code
this.status = status
}
}

const creates = function (req, res) {
db.transaction(async function (sql) {
const name = req.body.name
if (!name) {
throw new ErrorCode('NO_NAME', 400, `failed to parse: name=${name}`)
}

const rows = await sql.select('*').from('users').where('name', name)
logger.debug(rows.length) // 1 | 0
if (rows.length) {
throw new ErrorCode('ALREADY_EXISTS', 409, `already exists: rows.length=${rows.length}`)
}

const total = await sql.insert({ name: name }).into('users')
logger.debug(total) // [4]
if (!total) {
throw new ErrorCode('NO_INSERT', 404, `failed to insert: total=${total}`)
}

res.status(201).json({ total: total, name: name })
}).catch(function (exc) {
logger.warn(exc.message)
let status = 404
if (exc instanceof ErrorCode) status = exc.status
res.status(status).end()
})
}

const reads = function (req, res) {
db.transaction(async function (sql) {
const id = parseInt(req.params.id, 10)
if (Number.isNaN(id)) {
throw new ErrorCode('NO_NUMBER', 400, `failed to parse: id=${id}`)
}

const rows = await sql.select('*').from('users').where('id', id)
const exists = rows.length
if (!exists) {
throw new ErrorCode('NO_EXIST', 404, `failed to select: id=${id}`)
}

const user = rows[0]
logger.debug(user) // { id: 1, name: 'Alice' }
return res.json(user)
}).catch(function (exc) {
logger.warn(exc.message)
let status = 404
if (exc instanceof ErrorCode) status = exc.status
return res.status(status).end()
})
}

const readsAll = function (req, res) {
db.transaction(async function (sql) {
req.query.limit = req.query.limit || config.get('API.users.limit')

const limit = parseInt(req.query.limit, 10)
if (Number.isNaN(limit)) {
throw new ErrorCode('NO_NUMBER', 400, `failed to parse: limit=${limit}`)
}

const rows = await sql.select('*').from('users').limit(limit)
logger.debug(rows) // [ { id: 1, name: 'Alice' }, { id: 2, name: 'Bek' } ]
return res.json(rows)
}).catch(function (exc) {
logger.warn(exc.message)
let status = 404
if (exc instanceof ErrorCode) status = exc.status
return res.status(status).end()
})
}

const updates = function (req, res) {
db.transaction(async function (sql) {
const id = parseInt(req.params.id, 10)
if (Number.isNaN(id)) {
throw new ErrorCode('NO_NUMBER', 404, `failed to parse: id=${id}`)
}

const name = req.body.name
if (!name) {
throw new ErrorCode('NO_NAME', 400, `failed to parse: name=${name}`)
}

const updated = await sql.update('name', name).from('users').where('id', id)
logger.debug(updated) // 1 | 0
if (!updated) {
throw new ErrorCode('NO_UPDATE', 404, `failed to update: id=${id}`)
}

return res.status(204).end()
}).catch(function (exc) {
logger.warn(exc.message)
let status = 404
if (exc instanceof ErrorCode) status = exc.status
return res.status(status).end()
})
}

const deletes = function (req, res) {
db.transaction(async function (sql) {
const id = parseInt(req.params.id, 10)
if (Number.isNaN(id)) {
throw new ErrorCode('NO_NUMBER', 400, `failed to parse: id=${id}`)
}

const deleted = await sql.del().from('users').where('id', id)
logger.debug(deleted) // 1 | 0
if (!deleted) {
throw new ErrorCode('NO_DELETE', 404, `failed to delete: id=${id}`)
}

return res.status(204).end()
}).catch(function (exc) {
logger.warn(exc.message)
let status = 404
if (exc instanceof ErrorCode) status = exc.status
return res.status(status).end()
})
}

module.exports = {
creates,
reads,
readsAll,
updates,
deletes
}
Loading

0 comments on commit 98fcbbe

Please sign in to comment.