우선 우리가 기존에 알던 ExpressJS에서 사용하던 방식인
app.get('/', (req, res, next)=>{
res.send('hello');
});
같은 간단한 라우팅 방식을 지원하지만
ElysiaJS는 좀 더 사람에 맞춰 설계되었기 때문에 함수형으로 이루어져 있다.
import { Elysia } from 'elysia'
new Elysia()
.get('/', () => 'Hello World')
.listen(3000)
그리고 bun을 사용했기 때문에 기존 ExpressJS보다 21배 나 빠른 성능을 제공한다고 자랑한다.
엔드포인트 끝에서 값을 가져와 값에 맞는 콘텐츠를 전달할 때 동적 라우팅을 사용합니다.
ElysiaJS는 함수 자체에서 파라미터를 받아와 요청을 처리할 수 있습니다.
new Elysia()
.get('/id/:id', ({ params: { id } }) => 'hi '+id)
또한 하나 뿐만 아니라
여러 개의 파라미터를 받아와 요청을 처리할 수 있습니다.
new Elysia()
.get('/id/:id', ({ params: { id } }) => 'hi '+id)
.get('/id/:id/:name', ({ params: { id, name } }) => 'hi '+id + ' and ' + name)
만약 파라미터의 개수에 제한을 두고 싶지 않다면 와일드 카드를 사용할 수 있습니다.
사용법은 간단합니다 기존 :id를 둔 공간에 *
만 넣고 원하는 파라미터를 받아와 요청을 처리할 수 있습니다.
new Elysia().get('/id/*', ({ params }) => params['*']).listen(3000)
우리가 알고 있는 라우팅 메소드는 GET, POST, DELETE, PUT, PATCH 등등 기본적으로 http에서 기본적으로 사용되는 메소드로 사용합니다.
const app = new Elysia()
.get('/', () => 'hello')
.post('/hi', () => 'hi')
.listen(3000)
또한 메소드를 특정할 수 없을 때 .all()을 사용해 모든 메소드에 대한 요청을 받을 수 있습니다.
new Elysia().all('/', () => 'hi').listen(3000)
프레임워크는 다릅니다.
한 가지 특징이 하나 있는 데, 커스텀 메소드라는 기능입니다.
따로 엔드 포인트에 커스텀 메소드를 추가해 모든 메소드를 사용하고도 더 추가하고 싶은게 있을 때 새로 메소드를 만들 수 있습니다.
const app = new Elysia()
.get('/', () => 'hello')
.route('search', '/', () => 'connect')
.listen(3000)
보통 많은 개발자들이 Postman과 같은 REST 클라이언트를 많이 쓰는데 여기에선 그럴 필요 없습니다.
그냥 여기서 자체 테스트를 지원하거든요.
Elysia.handle이라는 함수를 쓰시면 됩니다.
const app = new Elysia()
.get('/', () => 'hello')
.post('/hi', () => 'hi')
.listen(3000)
app.handle(new Request('http://localhost/')).then(console.log)
이러면 좀 더 간편하게 테스트가 가능하겠죠?
set을 사용해 status를 조작 가능하다.
new Elysia()
.onBeforeHandle(({ set }) => {
set.status = 418
return 'I like tea'
})
.get('/', () => 'hi') //성공했을 경우에 200을 status로 지정
//또는
new Elysia()
.get('/', ({ set }) => {
// with auto-completion
set.status = "I'm a teapot"
return 'I like tea'
})
set을 이용해 헤더를 조작 가능하다.
new Elysia()
.get('/', ({ set }) => {
set.headers['x-powered-by'] = 'Elysia'
return 'a mimir'
})
.listen(3000)
set을 이용해 리다이렉션을 설정가능하다.
new Elysia()
.get('/', ({ set }) => {
set.redirect = 'https://youtu.be/whpVWVWBW4U?si=duN5cBbJuWgCrQRA&t=8'
})
.listen(3000)
기본적으로 404 같은 오류를 일으킬 경우 따로 에러를 던져버리지만
throw new Error();
ElysiaJS같은 경우에는 다르다.
만약 중간에 DB와 연결을 시도했지만, 실패했을 경우는 그냥 try catch 사용해서 에러 처리를 하시면 되는 데,
new Elysia()
.get('/', ({error}) => {
try{
//님들 코드적어둔거
}.catch(err){
error(500, err);
}
});
상대가 오류란 것을 알 수 있게 error() 함수를 사용해서 status와 메시지를 보낼 수 있습니다.(추천)
아니면 set.status를 사용해서 상태를 결정해 에러를 처리할 수 있다.
new Elysia()
.onBeforeHandle(({ set }) => {
set.status = 418
return 'I like tea'
})
.get('/', () => 'hi') //성공했을 경우에 200을 status로 지정
//또는
new Elysia()
.get('/', ({ set }) => {
// with auto-completion
set.status = "I'm a teapot"
return 'I like tea'
})
ElysiaJS는 표준 요청/응답을 기반으로 만들어져 있어서 그냥 문자열만 return 해도 아무 상관이 없지만 Resposne() 클래스를 사용하는 것을 더 선호한다면 사용할 수 있습니다. 물론 결과는 같겠지요.
import { Elysia } from 'elysia'
new Elysia()
.get('/', () => new Response('hi'))
.listen(3000)
ElysiaJS는 정적 콘텐츠를 import 하지 않아도 경로만 있다면 알아서 읽고 콘텐츠를 전달합니다.
음악파일이나 이미지파일 또는 영상파일을 보내더라도 방법은 같습니다. 그냥 파일을 보내주는 것 뿐이니까요.
import { Elysia } from 'elysia'
new Elysia()
.get('/video', Bun.file('kyuukurarin.mp4'))
Store는 ElysiaJS 앱에서 글로벌로 변경 가능한 오브젝트, 상태이다.
이는 마치 프론트엔드 상에서 사용하는 state와 비슷한 경험을 제공합니다.
state는 사용하기 이전에 무조건 등록 돼야 합니다.
new Elysia()
.state('version', 1)
.get('/', ({ store: { version } }) => version)
Decorate는 직접 context에 속성을 할당합니다.
기존 store와 다른 점은 Decorate는 읽기 전용이며, 다시 할당할 수 없습니다.
변하지 않는 속성을 할당하기 위해 합리적인 방법입니다.
마찬가지로 사용하기 전 먼저 할당되어 있어야 합니다.
new Elysia()
.decorate('logger', new Logger())
// ✅ 앞 줄이 먼저 선언되어야 함
.get('/', ({ logger }) => {
logger.log('hi')
return 'hi'
})
Decorate와 비슷하게, 직접 context에 속성을 할당할 수 있지만,
서버가 시작되기 전에 할당하는 대신 요청이 발생할 때마다 할당을 일으킵니다.
일반적으로 파생(기존에 있던 것을 기반으로 생성)만 가능합니다.
new Elysia()
.derive(({ headers }) => {
const auth = headers['authorization']
return {
bearer: auth?.startsWith('Bearer ') ? auth.slice(7) : null
}
})
.get('/', ({ bearer }) => bearer)
키-값
오브젝트
Remap(다시 선언하기)
new Elysia()
.state('counter', 0) //key-value
.state('logger', { //Object
logger: new Logger(),
trace: new Trace(),
telemetry: new Telemetry()
})
.state('counter', 0)
.state('version', 1)
.state(({ version, ...store }) => ({ //Remapping
...store,
elysiaVersion: 1
}))
// ✅ remapping으로 인해 새로 만들어짐.
.get('/', ({ store }) => store.elysiaVersion)
// ❌ remapping으로 인해 제외됨.
.get('/', ({ store }) => store.version)
보다 원활한 환경을 제공하기 위해 일부 플러그인에는 하나씩 다시 매핑하기에는 부담스러운 속성이 있을 수 있으므로, prefix와 suffix가 있는 Affix를 사용한다면 인스턴스에 있는 모든 속성을 다시 매핑할 수 있다.
const setup = new Elysia({ name: 'setup' })
.decorate({
argon: 'a',
boron: 'b',
carbon: 'c'
})
const app = new Elysia()
.use(setup.prefix('decorator', 'setup'))
.get('/', ({ setupCarbon }) => setupCarbon)
값을 변화 시키기 위해 직접 만질 필요가 없고 참조를 사용해 값을 변화 시키는 것이 중요하다.
자바스크립트에서 속성에 접근할 때, 직접 만지게 될 경우에는 참조가 사라지고, 새로운 값으로 변화시키기 때문이다.
우린 아래에서 store.counter 값을 접근할 수 있다.
const store = {
counter: 0
}
store.counter++
console.log(store.counter) // ✅ 1
하지만 아래와 같이 새로운 값으로 참조를 하게 될 경우 참조에 접근할 수 없다.
const store = {
counter: 0
}
let counter = store.counter
counter++
console.log(store.counter) // ❌ 0
console.log(counter) // ✅ 1
import { Elysia } from 'elysia'
new Elysia()
.state('counter', 0)
// ✅ 참조를 사용했기 때문에 값이 공유됨.
.get('/', ({ store }) => store.counter++)
// ❌ 새로운 값이 덮어 씌웠기 때문에, 연결이 사라짐
.get('/error', ({ store: { counter } }) => counter)
우선 여기까지만 적겠다.
더 많은 내용이 문서에 있지만 제대로 적지 못한 것이 많다.
자세한 것은 공식 문서를 보는 걸 추천한다.
개쩌는점 2
https://dl.acm.org/doi/abs/10.1145/3605098.3636068
아니 논문이 있다..............