A restful API that models a jobs database for tracking companies, jobs, and job applications. Users can retrieve information about companies and job postings. Logged in users can apply for jobs. Admins can post/update/delete jobs and manage users.
- Database of companies, jobs, job applications, and users in PostgreSQL
- Backend written in Node.js using Express
- RESTful API to interact with the database
- Implements Role based authorization system using signed JWTs
- Unit testing suite using Jest and Supertest
- SQL files of test data
- Localhost API base URL: http://localhost:3001
- This API is currently deployed to: https://kep-jobly-backend.herokuapp.com/
- The React Frontend is currently deployed to: https://hot-soap.surge.sh/
The repository for the React frontend is https://github.com/Keparoo/React-Jobly
Clone the repo by running
git clone https://github.com/Keparoo/Jobly
- node.js
- postgreSQL
- express 4.17.1
- pg 8.3.0
- jsonwebtoken 8.5.1
- jsonschema 1.2.6
- bcrypt 5.0.0
- cors 2.8.5
- jest 27.3.1
- supertest 5.0.0
See the package.json file and package-lock.json file for more info on dependencies
To install npm packages:
npm i
The project uses PostgreSQL databases
- Create two databases: One for testing and one for development:
createdb jobly
createdb jobly_test
To change the database URIs, edit the config.js
file
To create the database tables and seed with data, from the project directory:
psql < jobly.sql
Switch to the project directory
To run in development mode:
npm run dev
To run the server in producton mode:
npm start
Create a .env file matching the template provided in the .env.example file.
The SECRET_KEY= variable must be set in order to properly sign the JWTs that are used for authentication. Set this variable to a good random string. If nothing is set the config.js file falls back to a weak key suitable only for development.
The project maintains a users table of registered users. Two roles are used for authentication: user and admin.
- Anonymous user (not logged in)
Can view information about companies and job listings - User (logged in)
Can view information about companies and job listings Can view their own user-table info Can post a job application - Admin (logged in)
Can view all user, company, job, and application information Can post/edit/delete user, company, job and application information
A Bearer token must be sent in all request headers requiring logged in or higher permissions
To run the test suite:
npm test
To check the test-coverage of code:
npm coverage
There is currently 100% testing coverage.
The API is a restful API that returns json
GET /companies
-
General:
- Returns a list of all companies in database
- Authorization required: none
- Request Arguments: Accepts 3 optional query strings to filter results:
minEmployees
(integer)maxEmployees
(integer)nameLike
(string) case-insensitive, will make partial matches
- Filter queries other than the above will be ignored
- Returns a list of all companies in database
{
"companies": [
{
"handle": "ibm",
"name": "IBM corp",
"description": "Big Blue",
"numEmployees": 100000,
"logoUrl": "http://www.ibm.com"
}
]
}
GET /companies/<handle>
-
General:
- Returns info on company mathching handle
- Authorization required: none
- Request arguments: None
Sample Return:
{
"company": {
"handle": "ibm",
"name": "IBM corp",
"description": "Big Blue",
"numEmployees": 100000,
"logoUrl": "http://www.ibm.com",
"jobs": [
{
"id": 1,
"title": "Senior Developer",
"salary": 50000,
"equity": "0.2"
}
]
}
}
POST /companies
- General:
- Posts a new company to the database
- Authorization required: admin
- Request arguments: None
name
,handle
anddescription
are required
Sample Request Body:
{
"handle": "ibm",
"name": "IBM corp",
"description": "Big Blue",
"numEmployees": 100000,
"logoUrl": "http://www.ibm.com"
}
Sample Return:
{
"company": {
"handle": "ibm",
"name": "IBM corp",
"description": "Big Blue",
"numEmployees": 100000,
"logoUrl": "http://www.ibm.com"
}
}
PATCH /companies/<handle>
- General:
- Updates a company in the database
- Authorization required: admin
- Request arguments: None
name
,description
,numEmployees
andlogoUrl
may be updated- one or all field may be updated in a single query
Sample Request Body (each field is optional)
{
"name": "IBM corp",
"description": "Big Blue",
"numEmployees": 100000,
"logoUrl": "http://www.ibm.com"
}
Sample Return:
{
"company": {
"name": "IBM corp",
"description": "Big Blue",
"numEmployees": 100000,
"logoUrl": "http://www.ibm.com"
}
}
DELETE /companies/<handle>
- General:
- Deletes a company from the database
- Authorization required: admin
- Request arguments: None
Sample Return:
{
"deleted": "ibm"
}
GET /jobs
- General:
- Returns a list of all jobs in database
- Authorization required: none
- Request Arguments: Accepts 3 optional query strings to filter results:
minSalary
(integer)hasEquity
(boolean) true indicates equity is > '0.0'title
(string) case-insensitive, will make partial matches
- Filter queries other than the above will be ignored
Sample Return:
{
"jobs": [
{
"id": 1,
"title": "Senior programmer",
"salary": 100000,
"equity": "0.2",
"companyHandle": "ibm",
"companyName": "IBM corp"
}
]
}
GET /jobs/<job_id>
- General:
- Returns information about the job matching job_id
- Authorization required: none
Sample Return:
{
"job": {
"id": 1,
"title": "Senior programmer",
"salary": 100000,
"equity": "0.2",
"company": {
"handle": "ibm",
"name": "IBM corp",
"description": "Big Blue",
"numEmployees": 10000,
"logoUrl": "http://www.ibm.com"
}
}
}
POST /jobs
- General:
- Posts a new job to the database
- Authorization required: admin
- Request arguments: None
title
andcompanyHandle
are required
Sample Request Body:
{
"title": "Senior developer",
"salary": 100000,
"equity": "0.2",
"companyHandle": "ibm"
}
Sample Return:
{
"job": {
"title": "Senior developer",
"salary": 100000,
"equity": "0.2",
"companyHandle": "ibm"
}
}
PATCH /jobs/<job_id>
- General:
- Updates a job in the database
- Authorization required: admin
- Request arguments: None
title
,salary
, andequity
may be updated- one or all field may be updated in a single query
Sample Request Body (each field is optional)
{
"title": "Senior developer",
"salary": 100000,
"equity": "0.2"
}
Sample Return:
{
"id": 1,
"title": "Senior developer",
"salary": 100000,
"equity": "0.2",
"companyHandle": "ibm"
}
DELETE /companies/<handle>
- General:
- Deletes a job from the database
- Authorization required: admin
- Request arguments: None
Sample Return:
{
"deleted": 1
}
GET /users
- General:
- Returns a list of all users in database
- Authorization required: admin
- Request arguments: None
Sample Return:
{
"users": [
{
"username": "testuser",
"firstName": "Chris",
"lastName": "Robbins",
"email": "chris@chris.com"
}
]
}
GET /users/<username>
- General:
- Returns information about the user matching username
- Authorization required: user (logged in)
- A user is authorized to view info from their own account
- Admin may view any user's information
Sample Return:
{
"user": {
"username": "testuser",
"firstName": "Chris",
"lastName": "Robbins",
"isAdmin": false,
"jobs": [1, 3, 47]
}
}
POST /users
- General:
- Posts a new user to the database
- This is not a registration endpoint: This is an admin only enpoint to manage users
- Authorization required: admin
- Request arguments: None
username
,firstName
,lastName
,password
, andemail
are required
Sample Request Body:
{
"username": "testuser",
"firstName": "Chris",
"lastName": "Robbins",
"password": "password1",
"email": "chris@chris.com",
"isAdmin": false
}
Sample Return:
{
"user": {
"username": "testuser",
"firstName": "Chris",
"lastName": "Robbins",
"email": "chris@chris.com",
"isAdmin": false
},
"token": "long-bearer-token-string"
}
PATCH /users/<username>
- General:
- Updates a user in the database
- Authorization required: current logged in user or admin
- Request arguments: None
firstName
,lastName
,password
, andemail
may be updated- one or all field may be updated in a single query
Sample Request Body (each field is optional)
{
"firstName": "Chris",
"lastName": "Robbins",
"password": "password1",
"email": "chris@chris.com"
}
Sample Return:
{
"user": {
"username": "testuser",
"firstName": "Chris",
"lastName": "Robbins",
"email": "chris@chris.com",
"isAdmin": false
}
}
DELETE /users/<username>
- General:
- Deletes a user from the database
- Authorization required: current logged in user or admin
- Request arguments: None
Sample Return:
{
"deleted": "testuser"
}
POST /users/<username>/jobs/<job_id>
- General:
- Posts a job application to the database
- Authorization required: current logged in user or admin
- Request arguments: None
Sample Return:
{
"applied": 1
}
POST /auth/token
- General:
- Returns a JWT token which can be used to authenticate further requests
- Authorization required: none
- Request arguments: None
Sample Request Body:
{
"username": "testuser",
"password": "password123"
}
Sample Return:
{
"token": "long-JWT-auth-token"
}
POST /auth/register
- General:
- Registers a new user in the database
- Authorization required: none
- Request arguments: None
username
,password
,firstName
,lastName
, andemail
are required- Returns JWT token which can be used to authenticate further requests Sample Request Body:
{
"username": "testuser",
"password": "password123",
"firstName": "Chris",
"lastName": "Robbins",
"email": "chris@chris.com"
}
Sample Return:
{
"token": "long-JWT-auth-token"
}
Validation of input data is handled by the JSON-SCHEMA
files located in the schema
directory.
The following errors are defined and handled in the expressError.js
file
- ExpressError (extending Error)
- 404 NotFoundError
- 401 UnauthorizedError
- 400 BadRequestError
- 403 ForbiddenError
Kep Kaeppeler is the author of this project, test suite, and documentation