This is a Go-based RESTful API for managing products in a store. It provides endpoints for creating, reading, updating, and deleting products, along with basic authentication and pagination support. The API is built using the Gin web framework and interacts with a MySQL database for data storage.
- Product CRUD operations:
GET /api/v1/products: Retrieve a list of products with pagination support.GET /api/v1/products/:id: Retrieve a specific product by its ID.POST /api/v1/products: Create a new product.PUT /api/v1/products/:id: Update an existing product.DELETE /api/v1/products/:id: Delete a product.
- Authentication:
JWT_SECRET_KEYandAUTH_ENABLEDenvironment variables control JWT authentication.- Product endpoints require a valid JWT token in the
Authorizationheader when authentication is enabled.
- Pagination:
- The
GET /api/v1/productsendpoint supports pagination usinglimitandoffsetquery parameters.
- The
- Response handling:
- Centralized response and error handling middleware provides consistent and structured responses.
- Logging:
- Uses
logrusfor structured logging.
- Uses
- Validation:
- Basic input validation is implemented using Gin's binding and validation features.
- Testing:
- Unit tests are included for services, handlers, validators, and middleware.
- Docker support:
- A
Dockerfileanddocker-compose.ymlfile are provided for easy containerization and deployment.
- A
- Go: Make sure you have Go installed on your system. You can download it from the official website: https://golang.org/dl/
- MySQL: You'll need a running MySQL server. You can install it locally or use a Docker container (see the "Running with Docker Compose" section below).
- Docker (optional): If you want to run the application using Docker, make sure you have Docker installed. You can download it from the official website: https://www.docker.com/get-started
- Docker Compose (optional): If you want to use Docker Compose for easier setup, make sure you have it installed. You can find instructions on the official website: https://docs.docker.com/compose/install/
-
Clone the repository:
git clone https://github.com/dimitrisniras/simpler-products.git cd simpler-products -
Set up environment variables:
-
Create a
.envfile in the project root directory. -
Add the following environment variables, replacing the placeholders with your actual values:
PORT=8080 LOG_LEVEL=debug # or 'trace', 'info', 'warn', 'error', 'release' DB_USER=your_db_user DB_PASSWORD=your_db_password DB_HOST=localhost # or the hostname/IP of your MySQL server DB_PORT=3306 DB_NAME=your_db_name JWT_SECRET_KEY=your_strong_secret_key AUTH_ENABLED=true # or 'false' to disable authentication
-
-
Create the database and table:
-
Connect to your MySQL server using a tool like the MySQL command-line client or MySQL Workbench.
-
Create the database specified in your
.envfile (e.g.,your_db_name). -
Execute the following SQL query to create the
productstable:CREATE TABLE Products ( id VARCHAR(255) PRIMARY KEY, name VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, price DECIMAL(10, 2) NOT NULL );
-
-
Install dependencies:
make install-deps
-
Build the executable:
make build
-
Run the application:
make run
The API will be accessible at
http://localhost:8080.
-
Build and run the containers:
docker-compose up --build
This will build the Go application image, pull the MySQL image, and start both containers. The API will be accessible at
http://localhost:8080. Remember if you're a Mac OS user you'll need to specify the DB_HOST asdocker.for.mac.localhost
-
Run unit tests:
make testThis will execute all the unit tests in the
testsdirectory.
This API utilizes JWT (JSON Web Token) for authentication. You'll need to generate an RSA key pair (private and public keys) and set the public key as an environment variable.
You can generate an RSA key pair using OpenSSL:
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem- The
private.pemfile contains your private key, which should be kept secure and not shared. - The
public.pemfile contains your public key, which will be used by the API to verify JWTs.
-
Encode the public key to Base64:
export JWT_PUBLIC_KEY=$(cat public.pem | base64 -w 0)
-
Set the environment variable:
-
Locally: Include the following line in your
.envfile, replacingyour_base64_encoded_public_keywith the actual Base64-encoded value of your public key:JWT_PUBLIC_KEY=your_base64_encoded_public_key
-
Docker Compose: Add the following environment variable to the app service in your
docker-compose.ymlfile:environment: - JWT_PUBLIC_KEY=your_base64_encoded_public_key # ... other environment variables
-
GET /api/ping-
A simple health check endpoint that returns 200 OK.
-
Does not require authentication.
-
Success Response:
{ "status": 200, }
-
-
GET /api/v1/products-
Retrieves a list of products.
-
Supports pagination using limit and offset query parameters.
-
Requires authentication (when AUTH_ENABLED is true).
-
Success Response (with pagination):
{ "status": 200, "data": [ { "id": "uuid1", "name": "Product A", "description": "Description of Product A", "price": 10.99 }, // ... other products ], "pagination": { "limit": 10, "offset": 0, "total": 25 } }- Error Response (e.g., Invalid parameters):
{ "status": 400, "errors": [ { "message": "Invalid limit parameter" } ] }
-
-
GET /api/v1/products/:id-
Retrieves a specific product by its ID.
-
Requires authentication.
-
Success Response:
{ "status": 200, "data": [{ "id": "uuid1", "name": "Product A", "description": "Description of Product A", "price": 10.99 }] }- Error Response (e.g., Product not found):
{ "status": 404, "errors": [ { "message": "product not found" } ] }
-
-
POST /api/v1/products-
Creates a new product.
-
Requires authentication.
-
Success Response:
{ "status": 201, "data": [{ "id": "uuid1", "name": "New Product", "description": "This is a new product", "price": 19.99 }] } -
Error Response (e.g., Validation errors):
{ "status": 400, "errors": [ { "message": "Name is required" }, { "message": "Price must be greater than 0" } ] }
-
-
PUT /api/v1/products/:id-
Updates an existing product.
-
Requires authentication.
-
Success Response:
{ "status": 200, "data": [{ "id": "uuid1", "name": "Updated Product", "description": "Updated description", "price": 24.95 }] }- Error Response (e.g., Product not found):
{ "status": 404, "errors": [ { "message": "product not found" } ] }
-
-
DELETE /api/v1/products/:id-
Deletes a product.
-
Requires authentication.
-
Success Response:
{} -
Error Response (e.g., Product not found):
{ "status": 404, "errors": [ { "message": "product not found" } ] }
-
curl -X POST -H "Content-Type: application/json" \
-H "Authorization: Bearer your_jwt_token" \
-d '{"name": "New Product", "description": "This is a new product", "price": 19.99}' \
http://localhost:8080/api/v1/productscurl -H "Authorization: Bearer your_jwt_token" \
http://localhost:8080/api/v1/products?limit=5&offset=0Remember to replace your_jwt_token with an actual valid JWT token if authentication is enabled.