A simplified ride-sharing backend RESTFUL API developed in NestJS, which includes features like user registration, ride requests, and driver availability management.
high-ride-api
is a simplified ride-sharing backend API developed in NestJS, which includes features like user registration, ride requests, and driver availability management. The API is developed using NestJS, a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. It uses MongoDB
as the database service and is developed using TypeScript.
The API takes in the data in JSON
format and returns the data in JSON
format as well.
NodeJS
(>= 16.0.0
)MongoDB
(>= 4.4.6
)npm
(>= 7.10.0
)Git
(>= 2.25.1
)Postman
(>= 8.10.0
) or any other API testing tool e.g.Insomnia
,HTTPie
( I used this for testing the API ), etc.
- Follow the instructions given here to install NodeJS on your system.
- Follow the instructions given here to install MongoDB on your system.
- Create a
.env
file in the root directory of the project. - Copy the contents of
.env.example
to.env
file. - Replace the value of
JWT_SECRET
to a random( but secure ) string of your choice. This will be used to sign the JWT tokens. - By default, the database service is configured to run on
mongodb://localhost:27017/high-ride-api
which won't need any authentication. If you want to change the database service URL, replace the value ofDB_URI
in.env
file. - On
Unix
, start the MongoDB service.sudo systemctl start mongod
- On
Windows
, in order to start the MongoDB service, follow the instructions given here or here.
- Clone the repository.
git clone https://github.com/winterrdog/high-ride-api.git
- Navigate to the project's root directory.
cd high-ride-api
- Install the dependencies.
$ npm i
-
By default, the app is configured to run on
http://localhost:5000
. If you want to change the port, replace the value ofPORT
in your.env
file.# development mode $ npm start # watch mode $ npm run start:dev # production mode $ npm run start:prod
-
POST /user/register
- Register a new user. Accessible to everyone. Send aPOST
request with the following data inJSON
format.{ "firstName": "John", "lastName": "Doe", "email": "johndoe@gmail.com", "password": "johndoe123", "phoneNumber": "07000000000", "role": "passenger" }
The
role
field can be eitherpassenger
ordriver
.The server will respond with the registered user and a JWT token. Status code
201
will be returned if the user is successfully registered. -
GET /user/profile
- Get the profile of the logged-in user. Only authenticated users can access this route. The server will return the profile of the logged-in user. Status code200
will be returned if the user is successfully fetched. For instance:{ "firstName": "John", "lastName": "Doe", "email": "john23@example.com", "phoneNumber": "1234567890", "role": "driver", "driverStatus": "not applicable", "id": "64eb94ca0a86e88cd4d80944", "dateCreated": "2023-08-27T18:24:10.598Z", "lastModified": "2023-08-27T18:24:10.598Z" }
-
PATCH /user/profile/driver
- Update the profile of the logged-in user who's adriver
roles. Only authenticateddriver
role users can access this route. Send aPATCH
request with the following data inJSON
format.{ "driverStatus": "available" }
The
driverStatus
field can be eitheravailable
orunavailable
.The server will respond with the updated profile of the logged-in user. Status code
200
will be returned if the user is successfully updated. For instance:{ "firstName": "John", "lastName": "Doe", "email": "john23@example.com", "phoneNumber": "1234567890", "role": "driver", "driverStatus": "available", "id": "64eb94ca0a86e88cd4d80944", "dateCreated": "2023-08-27T18:24:10.598Z", "lastModified": "2023-08-27T18:24:10.598Z" }
-
GET /rides/
- Get the list of all available rides. Only authenticateddriver
role users can access this route. The server will return the list of all available rides. Status code200
will be returned if the rides are successfully fetched otherwise it'll return a404
. For instance:[ { "passenger": { "firstName": "Lyndon", "lastName": "Darren", "email": "lyndon@example.com", "phoneNumber": "1234567890", "role": "passenger", "driverStatus": "not applicable", "id": "64ec26c23ef09575c4838c64" }, "driver": null, "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" }, "rideStatus": "accepted", "createdAt": "2023-08-28T04:50:22.223Z", "updatedAt": "2023-08-28T09:14:29.816Z", "id": "64ec278e855903b8781efbc8" } ]
The driver field will be
null
if the ride is not accepted by any driver. Otherwise, it'll contain the driver's profile like in this case:{ "passenger": { "firstName": "Lyndon", "lastName": "Darren", "email": "lyndon@example.com", "phoneNumber": "1234567890", "role": "passenger", "driverStatus": "not applicable", "id": "64ec26c23ef09575c4838c64" }, "driver": { "firstName": "John", "lastName": "Doe", "email": "", "phoneNumber": "1234567890", "role": "driver", "driverStatus": "available", "id": "64ec26c23ef09575c4838c65" }, "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" }, "rideStatus": "accepted", "createdAt": "2023-08-28T04:50:22.223Z", "updatedAt": "2023-08-28T09:14:29.816Z", "id": "64ec278e855903b8781efbc8" }
-
POST /rides/
- Create a new ride. Only authenticatedpassenger
role users can create a ride. Send aPOST
request with the following data inJSON
format.{ "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" } }
The server will respond with the created ride. Status code
201
will be returned if the ride is successfully created. For instance:{ "passenger": { "firstName": "Lyndon", "lastName": "Darren", "email": "lyndon@example.com", "phoneNumber": "1234567890", "role": "passenger", "driverStatus": "not applicable", "id": "64ec26c23ef09575c4838c64" }, "driver": null, "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" }, "rideStatus": "accepted", "createdAt": "2023-08-28T04:50:22.223Z", "updatedAt": "2023-08-28T09:14:29.816Z", "id": "64ec278e855903b8781efbc8" }
-
PATCH /rides/:id
- Update a ride's status. Only authenticated users can update a ride's status. Passengers can only mark rides ascancelled
and drivers can mark rides ascompleted
,accepted
, andcancelled
. Send aPATCH
request with the following data inJSON
format.{ "rideStatus": "cancelled" }
The
rideStatus
field can be eithercancelled
,accepted
, orcompleted
.The server will respond with the updated ride. Status code
200
will be returned if the ride is successfully updated. For instance:{ "passenger": { "firstName": "Lyndon", "lastName": "Darren", "email": "lyndon@example.com", "phoneNumber": "1234567890", "role": "passenger", "driverStatus": "not applicable", "id": "64ec26c23ef09575c4838c64" }, "driver": null, "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" }, "rideStatus": "cancelled", "createdAt": "2023-08-28T04:50:22.223Z", "updatedAt": "2023-08-28T09:14:29.816Z", "id": "64ec278e855903b8781efbc8" }
When the ride's accepted, the driver field will contain the driver's profile like in this case:
{ "passenger": { "firstName": "Lyndon", "lastName": "Darren", "email": "lyndon@example.com", "phoneNumber": "1234567890", "role": "passenger", "driverStatus": "not applicable", "id": "64ec26c23ef09575c4838c64" }, "driver": { "firstName": "John", "lastName": "Doe", "email": "john23@example.com", "phoneNumber": "1234567890", "role": "driver", "driverStatus": "available", "id": "64ec26c23ef09575c4838c65" }, "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" }, "rideStatus": "accepted", "createdAt": "2023-08-28T04:50:22.223Z", "updatedAt": "2023-08-28T09:14:29.816Z", "id": "64ec278e855903b8781efbc8" }
NOTE: A driver can only accept a ride if the driver's status is
available
. If the driver's status isunavailable
, the server will respond with a403
status code. A driver can only accept one ride at a time. If the driver tries to accept another ride, the server will respond with a403
status code.When the ride's completed, the driver field will contain the driver's profile like in this case:
{ "passenger": { "firstName": "Lyndon", "lastName": "Darren", "email": "lyndon@example.com", "phoneNumber": "1234567890", "role": "passenger", "driverStatus": "not applicable", "id": "64ec26c23ef09575c4838c64" }, "driver": { "firstName": "John", "lastName": "Doe", "email": "john23@example.com", "phoneNumber": "1234567890", "role": "driver", "driverStatus": "available", "id": "64ec26c23ef09575c4838c65" }, "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" }, "rideStatus": "completed", "createdAt": "2023-08-28T04:50:22.223Z", "updatedAt": "2023-08-28T09:14:29.816Z", "id": "64ec278e855903b8781efbc8" }
When the ride's cancelled, the driver field will still be set to
null
like in this case:{ "passenger": { "firstName": "Lyndon", "lastName": "Darren", "email": "lyndon@example.com", "phoneNumber": "1234567890", "role": "passenger", "driverStatus": "not applicable", "id": "64ec26c23ef09575c4838c64" }, "driver": null, "pickUpLocation": { "latitude": 37.7749, "longitude": -122.4194, "locationName": "San Francisco", "country": "United States" }, "dropOffLocation": { "latitude": 34.0522, "longitude": -118.2437, "locationName": "Los Angeles", "country": "United States" }, "rideStatus": "cancelled", "createdAt": "2023-08-28T04:50:22.223Z", "updatedAt": "2023-08-28T09:14:29.816Z", "id": "64ec278e855903b8781efbc8" }
NOTE: By default, the database will be empty. So you'll need to create a ride first otherwise you'll get 404
error status code with a message No rides found
.
- Every JWT token will contain the following fields:
The
{ "sub": "64ec26c23ef09575c4838c64", "role": "passenger", "iat": 1629999999, "exp": 1630000000 }
sub
field will contain the user's id, therole
field will contain the user's role, theiat
field will contain the time at which the token was issued, and theexp
field will contain the time at which the token will expire. - The API uses
JWT
for authentication and authorization. TheJWT
tokens are signed using theJWT_SECRET
value provided in the.env
file. - The API uses
PassportJS
for authentication and authorization. ThePassportJS
strategy used isJWTStrategy
which is configured insrc/auth/strategies/jwt.strategy.ts
file. - I chose to use
JWT
for authentication and authorization because it's stateless and can be used in microservices architecture. It's also scales well in case the API needs to be scaled in the future.
# run unit tests
$ npm test
winterrdog - GitHub
high-ride-api
is of Unlicense.