Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Axios is not supported on Edge Runtime #161

Open
viktorlarsson opened this issue Jun 16, 2023 · 9 comments · Fixed by #216 · May be fixed by #225
Open

Axios is not supported on Edge Runtime #161

viktorlarsson opened this issue Jun 16, 2023 · 9 comments · Fixed by #216 · May be fixed by #225

Comments

@viktorlarsson
Copy link

viktorlarsson commented Jun 16, 2023

Description

As described in this ticket, Axios is not supported (and I don't think it will in any time near future) and you cannot use this library on an Serverless edge functions for example like Vercel, AWS etc. (or any service worker like Cloudflare)

axios/axios#5523

https://vercel.com/guides/library-sdk-compatible-with-vercel-edge-runtime-and-functions#library-recommendations

Steps to reproduce

  • Spin up a new NextJS app, create a new api endpoint using edge runtime. It will fail.
import typeSenseService from "@/services/typesense/typesense";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

export const config = {
  runtime: "edge",
};

export default async function handler(req: NextRequest) {
  if (req.method !== "POST") {
    return NextResponse.json({ error: "no" }, { status: 500 });
  }

  try {
    const { searchParams } = new URL(req?.url ?? "");
    const term = searchParams.get("term") as string;

    console.log(term);

   // Just a simple typesense search here
    const data = typeSenseService.search("index_name", term);

    return NextResponse.json({ result: data });
  } catch (e: any) {
    console.log(e);
    return NextResponse.json({ error: "no" }, { status: 500 });
  }

Expected Behavior

  • It should work if you move to fetch, or create more agnostic way to pass the httpClient. Example below:


export interface Fetch {
  (url: string, init?: RequestInit): Promise<Response>
}

export interface RequestInit {
  headers?: any
  method?: string
  body?: string
  redirect?: string
}

export interface Response {
  headers: Headers
  ok: boolean
  status: number
  statusText: string
  text: () => Promise<string>
  json: () => Promise<any>
}

export interface CallInfo extends RequestInit {
  name: string
  type: string
  url: string
  status: number
  statusText: string
  error?: Error
}

export interface FetcherOptions {
  record?: (info: CallInfo, data: string | Blob | ArrayBuffer | any) => Promise<void>
}

export interface Fetcher {
  (name: string, url: string, init?: RequestInit, childId?: string): Promise<Response>
}

export interface Recorder {
  (info: CallInfo, data: string | Blob | ArrayBuffer | any): Promise<void>
}

const record = async (
  name: string,
  url: string,
  init: RequestInit | undefined,
  type: string,
  options: FetcherOptions,
  response: Response,
  data: string | ArrayBuffer | Blob | any
): Promise<void> => {
  if (!options.record) {
    return
  }
  const info: CallInfo = {
    ...(init || {}),
    name,
    url,
    type,
    status: response.status,
    statusText: response.statusText
  }
  await options.record(info, data)
}

export default function wrap(fetch: Fetch, options: FetcherOptions = {}): Fetcher {
  return async (name: string, url: string, config: RequestInit = { headers: {} }): Promise<Response> => {
    const response = await fetch(url, config)

    const wrapMethod = (res: Response, methodName: string): void => {
      // @ts-ignore
      const original = res[methodName].bind(res)
      // @ts-ignore
      res[methodName] = async (...args) => {
        const result = await original(...args)
        await record(name, url, config, methodName, options, response, result)
        return result
      }
    }
    wrapMethod(response, 'json')
    wrapMethod(response, 'text')

    return response
  }
}


// Usage

const httpClient = wrap(axios, options)
const httpClient = wrap(fetch, options)

const ticketResponse = httpClient('unique-endpoint', '/api/tickets', {
  method: 'POST'
})

Actual Behavior

it... should work :D

Metadata

Typesense Version: 1.5.4

OS: Mac OS X

@denny64
Copy link

denny64 commented Aug 5, 2023

Any temporarily solutions to this?

@jasonbosco
Copy link
Member

It would be a non-trivial amount of work to replace axios in typesense-js.

So the workaround would be to use say fetch and call the Typesense HTTP APIs directly.

@leerobert
Copy link

It's worth exploring have an edge/deno supported js/ts library as many users are switching to such implementations for their backend.

Related:
#95

@leerobert
Copy link

That axios is not working on edge workers like Deno Deploy, Vercel Edge Functions or Cloudflare Workers is an absolute dealbreaker for me.

You can just make fetch('<urhost>/collection/) calls but it is annoying. That's what we do though.

@sixers
Copy link

sixers commented Feb 6, 2024

I use pnpm aliases with redaxios

In my package.json I specify:

 "axios": "npm:redaxios@^0.5.1",

And I also have the following: .pnpmfile.cjs

function readPackage(pkg) {
 if (pkg.dependencies && pkg.name === "typesense" && pkg.dependencies.axios) {
   pkg.dependencies.axios = "npm:redaxios@^0.5.1";
 }

 return pkg;
}

module.exports = {
 hooks: {
   readPackage,
 },
};

This setup works on Cloudflare Workers, not sure about other platforms.

@miguelvictor
Copy link

Could we bump the version of axios to the latest one? I think the latest version of axios has the fetch adapter which should work for runtimes other than node.

@zwily
Copy link

zwily commented Jul 31, 2024

I just tried this out on [email protected] (in a cloudflare worker) and get this error now:

[mf:err] TypeError: http_1.Agent is not a constructor
    at null.<anonymous> (/Users/zwily/Source/bhr/node_modules/.pnpm/[email protected]_@[email protected]/node_modules/typesense/src/Typesense/ApiCall.ts:223:38)
    at step (/Users/zwily/Source/bhr/node_modules/.pnpm/[email protected]/node_modules/tslib/tslib.js:195:27)
    at Object.next (/Users/zwily/Source/bhr/node_modules/.pnpm/[email protected]/node_modules/tslib/tslib.js:176:57)
    at step (/Users/zwily/Source/bhr/node_modules/.pnpm/[email protected]/node_modules/tslib/tslib.js:180:143)
    at Object.next (/Users/zwily/Source/bhr/node_modules/.pnpm/[email protected]/node_modules/tslib/tslib.js:176:57)
    at fulfilled (/Users/zwily/Source/bhr/node_modules/.pnpm/[email protected]/node_modules/tslib/tslib.js:166:62)```

@jasonbosco jasonbosco reopened this Aug 1, 2024
@cungminh2710
Copy link
Contributor

@jasonbosco I think that's because http and https node packages aren't compatible with Cloudflare worker. Should be a different issue i believe

@cungminh2710
Copy link
Contributor

@zwily http and https package were introduced in v1.9.0. You could use typesense v1.8.2 and install axios v1.7.2 to use Cloudflare worker - this is how I got it working. Hope that helps

@viktorlarsson viktorlarsson linked a pull request Aug 23, 2024 that will close this issue
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
8 participants