# Express API

[Express](https://expressjs.com) is a minimal and flexible Node.js web application framework. We will be using it to build our API.

Let's create an endpoint to get an emoji.

Here's the list of what we have to do:

* Delete`Emoji.ts` and `Emoji.spec.ts`.
* Install `express`
* Create a `routers` folder to keep our files organised
* Create a new file`/routers/emoji`
* Create a new file `app.ts`
* Create a new file `index.ts` (our main file)
* Update our config with `shared.ts` and `.env` to add `port`.
* To simplify imports, add`index.ts` exporting all the relevant files to each folder.

We will create the following file structure

```
.
├── app.ts
├── index.ts
└── routers
    ├── Emoji
    |   ├── index.spec.ts
    |   └── index.ts
    └── index.ts
```

{% code title="\~/packages/api" %}

```
$ yarn add express @types/express
```

{% endcode %}

To keep things clean, each top-level endpoint will have a router. \
The router defines the `GET, POST, PUT` and `DELETE`.

{% tabs %}
{% tab title="routers/emoji/router.ts" %}

```typescript
import { Router, Request, Response } from "express";
import { getFetch } from "../../containers";

export class EmojiRouter {
  public readonly router = Router();
  private readonly _fetch = getFetch();
  constructor() {
    this.router.get("/:emoji", this.get);
  }
  get = async (request: Request, response: Response) => {
    const { emoji } = request.params;
    const jsonResponse = await this._fetch("https://api.github.com/emojis");
    const data = await jsonResponse.json();
    if (!data[emoji]) {
      return response.status(404).json({ error: "emoji not found" });
    }
    return response.json({ emoji: data[emoji] });
  };
}
```

{% endtab %}

{% tab title="routers/index.ts" %}

```typescript
export * from "./emoji";
```

{% endtab %}
{% endtabs %}

We can test any method by mocking `request` and `response` using `jest.fn().mockReturnValue()`.

{% code title="routers/emoji/index.spect.ts" %}

```typescript
import fetch from "node-fetch";

import { EmojiRouter } from "./index";
import { setFetch } from "../../containers";
import { Request, Response } from "express";

describe("routes/emoji", () => {
  describe("/get/:emoji", () => {
    it("should fetch and return emoji passed as a param", async () => {
      // setup everything we need
      const computer = "mocked url";
      const data = { computer };
      const json = jest.fn().mockResolvedValue(data);
      const fetchMock = jest.fn(() => ({ json }));
      setFetch((fetchMock as unknown) as typeof fetch);

      const router = new EmojiRouter();

      // execute the code we want to test
      const request = ({ params: { emoji: "computer" } } as unknown) as Request;
      const response = ({} as unknown) as Response;
      response.json = jest.fn().mockReturnValue(response);
      await router.get(request, response);

      // assert the result
      expect(response.json).toHaveBeenCalledWith({ emoji: computer });
    });
    it("should return an error if not found", async () => {
      // setup everything we need
      const computer = "mocked url";
      const data = { computer };
      const json = jest.fn().mockResolvedValue(data);
      const fetchMock = jest.fn(() => ({ json }));
      setFetch((fetchMock as unknown) as typeof fetch);

      const router = new EmojiRouter();

      // execute the code we want to test
      const request = ({
        params: { emoji: "inexistent" }
      } as unknown) as Request;
      const response = ({} as unknown) as Response;
      response.status = jest.fn().mockReturnValue(response);
      response.json = jest.fn().mockReturnValue(response);
      await router.get(request, response);

      // assert the result
      expect(response.status).toHaveBeenCalledWith(404);
      expect(response.json).toHaveBeenCalledWith({ error: "emoji not found" });
    });
  });
});
```

{% endcode %}

{% hint style="info" %}
When types don't match, we use `as unknown` first to force TypeScript to accept the  other type. This is useful when we are mocking params.
{% endhint %}

We need a way to bootstrap our app (set containers, connect to the database, ...) and create an express app.

{% code title="app.ts" %}

```typescript
import * as express from "express";
import fetch from "node-fetch";
import { setFetch } from "./containers";
import { EmojiRouter } from "./routers";

export const bootstrap = async () => {
  setFetch(fetch);
  // connect to db here later...
};

export const createApp = () => {
  const app = express();
  app.use("/emoji", new EmojiRouter().router);
  return { app };
};
```

{% endcode %}

Our main file requires it and starts the server:

{% code title="index.ts" %}

```typescript
require("dotenv").config();

import { bootstrap, createApp } from "./app";
import { config } from "./config";

(async () => {
  await bootstrap();
  const { app } = createApp();
  app.listen(config.port, () => {
    console.info("Server listing on port " + config.port);
  });
})();
```

{% endcode %}

> Note: `sharedConfig` and `.env` needs to be updated to add `port`.

{% tabs %}
{% tab title="config/shared.ts" %}

```typescript
export interface SharedConfig {
  logLevel: string;
  port: number;
}

export const sharedConfig = {
  logLevel: process.env.LOG_LEVEL || "info", // we will come back to logs later
  port: parseInt(process.env.PORT || "1234")
};

```

{% endtab %}

{% tab title=".env" %}

```
CONFIG_ENV=local
LOG_LEVEL=debug
PORT=3000
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
[`express`](https://github.com/florianherrengt/book-code/tree/express) branch available on GitHub.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://tutorial.specian.co.uk/backend/express-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
