This is the backend repository for our InTuneNation application and is deployed at https://ppp-capstone-music.herokuapp.com/ as the root. This backend was built with the Node.js Express framework and postgres database.
Here is exercises table's schema
exports.up = function (knex) {
return knex.schema.createTable('exercises', (table) => {
table.increments();
// exercises table belongs to user's table
table.integer('user_id').references('id').inTable('users').onDelete('CASCADE')
.notNullable()
.index();
table.string('notes_array').notNullable().defaultTo('[]');
table.timestamps(true, true);
});
};
Description: The ERD schema starts at the root with “users” accounts. Users are able to create “exercises” that are tied to a foreign key of a user id. Each exercise can have a multitude of “scores”, tied by foreign keys of exercises_id and user_id.
Our app is currently built entirely with Javascript, and the back-end is built with Node.js Express server.
- Google-Oauth (Google Passport 2.0 OAuth Technologies allow user to sign in through google's account)
- Json-Web-Token (Json-Web-Token allows our server to authenticate users)
- body-parser (Parse incoming request bodies in a middleware before your handlers)
- knex (Knex.js is a SQL query builder for Postgres)
- bcrypt (bcrypt is a hashing algorithm which encrypt password, it provides 'compare' and 'hash' functionalities)
- morgan (Morgan is a middleware function using given format and options)
npm install
install dependenciestouch .env
Example .env file:- Please note that this repo requires Google Oauth. Please go to Google Developer Console and click
Google API Console
to register a new application and update the .env file with the client id and client secret. The callback URL will need to be updated here inIntuneBackEnd/config/passport.js
//Jwt key
JWT_KEY=XXXX
//This is google key
GOOGLE_CLIENT_ID='XXXX'
GOOGLE_CLIENT_SECRET='XXXX'
//callbackURL
CALL_BACK_URL='http://localhost:8000/auth/google/callback'
// Deployed version CALL_BACK_URL=`https://ppp-capstone-music.herokuapp.com/auth/google/callback`
- For running development environment locally, run command line:
createdb ppp_dev
- To run all of the tests locally, run command line:
createdb ppp_test
- Seed the database. run command line:
knex migrate:rollback && knex migrate:latest && knex seed:run
I make an new instance of GoogleStrategy using 'GOOGLE_CLIENT_ID', 'GOOGLE_CLIENT_SECRET', and 'CALL_BACK_URL'. You can obtain those information from your Google plus account. after Google successfully verify your information, it would give me back your profile information in an object form. Then, that is how I would display your information on the screen.
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.CALL_BACK_URL,
passReqToCallback: true
}, function(request, accessToken, refreshToken, profile, done) {
//profile is an object with your personal information. This is a callback of successful log in
}));
We wrote tests to test routes, seeds, and migrations.
TO Run Test run npm test
in your terminal
- mocha (Mocha is a feature-rich Javascript testing framework running on Node.js)
- chai (Chai is a BDD / TDD assertion library for node and the browser that can be delightfully paired with any javascript testing framework.)
- supertest (SuperTest is a module that provides high-level abstraction for testing HTTP in node.js.)
function middlewareVerify(req, res, next) {
jwt.verify(req.headers.token, process.env.JWT_KEY, (err, payload) => {
if (err) {
res.status(401);
res.send({ status: 401, ErrorMessage: 'Unauthorized' });
} else {
req.claim = payload;
next();
}
});
}
Backend contains endpoint routes for user accounts, user exercises, and user scores. Token verification is required to access /users
routes. Token is assigned upon signup and login and must be passed through the header request. All successful responses are returned in JSON format. Failed responses are returned in plain text. :userId
, :exId
, & :scId
must be replaced with integers when making a backend API request.
** Simple Summary of all routes**
- GET /user
- POST /user/signup
- POST /user/login
- GET /users/:userId/exercises
- GET /users/:userId/exercises/:exId
- POST /users/:userId/exercises
- GET /users/:userId/exercises/:exId/scores
- GET /users/:userId/exercises/:exId/scores/:scId
- POST /users/:userId/exercises/:exId/scores
Show all users
GET /user
Response:
[
{
id: integer,
first_name: string,
last_name: string,
email: string,
profile_picture: string,
created_at: timestamp,
updated_at: timestamp,
},
]
User Signup
POST /user/signup
Request Body:
{ firstName: string, lastName: string, email: string, password: string }
Response:
{
id: integer,
first_name: string,
last_name: string,
email: string,
profilePicture: string,
token: string
}
User Login
POST /user/login
Request Body:
{ email: string, password: string }
Response:
{
id: integer,
firstName: string,
lastName: string,
email: string,
profilePicture: string,
token: string
}
GET All exercises that belong to a user
GET /users/:userId/exercises
Request Header:
token: string
Response:
[
{
id: integer,
user_id: integer,
notes_array: string
created_at: timestamp,
updated_at: timestamp
},
...
]
GET Specific exercise that belongs to a user
GET /users/:userId/exercises/:exId
Request Header:
token: string
Response:
{
id: integer,
user_id: integer,
notes_array: string,
created_at: timestamp,
updated_at: timestamp
}
POST User exercise to database
POST /users/:userId/exercises
Request Header:
token: string
Request Request Body:
{ notes_array: array }
Response:
{
id: integer,
user_id: integer,
notes_array: string
}
GET All scores that belong to an exercise
GET /users/:userId/exercises/:exId/scores
Request Header:
token: string
Response:
[
{
id: integer,
user_id: integer,
scores_array: string,
avg_score: float,
created_at: timestamp,
updated_at: timestamp
}
]
Get Specific score that belong to an exercise
GET /users/:userId/exercises/:exId/scores/:scId
Request Header:
token: string
Response:
{
id: integer,
user_id: integer,
scores_array: string,
avg_score: float,
created_at: timestamp,
updated_at: timestamp
}
POST Score for a specific exercise
POST /users/:userId/exercises/:exId/scores
Request Header:
token: string
Request Body:
{ scores_array: array, avg_score: float }
Response:
{
id: integer,
user_id: integer,
scores_array: string,
avg_score: float
}
// production error handler
// no stack traces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
400: Invalid Input
401: Unauthorized
404: Not Found
500: Internal Server Error