# [ 살펴보기 ] Hono - Helpers

Hono는 application 운영에 필요한 기능을 다양한 helpers function을 통해 제공한다. 전체 helper list는 [documentation](https://hono.dev/docs/helpers/accepts)에서 확인할 수 있으며 그 중 일부를 살펴보자.

## Accepts

Request header중 accept 관련 header를 관리할 때 사용할 수 있는 helper다. 예를 들어 client request `Accept-Language` header의 값이 accepts helper option중 supports language에 포함되어 있는 language이면 해당 langauge를 return하고 그렇지 않으면 default로 설정한 값을 return한다.

```typescript
...
import { accepts } from "hono/accepts";

const app = new Hono();

app.get('/items', (c) => {
  const accept = accepts(c, {
    header: 'Accept-Language',
    supports: ['en', 'ko', 'fr'],
    default: 'en',
  })
  return c.json({ lang: accept })
})
```

예를 들어 코드가 위와 같을 때 request `Accept-Language` header의 값이 supports에 설정한 language에 포함되어 있으면 해당 language를 return하고 그렇지 않으면 default에 선언한 en을 return한다.

Accept-Language이외에 다음 Accept 관련 header역시 accepts helper를 통해 filter할 수 있다.

```typescript
export type AcceptHeader =
  | 'Accept'
  | 'Accept-Charset'
  | 'Accept-Encoding'
  | 'Accept-Language'
  | 'Accept-Patch'
  | 'Accept-Post'
  | 'Accept-Ranges'
```

## Adapter

adapter helper에서 제공하는 env function을 통해 environment variable에 접근할 수 있다. 만약 .env 파일을 통해 application environment variable을 관리중이라면 다음 package를 추가로 설치해준다. ( NodeJS runtime 기준 )

```typescript
npm install dotenv
```

*.env*

```typescript
DATABASE_NAME=mydb
```

*index.ts*

```typescript
...
import "dotenv/config";
import { env } from "hono/adapter";

type Env = {
  DATABASE_NAME: string;
};

const app = new Hono();

app.get("/items", async (c) => {
  const { DATABASE_NAME } = env<Env>(c, "node");
  return c.json({ message: "hello world" });
});

...
```

application을 운영하는 runtime에 따라 env function이 포함하는 범위에 다소 차이가 있으니 해당 사항은 documentation을 통해 확인할 수 있다. ( *Reference* - [Supported Runtimes, Serverless Platforms and Cloud Services](https://hono.dev/docs/helpers/adapter#env) )

Adaptor helper에서 제공하는 getRuntimeKey function을 통해 현재 application이 운영되는 runtime name을 조회할 수 있다.

```typescript
...
import { env, getRuntimeKey } from "hono/adapter";

const app = new Hono();

app.get("/items", async (c) => {
  const { DATABASE_NAME } = env<Env>(c, "node");
  const runtimeName = getRuntimeKey();
  return c.json({ message: "hello world" });
});
```

## ConnInfo

client ip address나 ip type과 같은 connection 정보를 확인할 수 있는 function을 제공한다.

```typescript
...
import { getConnInfo } from 'hono/cloudflare-workers'

const app = new Hono();

app.get('/items', (c) => {
  const info = getConnInfo(c) 
  const { remote } = info;
  return c.text(`remote address : ${remote.address}`)
})
```

## Cookie

cookie를 조회하거나, 설정, 삭제를 수행할 수 있는 function을 제공한다. 다음은 cookie helper가 제공하는 function을 통해 cookie를 조회하는 예제다.

```typescript
...
import { getCookie } from 'hono/cookie'

const app = new Hono();

app.get("/items", async (c) => {
  const allCookies = getCookie(c);
  const mysnackCookie = getCookie(c, "mysnack");
  return c.json({ message: "hello world" });
});
```

cookie를 설정하여 response header에 포함할 때는 setCookie function을 사용한다.

```typescript
...
import { setCookie } from "hono/cookie";

const app = new Hono();

app.get("/items", async (c) => {
  setCookie(c, "yoursnack", "grape");
  return c.json({ message: "hello world" });
});
```

response header의 특정 cookie를 삭제하고 싶다면 deleteCookie function을 사용한다.

```typescript
...
import { deleteCookie } from "hono/cookie";

const app = new Hono();

app.get("/items", async (c) => {
  deleteCookie(c, "mysnack");
  return c.json({ message: "hello world" });
});
```

cookie을 설정할 때 cookie의 option은 다음과 같이 설정할 수 있다.

```typescript
setCookie(c, 'yoursnack', 'grape', {
  path: '/',
  secure: true,
  domain: 'example.com',
  httpOnly: true,
  maxAge: 1000,
  sameSite: 'Strict',
})
```

Hono cookie helper를 통해 cookie를 parse할 때 다음 상황에서 error가 throw된다.

* cookie name이 \_\_Secure- prefix로 시작하지만 cookie에 secure option 설정되지 않았을 때
    
* cookie name이 \_\_Host- prefix로 시작하지만 cookie에 secure option이 설정되지 않았을 때
    
* cookie name이 \_\_Host- prefix로 시작하지만 cookie의 path가 `/`가 아닐 때
    
* cookie name이 \_\_Host- prefix로 시작하지만 cookie의 domain option이 설정되어 있을 때
    
* maxAge option이 400일 이상으로 설정되었을 때
    
* expires option이 400일 이상으로 설정되었을 때
    

## html

html helper에서 제공하는 function을 통해 Javascirpt template literal안에 html code를 작성해 response로 전달할 수 있다.

```typescript
...
import { html } from "hono/html";

const app = new Hono();

app.get("/items", async (c) => {
  return c.html(html`<html>
    <head>
      <title>Item page</title>
    </head>
    <body>
      <h1>Item Page</h1>
    </body>
  </html>`);
});
```

다음과 같이 functional component 형식과 같이 구성하여 render할 수도 있다. html helper에서 제공하는 html function은 HtmlEscapeString을 return 하므로 jsx, tsx extension을 사용할 필요는 없다.

*views/layout.ts*

```typescript
import { html } from "hono/html";
import type { HtmlEscapedString } from "hono/utils/html";

type Props = {
  title: string;
  mainContent: HtmlEscapedString | Promise<HtmlEscapedString>;
};

const Layout = ({ mainContent, title }: Props) =>
  html`<html>
    <head>
      <title>${title}</title>
    </head>
    <body>
      ${mainContent}
    </body>
  </html>`;

export default Layout;
```

*views/main.ts*

```typescript
import { html } from "hono/html";

const MainPage = () =>
  html`<main>
    <div>Main Page</div>
  </main>`;

export default MainPage;
```

*index.ts*

```typescript
import Layout from "./views/Layout.js";
import MainPage from "./views/MainPage.js";
...

const app = new Hono();

app.get("/items", async (c) => {
  return c.html(<Layout title="main page" mainContent={<MainPage />} />);
});
```

## JWT

JSON Web Token을 decode 하거나 sign, verify할 때 필요한 function을 제공한다.

JWT helepr에서 제공하는 sign function을 통해 token을 생성할 수 있다.

```typescript
...
import { sign } from "hono/jwt";

const app = new Hono();

const payload = {
  sub: "testUser",
  role: "user",
  exp: Math.floor(Date.now() / 1000) + 60 * 30, // Token expires in 30 minutes
};

const secret = "secretKey";

app.get("/items", async (c) => {
  const token = await sign(payload, secret);
  return c.json({ token });
});
```

verify function을 통해 전달받은 token이 유효한 token인지 검사한다.

```typescript
...
import { sign, verify } from "hono/jwt";

...

app.get("/items", async (c) => {
  const authorization = c.req.header("authorization");
  const token = authorization ? authorization.split(" ")[1] : "";
  try {
    const result = await verify(token, secret);
    return c.json({ token });
  } catch (err) {
    return c.json({ error: "Invalid Token"}, 401);
  }
});
```

decode function을 통해 JSON web token을 decode할 수 있다.

```typescript
...
import { decode, sign, verify } from "hono/jwt";

...

app.get("/items", async (c) => {
  const authorization = c.req.header("authorization");
  const token = authorization ? authorization.split(" ")[1] : "";
  try {
    const result = decode(token);
    return c.json({ token });
  } catch (err) {
    return c.json({ error: "Invalid Token" }, 401);
  }
});
```
