A comprehensive REST API for managing the greatest video games, featuring:
- Authentication with JSON Web Tokens (JWT)
- Image upload and processing (resize) with
sharp
- Storage of images on Netlify Blobs CDN (
@netlify/blobs
) - CRUD operations for games, including filtering, pagination, and Algolia-powered search
- OpenAPI (Swagger) documentation
- Unit tests with Jest and Supertest
- Code organized into controllers, routes, middleware, and models
- Built with Node.js, Express, and MongoDB (Mongoose)
- Prerequisites
- Installation
- Environment Variables
- Project Structure
- Usage
- API Endpoints
- Algolia Integration
- Netlify Blobs Integration
- Testing
- License
- Node.js (v16+ recommended, Node 18+ tested)
- npm (comes with Node.js)
- MongoDB (local or Atlas cluster)
- Algolia Account (for search, free tier available)
- Netlify Account (for Blobs, free tier available)
-
Clone the repository:
git clone <your-repo-url> cd videogame-api
-
Install dependencies:
npm install
-
Copy the example environment file:
cp .env.example .env
-
Fill in the environment variables in
.env
(see the next section).
Rename .env.example
to .env
and set the following:
# MongoDB connection URI (local or Atlas)
MONGO_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/videogames?retryWrites=true&w=majority
# JWT secret key
JWT_SECRET=YourSuperSecretKey
# Algolia credentials
ALGOLIA_APP_ID=YourAlgoliaAppID
ALGOLIA_ADMIN_KEY=YourAlgoliaAdminAPIKey
ALGOLIA_SEARCH_KEY=YourAlgoliaSearchOnlyAPIKey
# Netlify Blobs token
NETLIFY_BLOBS_TOKEN=YourNetlifyBlobsAccessToken
- MONGO_URI: Connection string to MongoDB. For Atlas, include database name (e.g.,
videogames
). - JWT_SECRET: A random string to sign JWT tokens.
- ALGOLIA_APP_ID / ALGOLIA_ADMIN_KEY / ALGOLIA_SEARCH_KEY: Credentials from your Algolia dashboard.
- NETLIFY_BLOBS_TOKEN: Token from your Netlify Blobs settings.
videogame-api/
โโโ src/
โ โโโ controllers/
โ โ โโโ authController.js
โ โ โโโ gamesController.js
โ โโโ middleware/
โ โ โโโ auth.js
โ โ โโโ permissions.js
โ โโโ models/
โ โ โโโ Game.js
โ โ โโโ User.js
โ โโโ routes/
โ โ โโโ auth.js
โ โ โโโ games.js
โ โโโ swagger.js
โ โโโ app.js
โโโ scripts/
โ โโโ seed.js
โ โโโ syncAlgolia.js
โโโ tests/
โ โโโ auth.test.js
โ โโโ games.test.js
โโโ babel.config.cjs
โโโ jest.config.js
โโโ package.json
โโโ .env.example
โโโ README.md
- src/controllers/: Request handlers (business logic) for Auth and Games.
- src/middleware/: Authentication (JWT verification) and permission checks.
- src/models/: Mongoose schemas for
User
andGame
. - src/routes/: Route definitions that delegate to controllers and middleware.
- src/swagger.js: Swagger/OpenAPI setup.
- src/app.js: Entry point; configures Express, connects to MongoDB, and mounts routes.
- scripts/:
seed.js
: Seed the database with 100 sample games.syncAlgolia.js
: Sync MongoDB data to Algolia index.
- tests/: Unit tests written with Jest + Supertest.
- babel.config.cjs: Babel configuration for Jest.
- jest.config.js: Jest configuration (transform, test matching, ignore patterns).
Before starting, populate the MongoDB database with sample data:
npm run seed
This script (scripts/seed.js
) will:
- Connect to MongoDB using
process.env.MONGO_URI
. - Delete all existing documents in the
games
collection. - Insert 100 sample game documents.
To start in development mode (using nodemon
):
npm run dev
To start in production mode:
npm start
The server listens on port 3000 by default (process.env.PORT || 3000
).
Visit http://localhost:3000
once running.
After starting the server, Swagger UI is available at:
http://localhost:3000/api-docs
You will see all endpoints, request parameters, response schemas, and security requirements.
-
POST
/auth/register
Register a new user.Request Body (JSON):
{ "username": "string", "password": "string" }
Responses:
201
: User created400
: Missing fields or user already exists
-
POST
/auth/login
Login and receive a JWT token.Request Body (JSON):
{ "username": "string", "password": "string" }
Responses:
200
: Returns{ "token": "JWT_TOKEN" }
400
: Invalid credentials
All /games
requests return/accept JSON, except POST /games
which uses multipart/form-data
for image upload.
-
GET
/games
Get a paginated list of games (with optional filters).Query Parameters:
page
(integer, default1
)limit
(integer, default10
)genre
(string)platform
(string)developer
(string)minScore
(integer)maxScore
(integer)q
(string): Title search (regex)
Response:
{ "total": 100, "page": 1, "pageSize": 10, "games": [ { "_id": "string", "title": "string", "platform": ["string"], "releaseYear": 2020, "genre": "string", "developer": "string", "metascore": 85, "imageUrl": "https://...", "createdBy": "userId" } ], "_links": { "self": "http://localhost:3000/games?page=1&limit=10", "first": "...", "last": "...", "prev": "...", "next": "..." } }
-
GET
/games/search?q=searchterm
Search games via Algolia index.Query Parameter:
q
(string, required)
Response:
[ { "objectID": "string", "title": "string", "platform": ["string"] } ]
-
GET
/games/:id
Get a single game by its ID.Path Parameter:
id
(string, Game ObjectId)
Response:
{ "_id": "string", "title": "string", "platform": ["string"], "releaseYear": 2020, "genre": "string", "developer": "string", "metascore": 85, "imageUrl": "https://...", "createdBy": "userId" }
404
if not found.
-
POST
/games
Create a new game. Requires Authorization header:Bearer JWT_TOKEN
.Content-Type:
multipart/form-data
Form Data Fields:
title
(string, required)platform
(string, required)releaseYear
(integer, required)genre
(string, required)developer
(string, required)metascore
(integer, required)image
(file, optional): JPEG/PNG image
Response:
{ "_id": "string", "title": "string", "platform": ["string"], "releaseYear": 2020, "genre": "string", "developer": "string", "metascore": 85, "imageUrl": "https://cdn.netlify.com/...", "createdBy": "userId" }
401
if not authenticated.
-
PUT
/games/:id
Update an existing game. Requires Authorization header:Bearer JWT_TOKEN
.Path Parameter:
id
(string, Game ObjectId)
Request Body (JSON):
- Any of:
title
,platform
,releaseYear
,genre
,developer
,metascore
Response:
{ "_id": "string", "title": "updated string" }
401
if not authenticated.403
if not the creator.404
if not found.
-
DELETE
/games/:id
Delete a game. Requires Authorization header:Bearer JWT_TOKEN
.Path Parameter:
id
(string, Game ObjectId)
Response:
{ "message": "Game deleted" }
401
if not authenticated.403
if not the creator.404
if not found.
-
Create an Algolia account (https://www.algolia.com) and obtain:
ALGOLIA_APP_ID
ALGOLIA_ADMIN_API_KEY
ALGOLIA_SEARCH_ONLY_API_KEY
-
Set these keys in
.env
:ALGOLIA_APP_ID=your_app_id ALGOLIA_ADMIN_KEY=your_admin_key ALGOLIA_SEARCH_KEY=your_search_key
-
Sync data after seeding the database:
npm run sync:algolia
- This script (
scripts/syncAlgolia.js
) fetches all games from MongoDB, formats them with anobjectID
and uploads to Algolia index namedvideogames
.
- This script (
-
Search endpoint
/games/search?q=term
uses theALGOLIA_SEARCH_KEY
to query the index.
- Create a Netlify account (https://www.netlify.com) and enable Netlify Large Media / Blobs.
- Generate a personal access token for Blobs (in Netlify site settings โBlobsโ).
- Set
NETLIFY_BLOBS_TOKEN
in.env
. - When creating a game with an image, the controller:
- Resizes the uploaded image with
sharp
. - Uploads the buffer to Netlify Blobs via
@netlify/blobs.put(...)
. - Stores the returned
blob.url
in theimageUrl
field of theGame
document.
- Resizes the uploaded image with
Unit tests are written with Jest and Supertest. A Babel pipeline handles ESM imports.
- Ensure MongoDB is running (local or Atlas, matching
MONGO_URI
). - Run tests:
This executes all
npm test
*.test.js
files undertests/
.
-
jest.config.js
:export default { testEnvironment: "node", transform: { "^.+\.js$": "babel-jest" }, testMatch: ["<rootDir>/tests/**/*.test.js"], moduleFileExtensions: ["js", "json", "node"], transformIgnorePatterns: [ "/node_modules/(?!(?:@netlify/blobs)/)" ], testPathIgnorePatterns: ["/node_modules/"] };
- Uses
babel-jest
to transform ESM syntax. - Includes
@netlify/blobs
in the transformation pipeline.
- Uses
-
babel.config.cjs
:module.exports = { presets: [ [ "@babel/preset-env", { targets: { node: "current" }, modules: "auto" } ] ] };
-
Directory of test files:
tests/auth.test.js
andtests/games.test.js
.
This project is licensed under the MIT License.
Enjoy building with the VideoGame API!