https://github.com/elysiajs/elysia
프로젝트 생성
bun create elysia elysia_test
cd elysia_test
bun dev
$ bun run --watch src/index.ts
🦊 Elysia is running at localhost:3000
TLS(https) 지원
const app = new Elysia()
.get("/", () => "Hello Elysia")
.listen({
port: 3000,
tls: {
key: Bun.file("/Users/dajkim76/test/server.key"),
cert: Bun.file("/Users/dajkim76/test/server.crt"),
},
hostname: "0.0.0.0"
});
compression(gzip)지원
bun add elysia-compress
import { compression } from 'elysia-compress'
const app = new Elysia()
.use(compression({ threshold: 100, encodings: ['deflate', 'gzip']}))
.get("/hello", () => "Hello Elysia Hello Elysia Hello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello ElysiaHello Elysia")
logger 여러 종류가 있지만
https://github.com/PunGrumpy/logixlysia 간단하고 충분한 스펙이다.
bun add logixlysia
import logixlysia from 'logixlysia'
const logger = logixlysia({
config: {
ip: true,
logFilePath: './example.log',
customLogFormat:
'🦊 {now} {level} {duration} {method} {pathname} {status} {message} {ip}',
// logFilter: {
// level: ['ERROR', 'WARNING'],
// status: [500, 404],
// method: 'GET'
// }
}
})
const app = new Elysia()
.use(logger)
결과는..
# 콘솔로그
┌────────────────────────────────────────────────┐
│ │
│ Elysia v1.0.27 │
│ │
│ 🦊 Elysia is running at https://0.0.0.0:3000 │
│ │
└────────────────────────────────────────────────┘
🦊 Elysia is running at 0.0.0.0:3000
🦊 7/7/2024, 12:13:38 AM INFO 30ms GET /hello 200
🦊 7/7/2024, 12:13:46 AM INFO 1ms GET /hello 200
🦊 7/7/2024, 12:13:47 AM INFO 1ms GET /hello 200
🦊 7/7/2024, 12:13:47 AM INFO 844µs GET /hello 200
🦊 7/7/2024, 12:13:48 AM INFO 1ms GET
# 파일 로그
dajkim76@Kims-Mac-mini ~/test/elysia_test % cat example.log
🦊 7/7/2024, 12:13:38 AM INFO 39ms GET /hello 200
🦊 7/7/2024, 12:13:46 AM INFO 3ms GET /hello 200
🦊 7/7/2024, 12:13:47 AM INFO 3ms GET /hello 200
🦊 7/7/2024, 12:13:47 AM INFO 2ms GET /hello 200
🦊 7/7/2024, 12:13:48 AM INFO 2ms GET /hello 200
logger 두번째 winston를 지원하는 elysia-logging 이게 나아보인다.
bun add @otherguy/elysia-logging
bun add winston
import { ElysiaLogging } from "@otherguy/elysia-logging";
import { type Logger, LogFormat } from "@otherguy/elysia-logging";
import { createLogger, transports, format } from "winston";
// Define Winston logger
const logger : Logger = createLogger({
// Use the LOG_LEVEL environment variable, or default to "info"
level: Bun.env.LOG_LEVEL ?? "info",
// Use JSON format
format: format.json(),
// Log to the console
transports: [new transports.Console()],
});
const elysiaLogging = ElysiaLogging(logger, {
level: "info",
// Access logs in JSON format
format: LogFormat.JSON,
})
const app = new Elysia()
.use(elysiaLogging)
결과는
🦊 Elysia is running at 0.0.0.0:3000
{"level":"info","message":"GET /hello completed with status 200 in 20.76ms","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":20761917}}
{"level":"info","message":"GET /hello completed with status 200 in 2.129ms","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":2129250}}
{"level":"info","message":"GET /hello completed with status 200 in 618.7µs","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":618709}}
파일로 쓰는 걸로 바꿔보자..
bun add winston-daily-rotate-file
코드 수정
import { ElysiaLogging } from "@otherguy/elysia-logging";
import { type Logger, LogFormat } from "@otherguy/elysia-logging";
import * as winston from "winston";
const winstonDaily = require('winston-daily-rotate-file')
const logDir = './logs'; // log 파일을 관리할 폴더
const IS_DEVELOPMENT = true;
const dailyOptions = (level: string) => {
return {
level,
datePattern: 'YYYY-MM-DD',
dirname: logDir + `/${level}`,
filename: `%DATE%.${level}.log`,
maxFiles: 7, //7일치 로그파일 저장
zippedArchive: true, // 로그가 쌓이면 압축하여 관리
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
),
};
};
const consoleOptions = {
level: 'debug', //console은 debug 이상은 다 출력한다
format: winston.format.combine(
//winston.format.colorize({ all: true }),
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
),
};
const createTransports = () => {
const transprots : winston.transport[]= [];
if (IS_DEVELOPMENT) {
// Console에 출력은 개발모드일때만 한다.
transprots.push(new winston.transports.Console(consoleOptions));
}
transprots.push(new winstonDaily(dailyOptions('debug'))); // 404 not found
transprots.push(new winstonDaily(dailyOptions('info'))); // normal api request
transprots.push(new winstonDaily(dailyOptions('warn'))); // error api response
transprots.push(new winstonDaily(dailyOptions('error'))); // internal error
return transprots;
};
// Define Winston logger
const logger : Logger = winston.createLogger({
// Use the LOG_LEVEL environment variable, or default to "info"
level: Bun.env.LOG_LEVEL ?? "info",
// Use JSON format
format: winston.format.json(),
// Log to the console
transports: createTransports(),
});
const elysiaLogging = ElysiaLogging(logger, {
level: "info",
// Access logs in JSON format
format: LogFormat.JSON,
})
const app = new Elysia()
.use(elysiaLogging)
결과는
콘솔은
🦊 Elysia is running at 0.0.0.0:3000
{"level":"info","message":"GET /hello completed with status 200 in 17.24ms","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":17236792}}
{"level":"info","message":"GET /hello completed with status 200 in 515.6µs","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":515583}}
{"level":"info","message":"GET /hello completed with status 200 in 408.4µs","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":408417}}
파일은
dajkim76@Kims-Mac-mini ~/test/elysia_test/logs/info % cat 2024-07-07.info.log
{"level":"info","message":"GET /hello completed with status 200 in 17.24ms","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":17236792}}
{"level":"info","message":"GET /hello completed with status 200 in 515.6µs","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":515583}}
{"level":"info","message":"GET /hello completed with status 200 in 408.4µs","request":{"ip":"127.0.0.1","method":"GET","url":{"params":{},"path":"/hello"}},"response":{"status_code":200,"time":408417}}
NestJS에서 쓰든 코드를 가져왔는데 일단 동작은 한다. 파일로 쓰기도 잘 되고, logger.error("")로 했을 때 error 디렉토리에도 error만 분리되서 잘 저장된다. 하지만 timestamp가 저장되지 않고 user-Agent로 찍혔으면 좋겠는데. 생각해보니 NestJS에서도 middleware로 HTTP log를 쓰도록 했었네. 구현부를 더 들여다보면 가능할 지도 모르지만 로그는 여기까지만 테스트 ..
Basic Auth
https://github.com/itsyoboieltr/elysia-basic-auth
https://github.com/eelkevdbos/elysia-basic-auth
잘 안 됨
RateLimit
bun add elysia-rate-limit
기본값이 6초 내에 10번 이상 요청이 들어오면 429 에러 응답.
import { rateLimit } from 'elysia-rate-limit'
const app = new Elysia()
.use(rateLimit())
cron job
https://elysiajs.com/plugins/cron.html
JWT 인증
https://elysiajs.com/plugins/jwt.html
Firebase IdToken 인증
유용한 링크
https://mirzaleka.medium.com/bun-crud-api-with-elysia-js-mongodb-10e73d484723