Shopping website REST API in Golang
Table of Contents
The shopping website backend implements these features:
- Show all available items
- Purchase a single item (logged-in user)
- Show purchased items (logged-in user)
- Show details for a purchased item (logged-in user)
- Register a new user
- Login and logout for users/admin
- Public dashboard with public statistics
- Dashboard with statistics about purchased items (admin only)
This project is for educational purposes.
Path | Method | Required JSON | Header | Description |
---|---|---|---|---|
/statistics | GET | Overall statistics for the landing page | ||
/items | GET | Show all available items | ||
/items/:id | GET | Show the details for an item | ||
/items | POST | name,price,details,producer | Authorization: Bearer | Add an item to the shop store (admin only) |
/items/:id | PUT | name,price,details,producer | Authorization: Bearer | Update the details for the specified item (admin only) |
/items/:id | DELETE | Authorization: Bearer | Delete an item from the shop store (admin only) | |
/items/:id/purchase | POST | Authorization: Bearer | Purchase the item for the logged-in user | |
/users/me/orders | GET | Authorization: Bearer | Show all the orders for the logged-in user | |
/users/me/orders/:id/items | GET | Authorization: Bearer | Show the details for the specified order | |
/auth/login | POST | username,password | The username and password you want to login with | |
/auth/logout | POST | Logout the current user | ||
/auth/refresh | POST | Refresh the JWT token | ||
/auth/register | POST | username,password, firstname,lastname | Authorization: Bearer | Register a new user |
/orders | GET | Authorization: Bearer | Get all the orders | |
/orders/:id | GET | Authorization: Bearer | Get the specified order | |
/orders/:id/pay | POST | Authorization: Bearer | Pay for the order | |
/admin/statistics | GET | Authorization: Bearer | Admin-only dashboard |
- Golang (>= 1.17)
- MySQL (5.7)
- Postman - https://www.getpostman.com/
- Stripe API Key - https://dashboard.stripe.com/account/apikeys
- Dokku on a server (optional) - https://dokku.viewdocs.io/dokku/getting-started/installation
- Docker Compose (optional) - https://docs.docker.com/compose/install/
- Docker (optional) - https://docs.docker.com/install/
-
Launch the Docker Compose file. It will start the MySQL and web application containers. (Ports 3306 and 8080)
docker-compose up -d
-
Open the browser and navigate to http://localhost:8080
Notes:
- The MySQL database is provided by Heroku and its current version is 8.0;
On the server, you can deploy the application using the following command:
-
Create a new app on Dokku
dokku apps:create <app-name>
-
Set the environment variables
# Set the following environment variables only if DATABASE_URL is not set dokku config:set <app-name> DB_HOST=<db-host> DB_PORT=<db-port> DB_USER=<db-user> DB_PASSWORD=<db-password> DB_NAME=<db-name> # Set the JWT_SECRET environment variable (e.g. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9) dokku config:set <app-name> JWT_SECRET=<jwt-secret> # Set the TIMEZONE environment variable (e.g. Paris/Europe) dokku config:set <app-name> TIMEZONE=<timezone> # Set the PORT environment variable (e.g. 8080) dokku config:set <app-name> PORT=<port> # Set the STRIPE_SECRET_KEY environment variable (e.g. sk_test_...) dokku config:set <app-name> STRIPE_API_KEY=<stripe-api-key>
-
Install the MySQL plugin
sudo dokku plugin:install https://github.com/dokku/dokku-mysql.git mysql
-
Create the database. In this case, we use MySQL version 5.7.
export MYSQL_IMAGE_VERSION=5.7 dokku mysql:create <app-name-db>
-
Link the database container to the app
dokku mysql:link <app-name> <app-name-db>
-
Select the Dockerfile as a builder
dokku builder:set <app-name> selected dockerfile
On the development machine, you can run the following commands to deploy the application:
-
Setup the remote repository
git remote add dokku dokku@<dokku-host>:<app-name>
-
Deploy the app
git push dokku master
- The API is accessible on the development machine (e.g. http://localhost:8080)
- The API is accessible on the server machine (e.g. http://:8080)
- The client application used is
httpie
(https://httpie.org/)
http POST http://localhost:8080/auth/login username=<username> password=<password>
Output:
{
"code": 200,
"expire": "2021-12-21T15:07:32Z",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDAxODU2NTIsIm9yaWdfaWF0IjoxNjQwMTgyMDUyLCJ1c2VySUQiOjZ9.yg-a4SgeKgK74fsb2PrnREFYPIwst1WFKM5Xga1t2E4"
}
http GET http://localhost:8080/users/me/orders "Authorization:Bearer <token>"
Output:
HTTP/1.1 200 OK
Content-Length: 634
Content-Type: application/json; charset=utf-8
Date: Wed, 22 Dec 2021 14:10:18 GMT
{
"data": [
{
"created_at": "2021-12-22T13:51:35Z",
"id": 6,
"items": null,
"payment_id": "432423423423",
"payment_method": "card",
"status": "created",
"total_price": 110.13,
"updated_at": "2021-12-22T13:51:35Z",
"user_id": 6
},
{
"created_at": "2021-12-22T13:51:35Z",
"id": 7,
"items": null,
"payment_id": "532525454545",
"payment_method": "paypal",
"status": "paid",
"total_price": 13.2,
"updated_at": "2021-12-22T13:51:35Z",
"user_id": 6
},
{
"created_at": "2021-12-22T13:51:35Z",
"id": 8,
"items": null,
"payment_id": "232342342324234",
"payment_method": "card",
"status": "created",
"total_price": 213.41,
"updated_at": "2021-12-22T13:51:35Z",
"user_id": 6
}
],
"message": "OK",
"success": true
}
http GET http://localhost:8080/items
Output:
HTTP/1.1 200 OK
Content-Length: 1254
Content-Type: application/json; charset=utf-8
Date: Tue, 21 Dec 2021 22:36:27 GMT
{
"data": [
{
"category": "garden",
"created_at": "2021-12-21T15:52:22Z",
"description": "Et sunt culpa unde distinctio quos.",
"id": 1,
"name": "The Misty Cup",
"price": 244.3,
"producer": "Beier Ltd",
"updated_at": "2021-12-21T15:52:22Z"
},
{
"category": "home",
"created_at": "2021-12-21T15:52:22Z",
"description": "Quos vel ut esse incidunt minima minima quae.",
"id": 2,
"name": "The Begging Jug",
"price": 302.1,
"producer": "Parker, Hyatt and Kris",
"updated_at": "2021-12-21T15:52:22Z"
},
{
"category": "electronic",
"created_at": "2021-12-21T15:52:22Z",
"description": "Earum aliquid deleniti beatae quibusdam inventore itaque velit voluptas.",
"id": 3,
"name": "The Expensive Flower",
"price": 110.13,
"producer": "Kutch Ltd",
"updated_at": "2021-12-21T15:52:22Z"
},
{
"category": "garden",
"created_at": "2021-12-21T15:52:22Z",
"description": "Quae quis laborum odio provident.",
"id": 4,
"name": "The Challenging Stove Salon",
"price": 13.2,
"producer": "Wisozk-Larson",
"updated_at": "2021-12-21T15:52:22Z"
},
{
"category": "home",
"created_at": "2021-12-21T15:52:22Z",
"description": "Enim provident velit blanditiis ut exercitationem.",
"id": 5,
"name": "The Performing Window Boutique",
"price": 213.41,
"producer": "Dickinson, Collins and Cremin",
"updated_at": "2021-12-21T15:52:22Z"
}
],
"message": "Get all items",
"success": true
}
http GET http://localhost:8080/items/1
Output:
HTTP/1.1 200 OK
Content-Length: 257
Content-Type: application/json; charset=utf-8
Date: Tue, 21 Dec 2021 22:37:57 GMT
{
"data": {
"category": "garden",
"created_at": "2021-12-21T15:52:22Z",
"description": "Et sunt culpa unde distinctio quos.",
"id": 1,
"name": "The Misty Cup",
"price": 244.3,
"producer": "Beier Ltd",
"updated_at": "2021-12-21T15:52:22Z"
},
"message": "Get item",
"success": true
}
http POST http://localhost:8080/items name="The Misty Cup" price="244.3" producer="Belkin" category="garden" description="Et sunt culpa unde distinctio quos."
Output:
HTTP/1.1 201 Created
Content-Length: 53
Content-Type: application/json; charset=utf-8
Date: Tue, 21 Dec 2021 23:01:28 GMT
{
"data": null,
"message": "Created item",
"success": true
}
- Purchase an item and create a new order.
http POST http://localhost:8080/items/1/purchase "Authorization:Bearer <token>"
Output:
HTTP/1.1 201 Created
Content-Length: 485
Content-Type: application/json; charset=utf-8
Date: Wed, 22 Dec 2021 14:12:57 GMT
{
"data": {
"created_at": "0001-01-01T00:00:00Z",
"id": 9,
"items": [
{
"category": "home",
"created_at": "2021-12-22T13:51:35Z",
"description": "Quos vel ut esse incidunt minima minima quae.",
"id": 2,
"name": "The Begging Jug",
"price": 302.1,
"producer": "Parker, Hyatt and Kris",
"updated_at": "2021-12-22T13:51:35Z"
}
],
"payment_id": "",
"payment_method": "stripe",
"status": "created",
"total_price": 302.1,
"updated_at": "0001-01-01T00:00:00Z",
"user_id": 6
},
"message": "Purchased item: order created",
"success": true
}
- Pay the order
http POST http://localhost:8080/orders/9/pay "Authorization:Bearer <token>"
Output:
HTTP/1.1 200 OK
Content-Length: 482
Content-Type: application/json; charset=utf-8
Date: Wed, 22 Dec 2021 14:13:48 GMT
{
"data": {
"created_at": "2021-12-22T14:12:57Z",
"id": 9,
"items": [
{
"category": "home",
"created_at": "2021-12-22T13:51:35Z",
"description": "Quos vel ut esse incidunt minima minima quae.",
"id": 2,
"name": "The Begging Jug",
"price": 302.1,
"producer": "Parker, Hyatt and Kris",
"updated_at": "2021-12-22T13:51:35Z"
}
],
"payment_id": "ch_3K9VZfIfr49YY8SJ2Zxl6iZJ",
"payment_method": "stripe",
"status": "paid",
"total_price": 302.1,
"updated_at": "2021-12-22T14:12:57Z",
"user_id": 6
},
"message": "OK",
"success": true
}
http GET http://localhost:8080/statistics
Output:
- Total amount of all orders
- Total registered users
- Total amount of all items
- Total amount of registered users, orders, and items for last month, last week, and yesterday
HTTP/1.1 200 OK
{
"success": true,
"message": "Statistics retrieved",
"data": {
"last_day": {
"totalAmount": 1219.8800106048584,
"totalOrders": 8,
"totalUsers": 7
},
"last_month": {
"totalAmount": 1219.8800106048584,
"totalOrders": 8,
"totalUsers": 7
},
"last_week": {
"totalAmount": 1219.8800106048584,
"totalOrders": 8,
"totalUsers": 7
},
"total_items": 5,
"total_orders": 8,
"total_users": 7
}
}
http GET http://localhost:8080/statistics/admin
Output:
- Total amount of all orders
- Total registered users
- Total amount of all items
- Total amount of registered users, orders, and items for last month, last week, and yesterday
- Best selling items
- Most profitable users
- Worst selling items
- Items not sold
HTTP/1.1 200 OK
{
"success": true,
"message": "Statistics retrieved",
"data": {
"items_not_ordered": [
{
"id": 5,
"name": "The Performing Window Boutique",
"description": "Enim provident velit blanditiis ut exercitationem.",
"price": 213.41,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 0
}
],
"last_day": {
"totalAmount": 1219.8800106048584,
"totalOrders": 8,
"totalUsers": 7
},
"last_month": {
"totalAmount": 1219.8800106048584,
"totalOrders": 8,
"totalUsers": 7
},
"last_week": {
"totalAmount": 1219.8800106048584,
"totalOrders": 8,
"totalUsers": 7
},
"least_ordered_items": [
{
"id": 4,
"name": "The Challenging Stove Salon",
"description": "Quae quis laborum odio provident.",
"price": 13.2,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 1
},
{
"id": 1,
"name": "The Misty Cup",
"description": "Et sunt culpa unde distinctio quos.",
"price": 244.3,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 2
},
{
"id": 2,
"name": "The Begging Jug",
"description": "Quos vel ut esse incidunt minima minima quae.",
"price": 302.1,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 2
},
{
"id": 3,
"name": "The Expensive Flower",
"description": "Earum aliquid deleniti beatae quibusdam inventore itaque velit voluptas.",
"price": 110.13,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 2
}
],
"most_ordered_items": [
{
"id": 1,
"name": "The Misty Cup",
"description": "Et sunt culpa unde distinctio quos.",
"price": 244.3,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 2
},
{
"id": 2,
"name": "The Begging Jug",
"description": "Quos vel ut esse incidunt minima minima quae.",
"price": 302.1,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 2
},
{
"id": 3,
"name": "The Expensive Flower",
"description": "Earum aliquid deleniti beatae quibusdam inventore itaque velit voluptas.",
"price": 110.13,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 2
},
{
"id": 4,
"name": "The Challenging Stove Salon",
"description": "Quae quis laborum odio provident.",
"price": 13.2,
"producer": "",
"category": "",
"created_at": "0001-01-01T00:00:00Z",
"updated_at": "0001-01-01T00:00:00Z",
"total_orders": 1
}
],
"total_items": 5,
"total_orders": 8,
"total_users": 7,
"users_spend_more": [
{
"id": 1,
"username": "keeling.else",
"firstname": "",
"lastname": "",
"email": "kayla.hilpert@gmail.com",
"created_at": "2021-12-22T01:35:16Z",
"updated_at": "0001-01-01T00:00:00Z",
"orders": null,
"total_spent": 656.5300064086914
},
{
"id": 3,
"username": "forrest75",
"firstname": "",
"lastname": "",
"email": "dawn47@hotmail.com",
"created_at": "2021-12-22T01:35:16Z",
"updated_at": "0001-01-01T00:00:00Z",
"orders": null,
"total_spent": 302.1000061035156
},
{
"id": 6,
"username": "test",
"firstname": "",
"lastname": "",
"email": "botsford.carlee@yahoo.com",
"created_at": "2021-12-22T01:35:16Z",
"updated_at": "0001-01-01T00:00:00Z",
"orders": null,
"total_spent": 244.3000030517578
},
{
"id": 2,
"username": "parker.annie",
"firstname": "",
"lastname": "",
"email": "ereilly@gmail.com",
"created_at": "2021-12-22T01:35:16Z",
"updated_at": "0001-01-01T00:00:00Z",
"orders": null,
"total_spent": 123.3299970626831
}
]
}
}
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the MIT license. See LICENSE
for more information.
Giovanni Liboni - giovanni@liboni.me
Project Link: https://github.com/giovanni-liboni/exercise-rest-api-shop