Giter VIP home page Giter VIP logo

eden's People

Contributors

0xmarko avatar autarc avatar brunoeduardodev avatar fredericoo avatar gaurishhs avatar hazelnutcloud avatar hranum avatar itsyoboieltr avatar jimmy-guzman avatar kiyo5hi avatar mast1999 avatar matthewary avatar mehtaabgill avatar nanospicer avatar patlux avatar rayriffy avatar ryoppippi avatar saltyaom avatar sp90 avatar tomatenbaumful avatar yadav-saurabh avatar yukikwi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

eden's Issues

Treaty and Dependency Injection

Hi!
In order to use some feature of elysia like dependency injection (https://elysiajs.com/patterns/dependency-injection.html), I split the code into multiple file:

First, a state for the database

// setup.ts
export const setup = new Elysia({ name: 'setup' })
    .state('redis', redis)
// routes/auth.ts
import { Elysia } from 'elysia';

import { setup } from '../setup';

export const authRouter = new Elysia({prefix: "/auth"})
    .use(setup)
    .post('/login', async (context) => { ... })
    .post('/logout', async (context) => { ... }));
// routes/foo.ts
import { Elysia } from 'elysia';

import { setup } from '../setup';

export const fooRouter = new Elysia({prefix: "/foo"})
    .use(setup)
    .get('/', async (context) => { ... })
    .post('/manage', async (context) => { ... }));

and finally the main entry point for my app

// server.ts
import { Elysia } from 'elysia';

import { authRouter } from './routes/auth';
import { fooRouter } from './routes/foo';

const app = new Elysia()
    .use(authRouter)
    .use(fooRouter)
    .listen(6001);

export type App = typeof app;

console.log(`๐ŸฆŠ Elysia is running at ${app.server?.hostname}:${app.server?.port}`);

Working as expected, manually querying various endpoints.

When using Treaty, I get an empty elysia app on the client side
Screenshot 2023-09-20 at 10 09 36

Any idea where my code fails?

Thanks in advance!

Eden Treaty does not recognise route without a leading `/`

Version

  • @elysiajs/eden: 1.0.0-rc
  • elysia: 1.0.0-rc.19

Reproduction:

const app = new Elysia()
  .post('login', (body) => handler)
  .listen(3000)

const client = edenTreaty<typeof app>('http://localhost:3000')

client.login.post(body) 
//     ^ cannot find login on {}

The fix

const app = new Elysia()
  .post('/login', (body) => handler) // leading forward-slash
  .listen(3000)

const client = edenTreaty<typeof app>('http://localhost:3000')

client.login.post(body) 
//     ^ typing passed

Request

Remove the hard-requirement to have leading /s before routes for eden to recognise them.

missing filename and filetype in formdata (always setting default filename: blob)

Is there any way to send formdata?

If I send the form data like in the bellow code, I am getting file.name as blob for every single file

// server
 .post(
        '/pdf',
        async ({ body }) => {
            console.log(body)
            for (let index = 0; index < body.files.length; index++) {
                const file = body.files[index]
                console.log('file.name: ', file.name)
                console.log('file.size: ', file.size)
                console.log('file.type: ', file.type)
            }
            return { message: 'success got the file' }
        },
        {
            body: t.Object({ files: t.Files() })
        }
    )
// client
const client = edenTreaty<Server>('http://localhost:8080')


const files = e.target.files
const { data, error } = await client.pdf.post({ files: files })
// output using edenFetch or edenTreaty
{
  files: [
    Blob (0.27 MB)
  ]
}
file.name:  blob
file.size:  270947
file.type:  

using fetch or axios with formdata I am able to get the file.name correctly

// client
const formData = new FormData()
for (let i = 0; i < files.length; i++) {
    let file = files.item(i)
    formData.append('files', file)
}
await axios.post('http://localhost:8080/pdf', formData, {
    headers: { 'Content-Type': 'multipart/form-data' }
})
await fetch('http://localhost:8080/pdf', {
    method: 'POST',
    body: formData
})
// output using axois to fetch with formdata
{
  files: [
    Blob (0.27 MB)
  ]
}
file.name:  society.pdf
file.size:  270947
file.type:  

Type error on sample code

Following EdenTreaty sample from docs:

// server.ts
import { Elysia, t } from 'elysia'

const app = new Elysia()
    .get('/', () => 'Hi Elysia')
    .get('/id/:id', ({ params: { id } }) => id)
    .post('/mirror', ({ body }) => body, {
        body: t.Object({
            id: t.Number(),
            name: t.String()
        })
    })
    .listen(8080)

export type App = typeof app 
import { edenTreaty } from '@elysiajs/eden'
import type { App } from './server' 

const app = edenTreaty<App>('http://localhost:8080')

I get type error:

Type 'Elysia<"", { request: {}; store: {}; derive: {}; resolve: {}; }, { type: {}; error: {}; }, {}, {}, { "/": { get: { body: unknown; params: never; query: unknown; headers: unknown; response: { 200: string; }; }; }; "/id/:id": { get: { body: unknown; params: Record<"id", string>; query: unknown; headers: unknown; respo...' does not satisfy the constraint 'Elysia<any, any, any, any, any, any, false>'.
Types have separate declarations of a private property 'dependencies'.ts(2344)

I have

  • restarted TS server
  • same version of elysia on both client and server (using turborepo)
  • tsconfig "strict": true
  • TS version > 5

Any idea what else I can test?

Error when using with NextJS App

Hi

I am trying to create a NextJS app using Elysia and did the following steps:

  1. bunx create-next-app
  2. bun add elysia @elysiajs/eden
  3. Create a server.ts file under the src directory and add a default elysia server
  4. Replace page.tsx with the following
"use client";

import { edenTreaty } from "@elysiajs/eden";
import { useEffect, useState } from "react";
import type { App } from "../server";

const app = edenTreaty<App>("http://localhost:3000");

export default function Home() {
  const [data, setData] = useState(null);
  useEffect(() => {
    app.api.test.get().then((x: any) => setData(x.data));
  }, []);
  return <div>test</div>;
}

When hovering over the type of the app I get back the return type of any.

image

Is this because the file is a tsx file?

Thanks for any help!

[treaty] Cannot have `null` body

Having this type definition in the handler:

{
 body: t.Null(),
}

results in Eden Treaty inferring the signature of .post as params: never...

Unknown Type in Response Data

Summary

The data property on EdenTreaty's .post() response objects are typed as unknown as of the current versions of Eden & Elysia (0.4.1 & 0.4.10 respectively).

To illustrate this I've tried recreating the example CodePen in the Elysia docs. This works fine with the older Eden & Elysia versions recorded in the package.json however upgrading them to the latest version causes the following misbehaviour:

Expected Behaviour (from example codesandbox)

expected-behaviour

Buggy Behaviour (local machine)

eden-type-issue

Setup to Recreate

package.json

{
  "name": "hi-elysia",
  "version": "1.0.0",
  "module": "src/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "bun run --hot src/index.ts"
  },
  "dependencies": {
    "@elysiajs/eden": "^0.4.1",
    "elysia": "^0.4.10"
  },
  "devDependencies": {
    "bun-types": "^0.5.0",
    "typescript": "^4.9.4"
  }
}

*note: swagger has been removed from example which I don't expect would affect the issue

http.ts

import { Elysia, t } from "elysia";

// Tips: navigate to /swagger to see the documentation
const app = new Elysia()
	.get("/", () => "Hello Elysia")
	.get("/id/:id", ({ params: { id } }) => id)
	.post("/json", ({ body }) => body, {
		schema: {
			body: t.Object({
				name: t.String(),
			}),
			query: t.Object({
				id: t.String(),
			}),
		},
	})
	.listen(3000);

console.log(
	`Elysia running at http://${app.server?.hostname}:${app.server?.port}`
);

export type App = typeof app;

eden.ts

import { edenTreaty } from "@elysiajs/eden";
import type { App } from "./http";

const api = edenTreaty<App>("http://0.0.0.0:8080");

// Hover over function to see type-definition

// Try hover over the response to see its type
const response1 = await api.index.get();

// Path parameters
api.id["177013"].get();

// Try hover over the response to see its type
// Type inference work even without direct type declaration
const { data } = await api.json.post({
	name: "saltyaom",
	$query: {
		id: "Chiffon",
	},
});

// Type Error
api.json.post({
	// 'name' is not assignable to string
	name: 1,
	// property 'id' is missing
	$query: {},
});

The only differences as far as I can tell between this setup and the example from the docs are the version updates and that swagger has been removed.

Eden is not recognizing routes when using plugin (router) structure.

admin-router.ts:

   const adminRouter = new Elysia({ name: 'Admin', prefix: '/admin' })
    adminRouter.get('/hello', () => 'hello')
    export type adminRouter = typeof adminRouter
    export default adminRouter

index.ts:

    import adminRouter from './admin-router.ts'
    const app = new Elysia({ prefix: '/api' }).use(adminRouter).listen(5000)

test.ts:

    import type { adminRouter } from '../admin'
    import { treaty } from '@elysiajs/eden'
    const app = treaty<adminRouter>('localhost:5000/')
    const x = await app.api.admin.hello.get() // admin does not exist on type adminRouter

Seems like treaty does not work properly using plugin with prefix approach. Does it only work with .group approach or am I doing something wrong?

Eden treaty can't send array

Reported on discord


Running following code does not work as expected:

Code

import { Elysia, t } from 'elysia';
import { edenTreaty } from '@elysiajs/eden/treaty';

const app = new Elysia()
  .post(
    '/upload',
    ({ body }) => {
      console.log(body);
      return 'yay';
    },
    {
      body: t.Array(
        t.Object({
          itemId: t.String()
        })
      )
    }
  )
  .listen(8080);

const api = edenTreaty<typeof app>('http://localhost:8080');

const { response, data } = await api.upload.post([
  {
    itemId: 'yay'
  }
]);

console.log(response.status, data);

app.stop();

Console output

400 Invalid body, 'type': Expected array

Expected: []

Found: {
  "0": {
    "itemId": "yay"
  }
}

Cause

This spread syntax converts array into object:

{ $query, $fetch, $headers, $transform, ...bodyObj } = {

const array = [
  {
    itemId: 'yay'
  }
];

const { ...obj } = array;

console.log(obj); // { "0": { itemId: "yay" } }

Customize eden fetch error type

I am trying to return a structured error response from the server, so I can return several validation messages at once for for example.

ie with an onError server handler like this

  .onError((context) => {
    const { code, error, set } = context
    switch (code) {
      case 'VALIDATION_ERROR':
        set.status = 422
        return {
          message: error.toString().replace('Error: ', '')
          fields: []
        }
      }
  })

But when a request fails, the eden fetch error.value type is a string:

  const { data, error} = await fetch('/endpoint')

It would be nice to be able to customize the returned error type, so I could access error.value.message and have it properly typed too.

Query missed in eden fetch

seems query missed in eden fetch

    return new Elysia({ prefix: "api/" })
      .post("business", ({ query }) => query.page, {
        query: t.Object({
          page: t.Numeric(),
        }),
      })

eden part

const fetch = edenFetch<BusinessService>("");
fetch("business",{
  query:{ page: "1"  } // ???? 
})

index and edenFn

I was going through the documentation and noticed a few things during implementation.

  • index: it's calling the root/index routes but the typescript is showing some type error for that, also no autocomplete for index
Property 'index' does not exist on type '{ get: (params?: { $fetch?: RequestInit | undefined; $query?: Record<string, string> | undefined; $headers?: Record<string, unknown> | undefined; $transform?: Transform<...> | undefined; } | undefined) => Promise<...>; post: (params?: { ...; } | undefined) => Promise<...>; } & ... 17 more ... & { ...; }'.ts(2339)
  • The same goes for edenFn
Module '"@elysiajs/eden"' has no exported member 'edenFn'.ts(2305)

nextjs (client): 13.5.4
eden version 0.7.4
elysia: 0.7.15
bun: 1.0.3

[BUG] Websocket data is not parsed, only returns string

Using the .subscribe method, I expected the data property to return the inferred type from the schema. Instead, it actually returns a string, which needs to be manually parsed after. I believe this is a bug, as the type inference is correct.

Types with group routes are incorrect.

Groups with paths at the root generate incorrect types in eden treaties.

The below snippet creates a route /group and should be accessible through treaty.group.get().

app.group('/group', (app) => {
    return app
        .get("/", () => "hi")
        .get("/sub", () => "yo")
})

However, typescript complains that .get() "is not callable". treaty.group.get() does work as expected and returns data from /group. Originally I thought this was an issue with Elysia's routing and raised the issue here elysiajs/elysia#39. It seems that Elysia is working as intended and treaty.group.get() behaves as I expect despite the typescript error, leading me to conclude that this is an issue with Eden's typing.

Eden Treaty: api.get() maps to request GET /ge instead of /

Issue:

Hi guys thanks for the great work!

I am running into an issue with eden treaty where the client call api.get() is making the request to /ge instead of /.

Deps:

"dependencies": {
    "@elysiajs/eden": "^0.6.4",
    "@elysiajs/swagger": "^0.6.2",
    "elysia": "latest"
  },
  "devDependencies": {
    "bun-types": "latest"
  },

Code:
Server:

import { Elysia, t } from "elysia";
import { swagger } from "@elysiajs/swagger";

const app = new Elysia()
  .use(swagger())
  .get("/", () => "Hello Elysia")
  .get("/id/:id", ({ params: { id } }) => id)
  .post("/json", ({ body }) => body, {
    body: t.Object({
      name: t.String(),
    }),
    query: t.Object({
      id: t.String(),
    }),
  })
  .listen(3000);

console.log(
  `Elysia running at http://${app.server?.hostname}:${app.server?.port}`
);

export type App = typeof app;

client:

import { edenTreaty } from "@elysiajs/eden";
import type { App } from "./http";

const api = edenTreaty<App>("http://0.0.0.0:3000");
const response = await api.get({});

console.log(response);

result:

{
  data: "NOT_FOUND",
  error: 1 |   status;
2 |   value;
3 |   constructor(e, t) {
4 |
5 |     super(), this.status = e, this.value = t;
  status: 404,
  raw: Response (0 KB) {
    ok: false,
    url: "http://0.0.0.0:3000/ge",
    headers: Headers {
      "content-type": "text/plain;charset=utf-8",
      "date": "Mon, 11 Sep 2023 22:57:21 GMT",
      "content-length": "9"
    },
    statusText: "Not Found",
    redirected: false,
    bodyUsed: true,
    status: 404
  },
  headers: Headers {
    "content-type": "text/plain;charset=utf-8",
    "date": "Mon, 11 Sep 2023 22:57:21 GMT",
    "content-length": "9"
  }
}

Type Issue with params Property in Eden Fetch

First off, I want to express my appreciation for the Elysia project! It's been instrumental in building efficient and type-safe servers, and the thoughtfulness in design truly shows. I've been using it for a while and am continually impressed by its capabilities and performance.

Expected

I was expecting to utilize the edenFetch from @elysiajs/eden with App type from server.ts without encountering type errors, ensuring smooth integration between client and server-side code.

Actual

However, I encountered a TypeScript error when trying to use the edenFetch with the App type. The error is as follows:

Property 'params' is missing in type '{}' but required in type '{ params: never; }'.ts(2345)
types.ts(40, 23): 'params' is declared here.
Screenshot 2024-01-02 at 10 10 25โ€ฏAM

Reproduction

The following code represents a simplified scenario based on the official example provided in the Elysia documentation for edenFetch. I've encountered the TypeScript error while working with this example directly from the Elysia Eden Fetch Documentation.

// server.ts
import { Elysia, t } from 'elysia'

const app = new Elysia().get('/', () => 'Hi Elysia').listen(8080)

export type App = typeof app
import { edenFetch } from '@elysiajs/eden'
import type { App } from './server'

const fetch = edenFetch<App>('http://localhost:8080')

// response type: 'Hi Elysia'
const pong = await fetch('/', {})

I understand the challenge and intricacies involved in type definitions and intersection types, especially when dealing with external type declarations. I suspect this issue might be related to the intersection types or perhaps an optional property that isn't set correctly in the typings.

I'm not entirely sure of the best solution here, but I wanted to bring it to your attention in case it's something that can be improved or if there's a workaround that I'm not aware of. Your insight and guidance would be immensely valuable.

Thank you again for all your hard work on this project. It's been a pleasure to use, and I look forward to continuing with it!

[FEATURE REQUEST] Transform function configuration

It would be nice if Eden Treaty allowed for the transformation of the response.

Proposed API:

const api = edenTreaty<App>('', {
  transform: (response) => {
    if (response.error) throw response.error;
    return response.data;
  },
});

const data = await api.get();

Current workaround, which needs to be coded manually for each request:

const api = edenTreaty<App>('');

const response = await api.get();
if (response.error) throw response.error;
const data = response.data;

--- Why would this be useful? ---

Lot of current querying libraries like Tanstack query, SWR, depend on errors being thrown, so they can be returned by the hook used for querying. Eden Treaty does not throw, but returns errors and data objects, which is fine, but there should be a way to configure the transformation of the response for these use-cases on the client level. Is this something that would be feasible to implement? @SaltyAom

How to upload a file using eden treaty?

While the documentation made clear that how to use get and post simple json, there are no example on how to post a file using it, and its not clear on how to approach that. Using formdata as body failed for me. So a better documentation depicting how to make it work would be great.

[BUG] Param string unions

Param string unions are not respected by eden treaty, any string is allowed instead of the specified union.

Reproduction:

import { Elysia, t } from 'elysia';
import { edenTreaty } from '@elysiajs/eden';

const app = new Elysia().get('/:name', ({ params }) => params.name, {
  params: t.Object({
    name: t.Union([t.Literal('Joe'), t.Literal('Josh')]),
  }),
});

type App = typeof app;

const api = edenTreaty<App>('http://localhost:3000');

api[':name'].get();

:name can be any string, without type checking. Instead of :name here, the type should only be able to be Joe or Josh, nothing else, as they are specified in the union.

edenFetch type error: Argument of type 'string' is not assignable to parameter of type 'never'.

with the basic setup described in the docs,

const fetch = edenFetch<App>('http://localhost:3000');
const pong = await fetch('/', {});

i get the type error on fetch Argument of type 'string' is not assignable to parameter of type 'never'.

the type described in vscode shows

const fetch: <never, Uppercase<string>, never>(endpoint: never, options: Omit<RequestInit, "headers" | "body" | "method"> & {
    method?: Uppercase<string> | undefined;

Type 'Elysia<...>' does not satisfy the constraint 'Elysia'

Am I doing something wrong?

// Server

=================================================
package.json deps:

    "@elysiajs/cookie": "^0.8.0",
    "@elysiajs/cors": "^1.0.2",
    "@elysiajs/jwt": "^0.8.0",
    "@elysiajs/swagger": "^0.8.5",
    "@prisma/client": "5.10.2",
    "elysia": "latest"
=================================================

const app = new Elysia();

app
  .use(swagger())  // It does not change after commenting out this line
  .use(cookie())  // It does not change after commenting out this line
  .use(corsHandler)  // It does not change after commenting out this line
  .use(errorHandler)  // It does not change after commenting out this line
  .use(eventHandler)  // It does not change after commenting out this line
  .use(jwtHandler)  // It does not change after commenting out this line
  .use(authRoutes)
  .listen(process.env.PORT || 4000);

export type App = typeof app;


// type App = Elysia<"", {
//    request: {};
//    store: {};
//    derive: {};
//    resolve: {};
// }, {
//    type: {};
//    error: {};
// }, {}, {}, {}, false> <== This is what VSC is showing when I hover App object
// Client

=================================================
package.json deps:

    "@elysiajs/eden": "latest",
    "elysia": "latest",  // It does not change after commenting out this line
=================================================

import { edenTreaty } from "@elysiajs/eden";
import type { App } from "../../../../project-api/src/index";

const app = edenTreaty<App>("http://localhost:4000");

// Outcome

Type 'Elysia<"", { request: {}; store: {}; derive: {}; resolve: {}; }, { type: {}; error: {}; }, {}, {}, {}, false>' does not satisfy the constraint 'Elysia<any, any, any, any, any, any, any, any>'.
  Type 'Elysia<"", { request: {}; store: {}; derive: {}; resolve: {}; }, { type: {}; error: {}; }, {}, {}, {}, false>' is missing the following properties from type 'Elysia<any, any, any, any, any, any, any, any>': _routes, _types, _ephemeral, _volatile, and 11 more.ts(2344)

Screenshot from 2024-04-05 21-04-48

Eden Fetch Add case for text/event-stream and forward response to return statement

Would love to give this one a shot, and write a pull request for it
Currently we can't stream with Eden Fetch

  • EdenFetch doesn't pass response, even though it's stated in the type definition
  • Default would transform the stream data into text , not allowing access to the Stream

Proposal add a text/event-stream case

 switch (res.headers.get('Content-Type')?.split(';')[0]) {
                    case 'application/json':
                        data = await res.json()
                        break
                    case 'text/event-stream': {
                        data = res
                        break
                    }
                    default:
                        data = await res.text().then((d) => {
                            if (!Number.isNaN(+d)) return +d
                            if (d === 'true') return true
                            if (d === 'false') return false

                            return d
                        })
                        break
                }

Add response to the return of EdenFetch

 return {
                    data,
                    error: null,
                    status: res.status,
                    response: res, 
                    headers: res.headers,
                    retry: execute
                }

[BUG] Client not receiving response body object with edenFetch

Making some native Bun tests using Eden but I'm facing a null data object in response object. I'm gonna let my fetching config and what I'm receiving in my test.

// --- src/main/adapter/rest-adapter.ts

import type { Controller, HttpRequest } from "@/presentation/common";

export default (controller: Controller<any>) =>
  async ({ body, query, params, headers, set }: any): Promise<any> => {
    set.headers["Content-Type"] = "application/json";

    try {
      const request: HttpRequest<any> = { body, query, params, headers };

      const result = await controller.handle(request);

      set.status = result.status;

      return JSON.stringify({ response: result.body });
    } catch (error: any) {
      set.status = 500;

      return JSON.stringify({ error: error.message });
    }
  };

// --- src/main/app.ts

import authRoutes from "@/main/routes/auth-routes";
import { Elysia } from "elysia";

const app = new Elysia()
  .get("/", () => {
    return "Hello World!";
  })
  .group("/auth", (app) => app.use(authRoutes));

export default app;

export type App = typeof app;

// --- tests/config/api.ts

import app, { type App } from "@/main/app";
import env from "@/main/env";
import { edenFetch } from "@elysiajs/eden";
import { afterAll, beforeAll } from "bun:test";

beforeAll(() => {
  app.listen(env.PORT, () => {
    console.log("Started testing server...");
  });
});

afterAll(async () => {
  await app.stop();
});

export default edenFetch<App>(`http://localhost:${env.PORT}`);

// --- test file
import api from "@/config/api";
import { faker } from "@faker-js/faker";
import { beforeAll, describe, expect, it } from "bun:test";

describe("Create an account/Sign up", () => {
  let response: any;

  beforeAll(async () => {
    const user = {
      username: faker.internet.userName(),
      email: faker.internet.email(),
      password: faker.internet.password(),
    };

    response = await api("/auth/signup", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: {
        username: user.username,
        email: user.email,
        password: user.password,
      },
    });
  });

  it("should receive email and password", () => {
    console.log(response.data);
    expect(response.status).toBe(200);
  });
});

When fetching from an external client, like Postman or Insomnia, I get my response just like it should be. But when running tests with bun run test data field in response object is null. This image is showing what is happening: I'm logging what I'm returning to route and logging the response object I'm getting of this route.

image

"@elysiajs/eden": "^0.8.1",
"elysia": "^0.8.17",

Any thoughts?

Treaty Type Mismatch

Below minimal code:

import { Elysia } from 'elysia'
import { edenTreaty } from '@elysiajs/eden'

const app = new Elysia().get('/', () => 'Hi Elysia').listen(3000)

type App = typeof app

const eden = edenTreaty<App>('http://localhost:3000')

Produces the error: Type 'Elysia<"", { ... }>' does not satisfy the constraint 'Elysia<any, { ... }>'.
And subsequent errors related to the LifeCycleStore type mismatch.

Versions used:

"@elysiajs/eden": "^0.6.0",
"elysia": "^0.6.10"

Server side dependencies prevent client side compilation

Hello!

If I have @elysiajs/static or @elysia/apollo-graphql in my server definition client side compilation will fail.
This is happening because of inner dependencies which are not available in the browser environment.

While it can be solved and handled (for example, by splitting your eden-safe plugins and unsafe ones and applying the unsafe ones in a different files), I think it points to an underlying limitation.

Am I missing something?
If not, do you think we can improve the situation with a specific bun build configuration? Shall we consider an alternative route where we produce a static specification of the API and embed a simple JSON in the client?

Error details:

Apollo:

error: No matching export in "node:zlib" for import "gzip"

import { gzip } from 'zlib';
         ^
elysia-demo/node_modules/@elysiajs/apollo/node_modules/@apollo/server/dist/esm/plugin/usageReporting/plugin.js:8:10 377

Static:

error: No matching export in "node:path" for import "resolve"

import { resolve, resolve as resolveFn, join } from 'path';
         ^
elysia-demo/node_modules/@elysiajs/static/dist/index.js:2:10 54

error: No matching export in "node:path" for import "resolve"

import { resolve, resolve as resolveFn, join } from 'path';
                  ^
elysia-demo/node_modules/@elysiajs/static/dist/index.js:2:19 63

error: No matching export in "node:path" for import "join"

import { resolve, resolve as resolveFn, join } from 'path';
                                        ^
elysia-demo/node_modules/@elysiajs/static/dist/index.js:2:41 85

Cheers

Remove URL transforming inside eden treaty 2

I often use elysia alongside eden embedded inside sveltekit. Sveltekit provides a custom fetch function inside load functions that , when used with plain relative paths e.g: fetch('/api') will call the server-side function directly when performing server-side rendering. More info here

Eden treaty 2 transforms the URL passed into treaty() by appending https:// to it. This disables sveltekit's features when using its fetch. To fix this, remove any transformation functions inside eden treaty 2 or expose configuration options to do so.

Treaty 2 unknown status and value types in error

The Issue

const { data, error } = await client.api.inbox({ chatId: chatId }).get()


// the types show as :
// const data: {
//                id: number
//                message: string
//               }

// const error: {
//              status: unknown
//              value: unknown
                 }

I'm not sure if this is how its supposed to be with treaty 2 but I had proper error types in v1

Upload multiple Files doesn't work from Bun client

reported on discord


Code

import { Elysia, t } from "elysia";
import { edenTreaty } from "@elysiajs/eden";

export const app = new Elysia()
    .post(
        '/uploadMultipleFiles',
        async ({ body }) => {
            for (const file of body.files) {
                console.log(`${file.name} uploaded`)
            }
            return 'File uploaded'
        },
        {
            type: 'formdata',
            body: t.Object({
                files: t.Files({
                    type: 'text/plain'
                })
            })
        }
    )
    .listen(0)

const client = edenTreaty<typeof app>(`${app.server?.url}`)

let fileContent = 'hello'
let file1 = new File([fileContent], 'input1.txt')
let file2 = new File([fileContent], 'input2.txt')

const { data: multipleFileUpload } = await client.uploadMultipleFiles.post({
    files: [file1, file2] // Type Error: Type 'File[]' is not assignable to type 'Files'.
})

app.stop()
console.log('Test for POST /uploadMultipleFiles path: ' + multipleFileUpload)

Expected Console output:

input1.txt uploaded
input2.txt uploaded
File uploaded

What i see instead:

{
  "type": "validation",
  "on": "body",
  "property": "/files",
  "message": "Expected kind 'Files'",
  "expected": {
    "files": "Files"
  },
  "found": {
    "files": "[object Blob],[object Blob]"
  },
  "errors": [
    {
      "type": 31,
      "schema": {
        "type": "array",
        "elysiaMeta": "Files",
        "default": "Files",
        "extension": "text/plain",
        "items": {
          "type": "string",
          "default": "Files",
          "format": "binary"
        }
      },
      "path": "/files",
      "value": "[object Blob],[object Blob]",
      "message": "Expected kind 'Files'"
    }
  ]
}

Solution (idk about treaty2):

eden/src/treaty/index.ts

Lines 268 to 270 in 59480e5

if (isServer) {
newBody.append(key, field as any)
} else {

doesn't check for Array.isArray(field)

type Files = File | FileList

should include File[]

Path parameter doesn't work in Eden Treaty 2

Hi.

I'm trying out Eden Treaty 2, but the path parameter isn't working as expected.
Here's the code I tried

const app = new Elysia({ aot: false }).get(
  "/users/:id",
  ({ params }) => params.id
);
await treaty(app).users({ id: 1 }).get();
treaty(app).users({ id: 1 }).get is not a function. 
(In 'treaty(app).users({ id: 1 }).get()', 'treaty(app).users({ id: 1 }).get' is undefined)

If there's something wrong with the way I've written it, I would like to know.

version

  • elysia: 1.0.0-rc.11
  • @elysiajs/eden: 1.0.0-rc.3

Typesafe isn't functioning properly on Eden Treaty 2

Hi
After testing various things with the RC version, it didn't work correctly. Below is the code I ran

describe("eden test", () => {
  it("derive test", async () => {
    const app = new Elysia()
      .derive(() => ({ user: "name" }))
      .get("/user", () => "user")
    treaty(app).get("/user", () => "user")
  })

  it("resolve test", async () => {
    const app = new Elysia()
      .resolve(() => ({ user: "name" }))
      .get("/user", () => "user")
    treaty(app).user.get("/user", () => "user")
  })

  it("prefix test", async () => {
    const app = new Elysia({ prefix: undefined }).get("/user", () => "user")
    treaty(app).user.user.get() //<-- this is wrong (This is how it's autocompleted.)
  })
})

capture
image

version
elysia: 1.0.0-rc.15
@elysiajs/eden: 1.0.0-rc.4

Eden Treaty: slow typescript intellisense

Hi, I'm using Elysia with Eden Treaty and I use multiple Elysia instances, some of them having prefixes to keep the code tidy.
However, when trying to use Eden Treaty, the intellisense is really slow and most of the time it times out and just doesn't work at all.

Allow to pass empty content-type for edenFetch and prevent

Hi!
I have to pass via a formData for a specific API endpoint.

My problem is :

I can open a PR if you want but removing the first might be a breaking change that's why I've open an issue first :D

Create a way to get direct access to the Response object without the stream being read

So taking a look at this

eden/src/treaty/index.ts

Lines 314 to 327 in 688011d

switch (response.headers.get('Content-Type')?.split(';')[0]) {
case 'application/json':
data = await response.json()
break
default:
data = await response.text().then((data) => {
if (!Number.isNaN(+data)) return +data
if (data === 'true') return true
if (data === 'false') return false
return data
})
}
block of code, I'm seeing that eden treaty can return either json data, or data as text. This is great except when you want to use Eden Treaty to download binary data or something else that needs to be treated in a particular way. Take this https://elysiajs.com/plugins/stream.html#fetch-stream feature for example. Elysia lets you stream a fetch request, basically making an endpoint into a proxy. But I don't think this will work with Eden Treaty. And what if I wanted to stream the download of a file? If the response returned is application/octet-stream Eden Treaty resolves the request and converts it to text, making it so I can't get at the binary data. The request object that's returned by calling endpoints with Eden Treaty has response.body locked, making it so the stream can't be read. SO! I have a proposed solution,

Eden Treaty can have this block

eden/src/treaty/index.ts

Lines 314 to 327 in 688011d

switch (response.headers.get('Content-Type')?.split(';')[0]) {
case 'application/json':
data = await response.json()
break
default:
data = await response.text().then((data) => {
if (!Number.isNaN(+data)) return +data
if (data === 'true') return true
if (data === 'false') return false
return data
})
}
extended to be helpful for more usecases like mine, but there should also be an option that can be passed into the HTTP method to get around a whole class of issues like mine:

EdenTreaty.<1>.<2>.<n>.<method>({
    ...body,
    $query?: {},
    $fetch?: RequestInit,
    getRaw?: boolean // ๐Ÿ‘ˆ This!
})

that will make it so no data property is returned and instead you'll have to handle the Response object yourself to get the data.

I have created a PR which provides a solution but without proper TypeSafety (if you set getRaw: true the signature will not change to the Response type). I welcome improvements to the PR. #34

Values of Date type are strings in eden treaty

I've noticed that the values of Date type in eden treaty don't match their types (In nested object, not sure about first level depth)
Type inference works correctly and the type is Date but the actual value at runtime is a string and not a date (see following screenshots), I've checked server side and the type sent are indeed Dates

image
image

Types:

export interface Experience extends Document {
  type: ExperienceType;
  company: Types.ObjectId | Company;
  summary: string;
  title: string;
  projects: Project[]
}

type Project = {
    name: string;
    start?: Date | undefined;
    end?: Date | undefined;
    summary?: string | undefined;
}

I use edenTreaty as such :

import { edenTreaty } from '@elysiajs/eden'
import type { Portfolio } from 'portfolio-api'

import { env } from '$env/dynamic/public'

const api = edenTreaty<Portfolio>(env.API_URL)

export { api }
let { data, error } = await portfolioApi.domain[params.domain].get({
    $fetch: { credentials: 'include' }
  });

Server side :

import Elysia, { t } from 'elysia';
import { Experience, Domain } from '../models/database';

export const domain = new Elysia()
  .get('/domain/:name', async ({ params: { name } }) => {
    const domain = await Domain.findOne({ name: name });
    if (!domain) throw new Error('Domain not found');
    const populatedDomain = await domain.populate<{ experiences: Experience[] }>('experiences');
    return populatedDomain;
  })

Incorrect infering of groups

routes/auth/index.ts
image

routes/auth/signin.ts
image

react code
image

Seems like it's the typescript typing issue and it wasn't resolved as stated in #3, or maybe it was resolved when group and route is in the same file, but I haven't checked that
Just checked, it was resolved for that particular case

Routing inconsistencies with and without prefix between '' and '/'

Recently I discovered some interesting routing behaviour, so I decided to write some test cases:

import { Elysia } from 'elysia';
import { edenTreaty } from '@elysiajs/eden';

/* test case 1: no prefix with slash */ โœ…

const noPrefixWithSlash = new Elysia().get('/', () => ({ data: '/' }));

type NoPrefixWithSlash = typeof noPrefixWithSlash;

const noPrefixWithSlashTreaty = edenTreaty<NoPrefixWithSlash>('');

// Works as intended!

await noPrefixWithSlashTreaty.get();

/* test case 2: prefix with slash */ โŒ

const prefixWithSlash = new Elysia({ prefix: '/prefix' }).get('/', () => ({
  data: '/',
}));

type PrefixWithSlash = typeof prefixWithSlash;

const prefixWithSlashTreaty = edenTreaty<PrefixWithSlash>('');

// why is this not mapping to 'prefixWithSlashTreaty.prefix.get()'?

// It does not work the same way as the first test case, where also '/' is used.

await prefixWithSlashTreaty.prefix[''].get();

/* test case 3: no prefix without slash */ โŒ

const noPrefixWithoutSlash = new Elysia().get('', () => ({ data: '' }));

type NoPrefixWithoutSlash = typeof noPrefixWithoutSlash;

const noPrefixWithoutSlashTreaty = edenTreaty<NoPrefixWithoutSlash>('');

// why is this not mapping to 'noPrefixWithoutSlashTreaty.get()'? Why is it not callable? (property) "": {}

await noPrefixWithoutSlashTreaty[''].get(); // Property 'get' does not exist on type '{}'.ts(2339)

/* test case 4: prefix without slash */ โœ…

const prefixWithoutSlash = new Elysia({ prefix: '/prefix' }).get('', () => ({
  data: '',
}));

type PrefixWithoutSlash = typeof prefixWithoutSlash;

const prefixWithoutSlashTreaty = edenTreaty<PrefixWithoutSlash>('');

// Works as intended!

await prefixWithoutSlashTreaty.prefix.get();

Are test cases 2 and 3 failing intentionally, is this working as intended? I thought these were also possible route and prefix combinations we could use in our Elysia apps with Eden Treaty.

EdenFetchError hard to debug

When we log an error from Eden, we get this error logged on console:

image

Problems:

  • It does not show any info about the error (e.g. status code).
  • The stack trace refers to a minified source code making error even harder to understand.
  • The error class EdenFetchError is reduced to E in console output.

super()

It might make it easier to debug if the error message is more human-friendly e.g. Request failed with status code 400

Cannot send empty body

.post({ body: {} })

sends { "body": {} }, while

.post({})

somehow sends "[object Object]"

baseUrl causes the typescript compiler to lose track of types and replace the edenTreaty client with any

Hi,

I just wanted to raise awareness about this since it took a lot of time for me to figure this out, maybe this will help someone save some time in the future:

I use eden and elysia to provide a typesafe way to access my api. Since I'm writing an API which is intended to be used across many projects, I decided to provide a library/sdk to access it. This library provides some init code and exports an edenTreaty api client to access the actual api. After building my library the output .d.ts files did not contain the correct types. TS replaced the return type of the edenTreaty call with an "any" type. It took me a very long time to figure out, why this was happening:

I used the "baseUrl" property in my tsconfig.json (the one in the API package, not the client) to simplify imports. After replacing that with relative imports, everything works as expected. I'm not really expecting the eden/elysia project to do something about this, so I'll instantly close this, but I wanted to create some record so people don't have to deal with the same issue as long as I did.

Can't send Headers to websocket `.subscribe()`

Problem & Steps

I have an Elysia app with authentication using bearer tokens.

export default (app: ElysiaPlugins) => {
  return app.ws("/chat/ws", {
    body: t.Object({ message: t.String() }),
    response: t.Object({ name: t.String(), message: t.String() }),
    open: (ws) => {
      // require authentication from users
      const { auth } = ws.data;
      if (!auth.isAuthed) {
        ws.close();
      }

      ws.subscribe("global");
    },
    message: (ws, { message }) => {
      const { auth } = ws.data;
      if (!auth.isAuthed) return;

      console.log(message);

      ws.publish("global", { name: auth.user.name, message });
    },

    close: () => {
      console.log("ws closed");
    },
  });
};

In order to open a connection I want to require users to pass in a valid token in the request headers (since WS is initially an HTTP request before upgrade).

Using the default JS implementation of WebSocket, I can connect just fine:

// this works & authenticates properly
const socket = new WebSocket("ws://localhost:3000/chat/ws", {
  headers: {
    Authorization:
      "Bearer <token>",
  },
});

But I cannot send headers when using an Eden Treaty client:

const api = edenTreaty<ElysiaAPI>("http://localhost:3000");

// fails authentication
const edenSocket = api.chat.ws.subscribe(); // no way to pass in headers here

Attempting to modify $fetch params doesn't seem to have an effect either, which I assume is because it uses WebSocket and not fetch in its implementation:

// also fails authentication on .subscribe() call
const api = edenTreaty<ElysiaAPI>("http://localhost:3000", {
  $fetch: {
    headers: {
      Authorization:
        "Bearer <token>",
    },
  },
});

Expected / Desired Result

I want to be able to pass in headers to the .subscribe method, or initialize an instance of edenTreaty configured with the desired set of headers for a WebSocket connection.

Ex:

const api = edenTreaty<ElysiaAPI>("http://localhost:3000", {
  $webSocket: { // or inherit options from $fetch ?
    headers: {
      Authorization:
        "Bearer <token>",
    },
  },
});

// or:

const edenSocket = api.chat.ws.subscribe({ headers: { Authorization: "Bearer <token>" } });

Type wants me to add `.index` to api function chain before HTTP verb after v1.0

"@elysiajs/eden": "1.0.5",
"elysia": "1.0.7"

index.ts:

import { edenTreaty } from "@elysiajs/eden";
import Elysia from "elysia";

const helloHandlers = new Elysia().get("", () => "hello");

const routes = new Elysia().group("/hello", (_) => _.use(helloHandlers));

const app = new Elysia().use(routes).listen(3000);

const eden = edenTreaty<typeof app>("localhost:3000");

console.log((await eden.hello.get()).data); // <-- has red squiggly, but works!
console.log((await eden.hello.index.get()).data); // <-- no red squiggly, "NOT_FOUND"

app.stop();

output from running:

bun run index.ts
hello
NOT_FOUND

Error message:

Property 'get' does not exist on type '{ index: { get: (params?: { $fetch?: RequestInit | undefined; getRaw?: boolean | undefined; $transform?: Transform<unknown> | undefined; $query?: Record<string, string> | undefined; $headers?: Record<...> | undefined; } | undefined, options?: { ...; } | undefined) => Promise<...>; }; }'

Type Error: 'app' is a type of unknown

I get a typerror when accessing routes by their name
image

here's my code

// app.ts
import { Elysia } from 'elysia'
import { helmet } from 'elysia-helmet'
import { rateLimit } from 'elysia-rate-limit'
import swagger from '@elysiajs/swagger'
import cors from '@elysiajs/cors'
import user from './Controllers/user'
import blog from './Controllers/blog'

const app = new Elysia()

app
	.onError(({ code, error, path }) => {
		if (code === 'NOT_FOUND') {
			return { err: `Couldn't find what you was looking for` }
		}
		if (code === 'INTERNAL_SERVER_ERROR') {
			return { err: `Something went wrong on our server, We'll try to fix it ASAP!` }
		}
		console.error(`Error occurred on path ${path} Error: ${error}, ${code}`)
	})
	.use(helmet())
	.use(swagger())
	.use(cors({ origin: process.env.DOMAIN, credentials: true }))
	.use(
		rateLimit({
			duration: 300000,
			max: 5200,
			responseCode: 420,
			responseMessage: 'Our server needs a 5 min coffee break'
		})
	)
	.use(user)
	.use(blog)
	.onResponse(() => {
		console.log(`response speed was: ${performance.now()}ms`)
	})
	.listen(process.env.PORT || 5000)

console.log(`Server running on ${app.server?.url}`)

export type App = typeof app
// +page.server.ts
import type { PageLoad } from './$types'
import {edenTreaty} from '@elysiajs/eden'
import type { App } from '../../../../../backend/src/app.ts'
const app = edenTreaty<App>(`http://backend:3000`)

export const load = (async ({ url }) => {
	
	const res = app.profile.tech

	const user = res.data.user
	const state = res.data.state
	console.log(res.data)

	return { user, state }
}) satisfies PageLoad

[Feature Request] Eden with Elysia instance.

Hi, love your work on Elysia.

Would be great if Eden be used directly with Elysia instance / instance.handle method?
I think I would be useful to have a type-safe client that can be used on the server with RSC, server actions or so.

I imagine something like:

import { edenFetch } from '@elysiajs/eden'
import { App } from './server'

const fetch = edenFetch<App>(App)

export default async function ServerComponent() {
  const response = await fetch('/test/:id', {
    params: {
        id: '1895'
    }
});
  const data = await response.json(); // <โ€”- Type inferred
  return (
      <pre>
        {JSON.stringify(data, null, 2)}
      </pre>
  );
}

Importing an Eden Treaty messes with React's default types

Problem

Importing they type for my Elysia app, or importing the edenTreaty package causes the react type system to explode.

This appears to be happening wherever React is used with eden Fetch

It appears that even in the turborepo monorepo that @SaltyAom created, for some reason the "bun-types" package conflicts with react types that shows up at the very least when you try to use event handlers and I imagine in other places as well.

How I noticed this error

For example this bug only shows up when I do a `import type {App} from "../path/to/elysia/app" and goes away completely when I get rid of that line

Screen Shot 2023-10-26 at 11 53 12 AM

I have a hunch that this is ultimately coming from the "bun-types" package, but I'm wondering if there is a way to avoid importing the "bun-types" package along with the Elysia app.

To reproduce this bug:

  1. Clone this repository
  2. Create an onChange event listener in any input element inside of the react project, and you will find this error
Property 'value' does not exist on type 'EventTarget & HTMLInputElement'.

Which will not show up normally in a react application.

To Show that it's likely coming from the "bun-types" package

  1. Create a new vite app with bun create vite@latest
  • select react app
  • select typescript
  1. add bun-types as a dependency
  2. create an onChange event handler
  3. Try to call e.target.value
  4. toggle whether or not you import "bun-types" and watch the error show up and not show up accordingly

Video Summary

Here's a video walking through the bug

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.