├── README.md ├── Dockerfile ├── vercel.json ├── helper.js ├── services ├── db.js └── programmingLanguages.js ├── .qovery.yml ├── config.js ├── package.json ├── index.js ├── routes └── programmingLanguages.js └── .gitignore /README.md: -------------------------------------------------------------------------------- 1 | # programming-langugages-api 2 | A demo REST API with Node.js Express.js and MySQL for most popular programming languages 3 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:14-alpine 2 | RUN mkdir -p /usr/src/app 3 | WORKDIR /usr/src/app 4 | COPY package*.json ./ 5 | RUN npm ci 6 | 7 | COPY . ./ 8 | EXPOSE 3000 9 | CMD ["node", "index.js"] 10 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "name": "programming-languages-api", 4 | "builds": [ 5 | { "src": "index.js", "use": "@vercel/node" } 6 | ], 7 | "routes": [ 8 | { "src": "/(.*)", "dest": "/index.js" } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /helper.js: -------------------------------------------------------------------------------- 1 | function getOffset(currentPage = 1, listPerPage) { 2 | return (currentPage - 1) * [listPerPage]; 3 | } 4 | 5 | function emptyOrRows(rows) { 6 | if (!rows) { 7 | return []; 8 | } 9 | return rows; 10 | } 11 | 12 | module.exports = { 13 | getOffset, 14 | emptyOrRows 15 | } 16 | -------------------------------------------------------------------------------- /services/db.js: -------------------------------------------------------------------------------- 1 | const mysql = require('mysql2/promise'); 2 | const config = require('../config'); 3 | 4 | async function query(sql, params) { 5 | const connection = await mysql.createConnection(config.db); 6 | const [results, ] = await connection.execute(sql, params); 7 | 8 | return results; 9 | } 10 | 11 | module.exports = { 12 | query 13 | } 14 | -------------------------------------------------------------------------------- /.qovery.yml: -------------------------------------------------------------------------------- 1 | --- 2 | application: 3 | name: "programming-langugages-api" 4 | project: "programming-languages-api" 5 | organization: "QoveryCommunity" 6 | publicly_accessible: true 7 | port: 3000 8 | routers: 9 | - name: "main-programming-langugages-api" 10 | routes: 11 | - application_name: "programming-langugages-api" 12 | paths: 13 | - "/*" 14 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | const env = process.env; 2 | 3 | const config = { 4 | db: { /* don’t expose password or any sensitive info, done only for demo */ 5 | host: env.DB_HOST || 'freedb.tech', 6 | user: env.DB_USER || 'freedbtech_geshann', 7 | password: env.DB_PASSWORD || 'G2VjjQ5d47zyjqX', 8 | database: env.DB_NAME || 'freedbtech_languagesabc', 9 | }, 10 | listPerPage: env.LIST_PER_PAGE || 10, 11 | }; 12 | 13 | module.exports = config; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "programming-langugages-api", 3 | "version": "1.0.0", 4 | "description": "A demo REST API with Node.js Express.js and MySQL for most popular programming languages", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/geshan/programming-langugages-api.git" 13 | }, 14 | "keywords": [], 15 | "author": "", 16 | "license": "ISC", 17 | "bugs": { 18 | "url": "https://github.com/geshan/programming-langugages-api/issues" 19 | }, 20 | "homepage": "https://github.com/geshan/programming-langugages-api#readme", 21 | "dependencies": { 22 | "express": "^4.17.1", 23 | "mysql2": "^2.2.5" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const app = express(); 4 | const port = process.env.PORT || 3000; 5 | const programmingLanguagesRouter = require('./routes/programmingLanguages'); 6 | 7 | app.use(bodyParser.json()); 8 | app.use( 9 | bodyParser.urlencoded({ 10 | extended: true, 11 | }) 12 | ); 13 | 14 | app.get('/', (req, res) => { 15 | res.json({'message': 'ok'}); 16 | }) 17 | 18 | app.use('/programming-languages', programmingLanguagesRouter); 19 | 20 | /* Error handler middleware */ 21 | app.use((err, req, res, next) => { 22 | const statusCode = err.statusCode || 500; 23 | console.error(err.message, err.stack); 24 | res.status(statusCode).json({'message': err.message}); 25 | 26 | return; 27 | }); 28 | 29 | app.listen(port, '0.0.0.0', () => { 30 | console.log(`Example app listening at http://localhost:${port}`) 31 | }); 32 | -------------------------------------------------------------------------------- /routes/programmingLanguages.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | const programmingLanguages = require('../services/programmingLanguages'); 4 | 5 | /* GET programming languages. */ 6 | router.get('/', async function(req, res, next) { 7 | try { 8 | res.json(await programmingLanguages.getMultiple(req.query.page)); 9 | } catch (err) { 10 | console.error(`Error while getting programming languages`, err.message); 11 | next(err); 12 | } 13 | }); 14 | 15 | /* POST programming language */ 16 | router.post('/', async function(req, res, next) { 17 | try { 18 | res.json(await programmingLanguages.create(req.body)); 19 | } catch (err) { 20 | console.error(`Error while creating programming language`, err.message); 21 | next(err); 22 | } 23 | }); 24 | 25 | /* PUT programming language */ 26 | router.put('/:id', async function(req, res, next) { 27 | try { 28 | res.json(await programmingLanguages.update(req.params.id, req.body)); 29 | } catch (err) { 30 | console.error(`Error while updating programming language`, err.message); 31 | next(err); 32 | } 33 | }); 34 | 35 | /* DELETE programming language */ 36 | router.delete('/:id', async function(req, res, next) { 37 | try { 38 | res.json(await programmingLanguages.remove(req.params.id)); 39 | } catch (err) { 40 | console.error(`Error while deleting programming language`, err.message); 41 | next(err); 42 | } 43 | }); 44 | 45 | module.exports = router; 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /services/programmingLanguages.js: -------------------------------------------------------------------------------- 1 | const db = require('./db'); 2 | const helper = require('../helper'); 3 | const config = require('../config'); 4 | 5 | async function getMultiple(page = 1){ 6 | const offset = helper.getOffset(page, config.listPerPage); 7 | const rows = await db.query( 8 | `SELECT id, name, released_year, githut_rank, pypl_rank, tiobe_rank 9 | FROM programming_languages LIMIT ?,?`, 10 | [offset, config.listPerPage] 11 | ); 12 | const data = helper.emptyOrRows(rows); 13 | const meta = {page}; 14 | 15 | return { 16 | data, 17 | meta 18 | } 19 | } 20 | 21 | async function create(programmingLanguage){ 22 | const result = await db.query( 23 | `INSERT INTO programming_languages 24 | (name, released_year, githut_rank, pypl_rank, tiobe_rank) 25 | VALUES 26 | (?, ?, ?, ?, ?)`, 27 | [ 28 | programmingLanguage.name, programmingLanguage.released_year, 29 | programmingLanguage.githut_rank, programmingLanguage.pypl_rank, 30 | programmingLanguage.tiobe_rank 31 | ] 32 | ); 33 | 34 | let message = 'Error in creating programming language'; 35 | 36 | if (result.affectedRows) { 37 | message = 'Programming language created successfully'; 38 | } 39 | 40 | return {message}; 41 | } 42 | 43 | async function update(id, programmingLanguage){ 44 | const result = await db.query( 45 | `UPDATE programming_languages 46 | SET name=?, released_year=?, githut_rank=?, 47 | pypl_rank=?, tiobe_rank=? 48 | WHERE id=?`, 49 | [ 50 | programmingLanguage.name, programmingLanguage.released_year, 51 | programmingLanguage.githut_rank, programmingLanguage.pypl_rank, 52 | programmingLanguage.tiobe_rank, id 53 | ] 54 | ); 55 | 56 | let message = 'Error in updating programming language'; 57 | 58 | if (result.affectedRows) { 59 | message = 'Programming language updated successfully'; 60 | } 61 | 62 | return {message}; 63 | } 64 | 65 | async function remove(id){ 66 | const result = await db.query( 67 | `DELETE FROM programming_languages WHERE id=?`, 68 | [id] 69 | ); 70 | 71 | let message = 'Error in deleting programming language'; 72 | 73 | if (result.affectedRows) { 74 | message = 'Programming language deleted successfully'; 75 | } 76 | 77 | return {message}; 78 | } 79 | 80 | module.exports = { 81 | getMultiple, 82 | create, 83 | update, 84 | remove 85 | } 86 | --------------------------------------------------------------------------------