TheBookKnight / saints-api

Learning how to build an API service.
0 stars 0 forks source link

API Tutorial: LIST Request #5

Open TheBookKnight opened 2 months ago

TheBookKnight commented 2 months ago

Setup LIST request to list a batch of saints.

Continue using ExpressJS and REST API tutorial https://www.mongodb.com/resources/languages/express-mongodb-rest-api-tutorial

TheBookKnight commented 2 months ago

I'll need to have at least 10 saints. So, I can teach pagination. Will have to update the saints.json

TheBookKnight commented 2 months ago

Get more saints here: https://www.catholic.org/saints/

TheBookKnight commented 2 months ago

This discusses pagination, will look into it https://www.mongodb.com/docs/manual/reference/method/cursor.skip/#pagination-example

TheBookKnight commented 2 months ago

Used Phind AI, and they suggested this

To create a GET request that can filter by a URL flag in your ExpressJS API service connected to MongoDB, you can follow these steps:

Step 1: Set up the route

First, let's set up the route handler for the GET request:

app.get('/api/items', async (req, res) => {
  const query = req.query;

  // Process the query parameters here

  // Fetch filtered items from MongoDB
  const filteredItems = await fetchFilteredItems(query);

  res.json(filteredItems);
});

Step 2: Create a function to filter items

Next, let's create a function to fetch filtered items from MongoDB:

async function fetchFilteredItems(query) {
  const collection = db.collection('items'); // Replace 'items' with your collection name

  const cursor = collection.find({});

  if (query.food) {
    cursor.filter({ type: 'food' });
  }

  if (query.category) {
    cursor.filter({ category: query.category });
  }

  // Add more filters as needed

  const items = await cursor.toArray();
  return items;
}

Step 3: Handle pagination (optional)

If you want to support pagination, you can modify the function to accept page and limit parameters:

async function fetchFilteredItems(query, page = 1, limit = 10) {
  const collection = db.collection('items');

  const cursor = collection.find({});

  if (query.food) {
    cursor.filter({ type: 'food' });
  }

  // Apply other filters

  const totalItems = await collection.countDocuments(cursor);

  const startIndex = (page - 1) * limit;
  const endIndex = page * limit;

  const items = await cursor.skip(startIndex).limit(limit).toArray();

  return {
    items,
    totalPages: Math.ceil(totalItems / limit),
    currentPage: page,
    itemCount: items.length
  };
}

Step 4: Update the route handler

Update the route handler to handle pagination:

app.get('/api/items', async (req, res) => {
  const query = req.query;
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;

  try {
    const result = await fetchFilteredItems(query, page, limit);
    res.json(result);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Key Points to Consider:

  1. Query Parameter Handling: ExpressJS automatically parses query parameters, so you can access them directly via req.query.

  2. Filtering: You can add more filters based on the query parameters. For example, if you have a parameter category, you can filter by it.

  3. Pagination: Implementing pagination helps manage large datasets efficiently.

  4. Error Handling: Always include error handling to gracefully manage unexpected situations.

  5. Validation: Consider adding input validation to ensure the query parameters are valid.

Code Implementation:

Here's a complete example incorporating all the steps:

const express = require('express');
const MongoClient = require('mongodb').MongoClient;

const app = express();
app.use(express.json());

let db;

MongoClient.connect('mongodb://localhost:27017/mydb', (err, client) => {
  if (err) {
    console.error(err);
    return;
  }
  db = client.db('mydb');
  app.listen(3000, () => {
    console.log('Server running on port 3000');
  });
});

async function fetchFilteredItems(query, page = 1, limit = 10) {
  const collection = db.collection('items');

  const cursor = collection.find({});

  if (query.food) {
    cursor.filter({ type: 'food' });
  }

  const totalItems = await collection.countDocuments(cursor);

  const startIndex = (page - 1) * limit;
  const endIndex = page * limit;

  const items = await cursor.skip(startIndex).limit(limit).toArray();

  return {
    items,
    totalPages: Math.ceil(totalItems / limit),
    currentPage: page,
    itemCount: items.length
  };
}

app.get('/api/items', async (req, res) => {
  const query = req.query;
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;

  try {
    const result = await fetchFilteredItems(query, page, limit);
    res.json(result);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Internal server error' });
  }
});

Summary:

This implementation creates a GET endpoint that filters items based on query parameters. It supports pagination and returns the filtered results along with pagination metadata. The code follows best practices by:

  1. Using async/await for better readability and error handling.
  2. Implementing pagination to manage large datasets efficiently.
  3. Separating concerns by creating a dedicated function for fetching filtered items.
  4. Providing clear error responses in case of internal server errors.
  5. Using MongoDB's native driver for interacting with the database.

This solution provides a flexible foundation for building a RESTful API with filtering capabilities. You can extend it further by adding more complex filters, validating inputs, or implementing additional features as needed.