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

feat: 1.0.0 release #91

Merged
merged 11 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# The fal.ai JS client

![@fal-ai/serverless-client npm package](https://img.shields.io/npm/v/@fal-ai/serverless-client?color=%237527D7&label=client&style=flat-square)
![@fal-ai/serverless-proxy npm package](https://img.shields.io/npm/v/@fal-ai/serverless-proxy?color=%237527D7&label=proxy&style=flat-square)
![@fal-ai/client npm package](https://img.shields.io/npm/v/@fal-ai/client?color=%237527D7&label=client&style=flat-square)
![@fal-ai/server-proxy npm package](https://img.shields.io/npm/v/@fal-ai/server-proxy?color=%237527D7&label=proxy&style=flat-square)
![Build](https://img.shields.io/github/actions/workflow/status/fal-ai/fal-js/build.yml?style=flat-square)
![License](https://img.shields.io/github/license/fal-ai/fal-js?style=flat-square)

## About the Project

The fal serverless JavaScript/TypeScript Client is a robust and user-friendly library designed for seamless integration of fal serverless functions in Web, Node.js, and React Native applications. Developed in TypeScript, it provides developers with type safety right from the start.
The fal JavaScript/TypeScript Client is a robust and user-friendly library designed for seamless integration of fal endpoints in Web, Node.js, and React Native applications. Developed in TypeScript, it provides developers with type safety right from the start.

## Getting Started

The `@fal-ai/serverless-client` library serves as a client for fal serverless Python functions. For guidance on creating your functions, refer to the [quickstart guide](https://fal.ai/docs).
The `@fal-ai/client` library serves as a client for fal apps hosted on fal. For guidance on consuming and creating apps, refer to the [quickstart guide](https://fal.ai/docs).

### Client Library

Expand All @@ -22,12 +22,12 @@ This client library is crafted as a lightweight layer atop platform standards li

1. Install the client library
```sh
npm install --save @fal-ai/serverless-client
npm install --save @fal-ai/client
```
2. Start by configuring your credentials:

```ts
import * as fal from "@fal-ai/serverless-client";
import { fal } from "@fal-ai/client";

fal.config({
// Can also be auto-configured using environment variables:
Expand All @@ -46,21 +46,21 @@ See the available [model APIs](https://fal.ai/models) for more details.

### The fal client proxy

Although the fal client is designed to work in any JS environment, including directly in your browser, **it is not recommended** to store your credentials in your client source code. The common practice is to use your own server to serve as a proxy to serverless APIs. Luckily fal supports that out-of-the-box with plug-and-play proxy functions for the most common engines/frameworks.
Although the fal client is designed to work in any JS environment, including directly in your browser, **it is not recommended** to store your credentials in your client source code. The common practice is to use your own server to serve as a proxy to fal APIs. Luckily fal supports that out-of-the-box with plug-and-play proxy functions for the most common engines/frameworks.

For example, if you are using Next.js, you can:

1. Instal the proxy library
```sh
npm install --save @fal-ai/serverless-proxy
npm install --save @fal-ai/server-proxy
```
2. Add the proxy as an API endpoint of your app, see an example here in [pages/api/fal/proxy.ts](https://github.com/fal-ai/fal-js/blob/main/apps/demo-nextjs-page-router/pages/api/fal/proxy.ts)
```ts
export { handler as default } from "@fal-ai/serverless-proxy/nextjs";
export { handler as default } from "@fal-ai/server-proxy/nextjs";
```
3. Configure the client to use the proxy:
```ts
import * as fal from "@fal-ai/serverless-client";
import { fal } from "@fal-ai/client";
fal.config({
proxyUrl: "/api/fal/proxy",
});
Expand Down
4 changes: 2 additions & 2 deletions apps/demo-express-app/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* This is only a minimal backend to get started.
*/

import * as fal from "@fal-ai/serverless-client";
import * as falProxy from "@fal-ai/serverless-proxy/express";
import { fal } from "@fal-ai/client";
import * as falProxy from "@fal-ai/server-proxy/express";
import cors from "cors";
import { configDotenv } from "dotenv";
import express from "express";
Expand Down
2 changes: 1 addition & 1 deletion apps/demo-nextjs-app-router/app/api/fal/proxy/route.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { route } from "@fal-ai/serverless-proxy/nextjs";
import { route } from "@fal-ai/server-proxy/nextjs";

export const { GET, POST, PUT } = route;
4 changes: 2 additions & 2 deletions apps/demo-nextjs-app-router/app/camera-turbo/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/* eslint-disable @next/next/no-img-element */
"use client";

import * as fal from "@fal-ai/serverless-client";
import { createFalClient } from "@fal-ai/client";
import { MutableRefObject, useEffect, useRef, useState } from "react";

fal.config({
const fal = createFalClient({
proxyUrl: "/api/fal/proxy",
});

Expand Down
19 changes: 8 additions & 11 deletions apps/demo-nextjs-app-router/app/comfy/image-to-image/page.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
/* eslint-disable @next/next/no-img-element */
"use client";

import * as fal from "@fal-ai/serverless-client";
import { createFalClient } from "@fal-ai/client";
import { useMemo, useState } from "react";

// @snippet:start(client.config)
fal.config({
const fal = createFalClient({
proxyUrl: "/api/fal/proxy", // the built-int nextjs proxy
// proxyUrl: 'http://localhost:3333/api/fal/proxy', // or your own external proxy
});
// @snippet:end

// @snippet:start(client.result.type)
type Image = {
filename: string;
subfolder: string;
type: string;
url: string;
};

type Result = {
type ComfyOutput = {
url: string;
outputs: Record<string, any>[];

Check warning on line 21 in apps/demo-nextjs-app-router/app/comfy/image-to-image/page.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
images: Image[];
};
// @snippet:end

type ErrorProps = {
error: any;

Check warning on line 26 in apps/demo-nextjs-app-router/app/comfy/image-to-image/page.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
};

function Error(props: ErrorProps) {
Expand Down Expand Up @@ -54,7 +51,7 @@
// Result state
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const [result, setResult] = useState<Result | null>(null);
const [result, setResult] = useState<ComfyOutput | null>(null);
const [logs, setLogs] = useState<string[]>([]);
const [elapsedTime, setElapsedTime] = useState<number>(0);
// @snippet:end
Expand All @@ -73,7 +70,7 @@
setElapsedTime(0);
};

const getImageURL = (result: Result) => {
const getImageURL = (result: ComfyOutput) => {
return result.outputs[9].images[0];
};

Expand All @@ -83,7 +80,7 @@
setLoading(true);
const start = Date.now();
try {
const result: Result = await fal.subscribe(
const { data } = await fal.subscribe<ComfyOutput>(
"comfy/fal-ai/image-to-image",
{
input: {
Expand All @@ -102,8 +99,8 @@
},
},
);
setResult(getImageURL(result));
setResult(getImageURL(data));
} catch (error: any) {

Check warning on line 103 in apps/demo-nextjs-app-router/app/comfy/image-to-image/page.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
setError(error);
} finally {
setLoading(false);
Expand Down
18 changes: 7 additions & 11 deletions apps/demo-nextjs-app-router/app/comfy/image-to-video/page.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
"use client";

import * as fal from "@fal-ai/serverless-client";
import { createFalClient } from "@fal-ai/client";
import { useMemo, useState } from "react";

// @snippet:start(client.config)
fal.config({
const fal = createFalClient({
proxyUrl: "/api/fal/proxy", // the built-int nextjs proxy
// proxyUrl: 'http://localhost:3333/api/fal/proxy', // or your own external proxy
});
// @snippet:end

// @snippet:start(client.result.type)
type Image = {
filename: string;
subfolder: string;
type: string;
url: string;
};

type Result = {
type ComfyOutput = {
url: string;
outputs: Record<string, any>[];

Check warning on line 20 in apps/demo-nextjs-app-router/app/comfy/image-to-video/page.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
images: Image[];
};
// @snippet:end

type ErrorProps = {
error: any;

Check warning on line 25 in apps/demo-nextjs-app-router/app/comfy/image-to-video/page.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
};

function Error(props: ErrorProps) {
Expand All @@ -50,7 +46,7 @@
// Result state
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const [result, setResult] = useState<Result | null>(null);
const [result, setResult] = useState<ComfyOutput | null>(null);
const [logs, setLogs] = useState<string[]>([]);
const [elapsedTime, setElapsedTime] = useState<number>(0);
// @snippet:end
Expand All @@ -69,7 +65,7 @@
setElapsedTime(0);
};

const getImageURL = (result: Result) => {
const getImageURL = (result: ComfyOutput) => {
return result.outputs[10].images[0];
};

Expand All @@ -79,7 +75,7 @@
setLoading(true);
const start = Date.now();
try {
const result: Result = await fal.subscribe(
const { data } = await fal.subscribe<ComfyOutput>(
"comfy/fal-ai/image-to-video",
{
input: {
Expand All @@ -97,8 +93,8 @@
},
},
);
setResult(getImageURL(result));
setResult(getImageURL(data));
} catch (error: any) {

Check warning on line 97 in apps/demo-nextjs-app-router/app/comfy/image-to-video/page.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
setError(error);
} finally {
setLoading(false);
Expand All @@ -117,7 +113,7 @@
<div className="flex w-full flex-col space-y-4">
<div className="mx-auto flex">
{imageFile && (
<img

Check warning on line 116 in apps/demo-nextjs-app-router/app/comfy/image-to-video/page.tsx

View workflow job for this annotation

GitHub Actions / build

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
src={URL.createObjectURL(imageFile)}
alt=""
className="mx-auto w-1/2"
Expand Down
47 changes: 23 additions & 24 deletions apps/demo-nextjs-app-router/app/comfy/text-to-image/page.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
"use client";

import * as fal from "@fal-ai/serverless-client";
import { createFalClient } from "@fal-ai/client";
import { useMemo, useState } from "react";

// @snippet:start(client.config)
fal.config({
const fal = createFalClient({
proxyUrl: "/api/fal/proxy", // the built-int nextjs proxy
// proxyUrl: 'http://localhost:3333/api/fal/proxy', // or your own external proxy
});
// @snippet:end

// @snippet:start(client.result.type)
type Image = {
filename: string;
subfolder: string;
type: string;
url: string;
};

type Result = {
type ComfyOutput = {
url: string;
outputs: Record<string, any>[];

Check warning on line 20 in apps/demo-nextjs-app-router/app/comfy/text-to-image/page.tsx

View workflow job for this annotation

GitHub Actions / build

Unexpected any. Specify a different type
images: Image[];
};
// @snippet:end

type ErrorProps = {
error: any;
Expand Down Expand Up @@ -53,7 +49,7 @@
// Result state
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const [result, setResult] = useState<Result | null>(null);
const [result, setResult] = useState<ComfyOutput | null>(null);
const [logs, setLogs] = useState<string[]>([]);
const [elapsedTime, setElapsedTime] = useState<number>(0);
// @snippet:end
Expand All @@ -72,7 +68,7 @@
setElapsedTime(0);
};

const getImageURL = (result: Result) => {
const getImageURL = (result: ComfyOutput) => {
return result.outputs[9].images[0];
};

Expand All @@ -82,22 +78,25 @@
setLoading(true);
const start = Date.now();
try {
const result: Result = await fal.subscribe("comfy/fal-ai/text-to-image", {
input: {
prompt: prompt,
const { data } = await fal.subscribe<ComfyOutput>(
"comfy/fal-ai/text-to-image",
{
input: {
prompt: prompt,
},
logs: true,
onQueueUpdate(update) {
setElapsedTime(Date.now() - start);
if (
update.status === "IN_PROGRESS" ||
update.status === "COMPLETED"
) {
setLogs((update.logs || []).map((log) => log.message));
}
},
},
logs: true,
onQueueUpdate(update) {
setElapsedTime(Date.now() - start);
if (
update.status === "IN_PROGRESS" ||
update.status === "COMPLETED"
) {
setLogs((update.logs || []).map((log) => log.message));
}
},
});
setResult(getImageURL(result));
);
setResult(getImageURL(data));
} catch (error: any) {
setError(error);
} finally {
Expand Down
15 changes: 6 additions & 9 deletions apps/demo-nextjs-app-router/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
"use client";

import * as fal from "@fal-ai/serverless-client";
import { createFalClient } from "@fal-ai/client";
import { useMemo, useState } from "react";

// @snippet:start(client.config)
fal.config({
const fal = createFalClient({
// credentials: 'FAL_KEY_ID:FAL_KEY_SECRET',
proxyUrl: "/api/fal/proxy", // the built-int nextjs proxy
// proxyUrl: 'http://localhost:3333/api/fal/proxy', // or your own external proxy
});
// @snippet:end

// @snippet:start(client.result.type)
type Image = {
url: string;
file_name: string;
file_size: number;
};
type Result = {
type Output = {
image: Image;
};
// @snippet:end
Expand Down Expand Up @@ -51,7 +48,7 @@ export default function Home() {
// Result state
const [loading, setLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const [result, setResult] = useState<Result | null>(null);
const [result, setResult] = useState<Output | null>(null);
const [logs, setLogs] = useState<string[]>([]);
const [elapsedTime, setElapsedTime] = useState<number>(0);
// @snippet:end
Expand Down Expand Up @@ -79,7 +76,7 @@ export default function Home() {
setLoading(true);
const start = Date.now();
try {
const result: Result = await fal.subscribe("fal-ai/illusion-diffusion", {
const result = await fal.subscribe<Output>("fal-ai/illusion-diffusion", {
input: {
prompt,
image_url: imageFile,
Expand All @@ -96,7 +93,7 @@ export default function Home() {
}
},
});
setResult(result);
setResult(result.data);
} catch (error: any) {
setError(error);
} finally {
Expand Down
4 changes: 2 additions & 2 deletions apps/demo-nextjs-app-router/app/queue/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import * as fal from "@fal-ai/serverless-client";
import { fal } from "@fal-ai/client";
import { useState } from "react";

fal.config({
Expand Down Expand Up @@ -54,7 +54,7 @@ export default function Home() {
setLoading(true);
const start = Date.now();
try {
const result: any = await fal.subscribe(endpointId, {
const result = await fal.subscribe(endpointId, {
input: JSON.parse(input),
logs: true,
// mode: "streaming",
Expand Down
Loading
Loading