{"id":23257233,"url":"https://github.com/iknowjavascript/circa-backend","last_synced_at":"2025-04-06T04:42:25.470Z","repository":{"id":44082292,"uuid":"203012127","full_name":"iKnowJavaScript/circa-backend","owner":"iKnowJavaScript","description":null,"archived":false,"fork":false,"pushed_at":"2023-01-04T07:32:45.000Z","size":2221,"stargazers_count":1,"open_issues_count":27,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-06T02:12:07.690Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/iKnowJavaScript.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-18T14:03:43.000Z","updated_at":"2019-08-19T03:12:21.000Z","dependencies_parsed_at":"2023-02-02T03:32:09.543Z","dependency_job_id":null,"html_url":"https://github.com/iKnowJavaScript/circa-backend","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iKnowJavaScript%2Fcirca-backend","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iKnowJavaScript%2Fcirca-backend/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iKnowJavaScript%2Fcirca-backend/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/iKnowJavaScript%2Fcirca-backend/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/iKnowJavaScript","download_url":"https://codeload.github.com/iKnowJavaScript/circa-backend/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247436139,"owners_count":20938532,"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":[],"created_at":"2024-12-19T12:27:30.697Z","updated_at":"2025-04-06T04:42:25.215Z","avatar_url":"https://github.com/iKnowJavaScript.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# circa express-rest-api-backend\n\n\u003e Circa Express REST API with JWT Authentication mongoose and mongodb\n\n- authentication via [JWT](https://jwt.io/)\n- routes mapping via [express-routes-mapper](https://github.com/aichbauer/express-routes-mapper)\n- database [mongodb](https://www.mongodb.com/)\n- environments for `development`, `testing`, and `production`\n- linting via [eslint](https://github.com/eslint/eslint)\n- integration tests running with [Jest](https://github.com/facebook/jest)\n- built with [npm sripts](#npm-scripts)\n- example for User model and User controller, with jwt authentication, simply type `yarn` and `yarn start`\n\n## Table of Contents\n\n- [Install \u0026 Use](#install-and-use)\n- [Folder Structure](#folder-structure)\n- [Api](#api)\n  - [Controllers](#controllers)\n    - [Create a Controller](#create-a-controller)\n    - [Models](#models)\n      - [Create a Model](#create-a-model)\n  - [Policies](#policies)\n    - [auth.policy](#authpolicy)\n  - [Services](#services)\n- [Config](#config)\n  - [Connection and Database](#connection-and-database)\n- [Routes](#routes)\n  - [Create Routes](#create-routes)\n- [Test](#test)\n  - [Setup](#setup)\n- [npm Scripts](#npm-scripts)\n\n## Install and Use\n\nStart by cloning this repository\n\n```sh\n# HTTPS\n$ git clone https://github.com/joefazee/real-estate.git\n```\n\nthen\n\n```sh\n# cd into project root\n# install all dependencipes\n$ yarn\n# if you use npm run\n$ npm install\n# start the api\n$ yarn start\n$ npm start\n```\n\nsqlite is supported out of the box as it is the default.\n\n## Folder Structure\n\nThis boilerplate has 4 main directories:\n\n- api - for controllers, models, services, etc.\n- config - for database, etc.\n- helpers - this is only a dir for all utility/helper functions.\n- routes - this is for routes\n- test - using [Jest](https://github.com/facebook/jest)\n\n## Controllers\n\n### Create a Controller\n\nControllers in this boilerplate have a naming convention: `ModelnameController.js` and uses an object factory pattern.\nTo use a model inside of your controller you have to require it.\nWe use [Mongoose](https://mongoosejs.com/) as ODM, if you want further information read the [Docs](https://mongoosejs.com/docs/).\n\nExample Controller for all **CRUD** oparations:\n\n```js\nconst Model = require('../models/Model');\n\nconst ModelController = () =\u003e {\n  const create = async (req, res) =\u003e {\n    // body is part of a form-data\n    const { value } = req.body;\n\n    try {\n      const model = await Model.create({\n        key: value\n      });\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' });\n      }\n\n      return res.status(200).json({ model });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const getAll = async (req, res) =\u003e {\n    try {\n      const model = await Model.findAll();\n\n      if(!models){\n        return res.status(400).json({ msg: 'Bad Request: Models not found' });\n      }\n\n      return res.status(200).json({ models });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const get = async (req, res) =\u003e {\n    // params is part of an url\n    const { id } = req.params;\n\n    try {\n      const model = await Model.findOne({\n        where: {\n          id,\n        },\n      });\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' });\n      }\n\n      return res.status(200).json({ model });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const update = async (req, res) =\u003e {\n    // params is part of an url\n    const { id } = req.params;\n\n    // body is part of form-data\n    const { value } = req.body;\n\n    try {\n      const model = await Model.findById(id);\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' });\n      }\n\n      const updatedModel = await model.update({\n        key: value,\n      )};\n\n      return res.status(200).json({ updatedModel });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  const destroy = async (req, res) =\u003e {\n    // params is part of an url\n    const { id } = req.params;\n\n    try {\n      const model =  Model.findById(id);\n\n      if(!model) {\n        return res.status(400).json({ msg: 'Bad Request: Model not found' })\n      }\n\n      await model.destroy();\n\n      return res.status(200).json({ msg: 'Successfully destroyed model' });\n    } catch (err) {\n      // better save it to log file\n      console.error(err);\n\n      return res.status(500).json({ msg: 'Internal server error' });\n    }\n  };\n\n  // IMPORTANT\n  // don't forget to return the functions\n  return {\n    create,\n    getAll,\n    get,\n    update,\n    destroy,\n  };\n};\n\nmodel.exports = ModelController;\n```\n\n## Models\n\n### Create a Model\n\nModels in this boilerplate have a naming convention: `Model.js` and uses [Mongoose](https://mongoosejs.com/) to define our Models, if you want further information read the [Docs](https://mongoosejs.com/docs/).\n\nExample User Model:\n\n```js\nconst { Schema, model } = require('mongoose');\n\n// for encrypting our passwords\nconst bcryptSevice = require('../services/bcrypt.service');\n\n// the actual model\nconst schema = new Schema(\n  {\n    name: {\n      type: String,\n      lowercase: true,\n      trim: true,\n      minlength: 3,\n      maxlength: 150,\n      required: true\n    },\n    email: {\n      type: String,\n      index: true,\n      unique: true,\n      lowercase: true,\n      trim: true,\n      minlength: 5,\n      maxlength: 150,\n      required: true\n    },\n    password: {\n      type: String,\n      required: true\n    },\n    isAdmin: {\n      type: Boolean,\n      default: false\n    }\n  },\n  { timestamps: { createdAt: 'createdAt', updatedAt: 'updatedAt' } }\n);\n\n// instead of using instanceMethod\n// in mongoose ˆ5 we are writing the function\n// to the prototype object of our model.\n// as we do not want to share sensitive data, the password\n// field gets ommited before sending\nschema.methods.toJSON = function() {\n  const user = this.toObject();\n  delete user.password;\n  return user;\n};\n\n// IMPORTANT\n// don't forget to export the Model\nmodule.exports = model('user', schema);\n\n// hooks are functions that can run before or after a specific event\nschema.post('save', user =\u003e {\n  user.password = bcryptService().hashPassword(user);\n});\n```\n\n## Policies\n\nMiddlewares are functions that can run before hitting a apecific or more specified route(s).\n\nExample isAdmin:\n\nOnly allow if the user is marked as admin.\n\n\u003e Note: this is not a secure example, only for presentation puposes\n\n```js\nconst httpStatus = require('http-status');\nconst sendResponse = require('../../helpers/response');\n\nmodule.exports = (req, res, next) =\u003e {\n  const { isAdmin } = req.token;\n\n  if (!isAdmin) {\n    return res.json(\n      sendResponse(\n        httpStatus.UNAUTHORIZED,\n        'You are not Authorized to perform this operation!',\n        null\n      )\n    );\n  }\n\n  next();\n};\n```\n\nTo use this middleware on all routes that only admins are allowed:\n\napi.js\n\n```js\nconst isAdmin = require('../api/middlewares/isAdmin');\n\napp.all('/admin/*', (req, res, next) =\u003e isAdmin(req, res, next));\n```\n\nOr for one specific route\n\napi.js\n\n```js\nconst isAdmin = require('../api/middlewares/isAdmin');\n\nconst privateRoutes = {\n  'GET /users': {\n    path: 'UserController.getAll',\n    middlewares: [isAdmin]\n  }\n};\n\nmodule.exports = privateRoutes;\n```\n\n## auth.policy\n\nThe `auth.policy` checks wether a `JSON Web Token` ([further information](https://jwt.io/)) is send in the header of an request as `Authorization: Bearer [JSON Web Token]` or inside of the body of an request as `token: [JSON Web Token]`.\nThe policy runs default on all api routes that are are prefixed with `/private`. To map multiple routes read the [docs](https://github.com/aichbauer/express-routes-mapper/blob/master/README.md) from `express-routes-mapper`.\n\nTo use this policy on all routes of a specific prefix:\n\napp.js\n\n````js\nconst auth = require('./policies/auth.policy');\n\n// secure your private routes with jwt authentication middleware\napp.all('/api/v1/private/*', (req, res, next) =\u003e auth(req, res, next));```\n\nor to use this policy on one specific route:\n\napp.js\n\n```js\napp.get('/specificRoute',\n  (req, res, next) =\u003e auth(req, res, next),\n  (req, res) =\u003e {\n  // do some fancy stuff\n});\n````\n\n## Services\n\nServices are little useful snippets, or calls to another API that are not the main focus of your API.\n\nExample service:\n\nGet user password hashed on signup or compare user passowrd on login:\n\n```js\nrequire('dotenv').config();\nconst bcrypt = require('bcrypt');\n\nconst bcryptService = () =\u003e {\n  const hashPassword = ({ password }) =\u003e {\n    const salt = bcrypt.genSaltSync(Number(process.env.HASHING_SALT));\n    return bcrypt.hashSync(password, salt);\n  };\n\n  const comparePassword = (pw, hash) =\u003e bcrypt.compareSync(pw, hash);\n\n  return {\n    hashPassword,\n    comparePassword\n  };\n};\n\nmodule.exports = bcryptService;\n```\n\n## Config\n\nHolds all the server configurations.\n\n## Connection and Database\n\n\u003e Note: as for this project we using mongodb so make sure mongodb server is running on the machine\n\nThis two files are the way to establish a connaction to a database.\n\nYou only need to touch env.js, default for `development`\n\n\u003e Note: to connect to a mongodb running database run these package with: `yarn` or `npm install` and start the app with `yarn start` or `npm start`\n\nNow simple configure the keys with your credentials.\n\n```js\n// require and configure dotenv, will load vars in .env in PROCESS.ENV\nrequire('dotenv').config();\n\nconst config = {\n  env: envVars.NODE_ENV,\n  port: envVars.PORT,\n  mongooseDebug: envVars.MONGOOSE_DEBUG,\n  jwtSecret: envVars.JWT_SECRET,\n  jwtExpirationInterval: envVars.JWT_EXPIRATION_INTERVAL,\n  mongo: {\n    host: process.env.NODE_ENV === 'development' ? envVars.MONGO_HOST : envVars.MONGO_HOST_TEST,\n    port: envVars.MONGO_PORT\n  },\n  clientSideUrl: envVars.CLIENT_SIDE_URL,\n  circa_email: envVars.CIRCA_DEV_EMAIL\n};\n\nmodule.exports = config;\n```\n\nTo not configure the production code.\n\nTo start the DB, add the credentials for production. add `environment variables` by adding your configs to the .env files e.g. check the .env-example file for more understanding read the [docs](https://github.com/dotenv/dotenv/README.md) from `dotenv`. before starting the api.\n\n## Routes\n\nHere you define all your routes for your api. It doesn't matter how you structure them. By default they are mapped on `privateRoutes` and `publicRoutes`. You can define as much routes files as you want e.g. for every model or for specific use cases, e.g. normal user and admins.\n\n## Create Routes\n\nFor further information read the [docs](https://github.com/aichbauer/express-routes-mapper/blob/master/README.md) of express-routes-mapper.\n\nExample for User Model:\n\n\u003e Note: Only supported Methods are **POST**, **GET**, **PUT**, and **DELETE**.\n\nuserRoutes.js\n\n```js\nconst userRoutes = {\n  'POST /user': 'UserController.create',\n  'GET /users': 'UserController.getAll',\n  'GET /user/:id': 'UserController.get',\n  'PUT /user/:id': 'UserController.update',\n  'DELETE /user/': 'UserController.destroy'\n};\n\nmodule.exports = userRoutes;\n```\n\nTo use these routes in your application, require them in the config/index.js and export them.\n\n```js\nconst userRoutes = require('./userRoutes');\n\nconst config = {\n  allTheOtherStuff,\n  userRoutes\n};\n\nmodule.exports = config;\n```\n\napi.js\n\n```js\nconst mappedUserRoutes = mapRoutes(config.userRoutes, 'api/controllers/');\n\napp.use('/prefix', mappedUserRoutes);\n\n// to protect them with authentication\napp.all('/prefix/*', (req, res, next) =\u003e auth(req, res, next));\n```\n\n## Test\n\nAll test for this boilerplate uses [Jest](https://github.com/facebook/jest) and [supertest](https://github.com/visionmedia/superagent) for integration testing. So read their docs on further information.\n\n### Setup\n\nThe setup directory holds the `_setup.js` which holds `beforeAction` which starts a test express application and connects to your test database, and a `afterAction` which closes the db connection.\n\n### Controller\n\n\u003e Note: those request are asynchronous, we use `async await` syntax.\n\n\u003e Note: As we don't use import statements inside the api we also use the require syntax for tests\n\nTo test a Controller we create `fake requests` to our api routes.\n\nExample `GET /user` from last example with prefix `prefix`:\n\n```js\nconst request = require('supertest');\nconst { beforeAction, afterAction } = require('../setup/_setup');\n\nlet api;\n\nbeforeAll(async () =\u003e {\n  api = await beforeAction();\n});\n\nafterAll(() =\u003e {\n  afterAction();\n});\n\ntest('test', async () =\u003e {\n  const token = 'this-should-be-a-valid-token';\n\n  const res = await request(api)\n    .get('/prefix/user')\n    .set('Accept', /json/)\n    // if auth is needed\n    .set('Authorization', `Bearer ${token}`)\n    .set('Content-Type', 'application/json')\n    .expect(200);\n\n  // read the docs of jest for further information\n  expect(res.body.user).toBe('something');\n});\n```\n\n### Models\n\nAre usually automatically tested in the integration tests as the Controller uses the Models, but you can test them separatly.\n\n## npm scripts\n\nThere are no automation tool or task runner like [grunt](https://gruntjs.com/) or [gulp](http://gulpjs.com/) used for this boilerplate. These boilerplate only uses npm scripts for automatization.\n\n### npm start\n\nThis is the entry for a developer. This command:\n\nBy default it uses a sqlite databse, if you want to migrate the sqlite db by each start, disable the `prestart` and `poststart` command. Also mind if you are using a sqlite database to delete the `drop-sqlite-db` in the prepush hook.\n\n- runs **nodemon watch task** for the all files conected to `.api/api.js`\n- sets the **environment variable** `NODE_ENV` to `development`\n- opens the db connection for `development`\n- starts the server on 127.0.0.1:2017\n\n### npm test\n\nThis command:\n\n- runs `npm run lint` ([eslint](http://eslint.org/)) with the [airbnb styleguide](https://github.com/airbnb/javascript) without arrow-parens rule for **better readability**\n- sets the **environment variable** `NODE_ENV` to `testing`\n- creates the `database.sqlite` for the test\n- runs `jest --coverage` for testing with [Jest](https://github.com/facebook/jest) and the coverage\n- drops the `database.sqlite` after the test\n\n## npm run production\n\nThis command:\n\n- sets the **environment variable** to `production`\n- opens the db connection for `production`\n- starts the server on 127.0.0.1:2017 or on 127.0.0.1:PORT_ENV\n\nBefore running on production you have to set the **environment vaiables**:\n\n- DB_NAME - database name for production\n- DB_USER - database username for production\n- DB_PASS - database password for production\n- DB_HOST - database host for production\n- JWT_SECERT - secret for json web token\n\nOptional:\n\n- PORT - the port your api on 127.0.0.1, default to 2017\n\n### other commands\n\n- `npm run dev` - simply start the server withou a watcher\n- `npm run create-sqlite-db` - creates the sqlite database\n- `npm run drop-sqlite-db` - drops **ONLY** the sqlite database\n- `npm run lint` - linting with [eslint](http://eslint.org/)\n- `npm run nodemon` - same as `npm start``\n- `npm run prepush` - a hook wich runs before pushing to a repository, runs `npm test` and `npm run dropDB`\n- `pretest` - runs linting before `npm test`\n- `test-ci` - only runs tests, nothing in pretest, nothing in posttest, for better use with ci tools\n\n## LICENSE\n\nMIT © Circa\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiknowjavascript%2Fcirca-backend","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fiknowjavascript%2Fcirca-backend","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fiknowjavascript%2Fcirca-backend/lists"}