<repo_name>GabrielCordeiroDev/firebase-cloud-functions<file_sep>/functions/src/controllers/UpdateEntryController.ts
import {Request, Response} from "express";
import {db} from "../config/firebase";

export class UpdateEntryController {
  async handle(req: Request, res: Response): Promise<Response> {
    const {body: {title, text}, params: {entryId}} = req;

    const entry = db.collection("entries").doc(entryId);
    const currentEntryData = (await entry.get()).data() || {};

    const entryObject = {
      id: entry.id,
      title: title || currentEntryData.title,
      text: text || currentEntryData.text,
    };

    await entry.set(entryObject).catch((err) => {
      throw new Error(err.message);
    });

    return res.json({
      status: "success",
      message: "entry updated successfully",
      data: entryObject,
    });
  }
}
<file_sep>/functions/src/controllers/DeleteEntryController.ts
import {Request, Response} from "express";
import {db} from "../config/firebase";

export class DeleteEntryController {
  async handle(req: Request, res: Response): Promise<Response> {
    const {entryId} = req.params;

    const entry = db.collection("entries").doc(entryId);

    const doc = await entry.get();
    if (!doc.exists) throw new Error("This entry does not exist.");

    await entry.delete().catch((err) => {
      throw new Error(err.message);
    });

    return res.json({
      status: "success",
      message: "entry deleted successfully",
    });
  }
}
<file_sep>/README.md
<h1 align="center">
  Firebase Cloud Functions
</h1>

<p align="center">
  <img alt="GitHub top language" src="https://img.shields.io/github/languages/top/GabrielCordeiroDev/firebase-cloud-functions">

  <a href="https://www.linkedin.com/in/dev-gabriel-cordeiro/">
    <img alt="Made by" src="https://img.shields.io/badge/made%20by-Gabriel%20Cordeiro-gree">
  </a>
  
  <img alt="Repository size" src="https://img.shields.io/github/repo-size/GabrielCordeiroDev/firebase-cloud-functions">
  
  <img alt="GitHub" src="https://img.shields.io/github/license/GabrielCordeiroDev/firebase-cloud-functions">
</p>

<p align="center">
  <a href="#-about-the-project">About the project</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
  <a href="#-technologies">Technologies</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
  <a href="#-getting-started">Getting started</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;
  <a href="#-license">License</a>
</p>

<p id="insomniaButton" align="center">
  <a href="https://insomnia.rest/run/?label=Firebase%20Cloud%20Functions&uri=https%3A%2F%2Fraw.githubusercontent.com%2FGabrielCordeiroDev%2Ffirebase-cloud-functions%2Fmain%2FInsomnia.json" target="_blank"><img src="https://insomnia.rest/images/run.svg" alt="Run in Insomnia"></a>
</p>

## 👨🏻‍💻 About the project

This is a Serverless API developed with Firebase Cloud Functions for learning purposes.

## 🚀 Technologies

Technologies that I used to develop this API:

- [Node.js](https://nodejs.org/en/)
- [TypeScript](https://www.typescriptlang.org/)
- [Firebase](https://firebase.google.com/)
- [Express](https://expressjs.com/pt-br/)
- [Eslint](https://eslint.org/)

## 💻 Getting started

Import the `Insomnia.json` on Insomnia App or click on [Run in Insomnia](#insomniaButton) button.

### Requirements

- [Node.js](https://nodejs.org/en/)
- [Yarn](https://classic.yarnpkg.com/) or [npm](https://www.npmjs.com/)
- [Firebase CLI](https://firebase.google.com/docs/cli)

> Obs.: You will need to create a [Firebase](https://firebase.google.com/) account and also a project to make Cloud Functions and Firestore Database available.

**Clone the project and access the folder**

```bash
$ git clone https://github.com/GabrielCordeiroDev/firebase-cloud-functions
$ cd firebase-cloud-functions/functions
```

**Environment Setting**

```bash
$ firebase login
$ firebase functions:config:set private.key="" project.id="" client.email=""
```

**Follow the steps below**

```bash
# Install the dependencies
$ yarn

# Make a copy of '.env.example' to '.env'
# and set with YOUR environment variables
$ cp .env.example .env
```

**Deploy**

```
$ yarn deploy
```

## 📝 License

This project is licensed under the MIT License - see the [LICENSE](https://github.com/GabrielCordeiroDev/firebase-cloud-functions/blob/main/LICENSE) file for details.
<file_sep>/functions/src/controllers/AddEntryController.ts
import {Request, Response} from "express";
import {db} from "../config/firebase";

export class AddEntryController {
  async handle(req: Request, res: Response): Promise<Response> {
    const {title, text} = req.body;

    const entry = db.collection("entries").doc();

    const entryObject = {
      id: entry.id,
      title,
      text,
    };

    await entry.set(entryObject);

    return res.status(201).json({
      status: "success",
      message: "entry added successfully",
      data: entryObject,
    });
  }
}
<file_sep>/functions/src/controllers/GetAllEntriesController.ts
import {Request, Response} from "express";
import {db} from "../config/firebase";

export class GetAllEntriesController {
  async handle(_: Request, res: Response): Promise<Response> {
    const allEntries: FirebaseFirestore.DocumentData[] = [];

    const querySnapshot = await db.collection("entries").get();
    querySnapshot.forEach((doc) => allEntries.push(doc.data()));

    return res.json(allEntries);
  }
}
<file_sep>/functions/src/routes.ts
import {Router} from "express";
import {AddEntryController} from "./controllers/AddEntryController";
import {DeleteEntryController} from "./controllers/DeleteEntryController";
import {GetAllEntriesController} from "./controllers/GetAllEntriesController";
import {UpdateEntryController} from "./controllers/UpdateEntryController";

const router = Router();

const addEntryController = new AddEntryController();
const getAllEntriesController = new GetAllEntriesController();
const updateEntryController = new UpdateEntryController();
const deleteEntryController = new DeleteEntryController();

router.post("/entry", addEntryController.handle);
router.get("/entry", getAllEntriesController.handle);
router.put("/entry/:entryId", updateEntryController.handle);
router.delete("/entry/:entryId", deleteEntryController.handle);

export {router};
<file_sep>/functions/src/index.ts
import * as functions from "firebase-functions";
import "express-async-errors";
import * as express from "express";
import {router} from "./routes";

const app = express();

app.use(router);

app.use((
    err: Error,
    req: express.Request,
    res: express.Response,
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    _: express.NextFunction) => {
  if (err instanceof Error) {
    return res.status(400).json({
      status: "error",
      message: err.message,
    });
  }

  return res.status(500).json({
    status: "error",
    message: "Internal Server Error",
  });
});


exports.app = functions.https.onRequest(app);
