Skip to content

Commit

Permalink
feat: 1.0.0 release (#91)
Browse files Browse the repository at this point in the history
* chore(client): rename function module

* chore: allow client to be created multiple times

singleton client is not the default but it still present as a compatibility layer

* chore: update docs

* feat(client): improved result typing

* chore: update demo app code

* chore: updated reference docs

* chore: update proxy code

* chore: alpha release

* chore: fix lint staged rule

* chore: clean-up docs

* chore: reference docs updated
  • Loading branch information
drochetti authored Oct 7, 2024
1 parent 762f289 commit 543b920
Show file tree
Hide file tree
Showing 85 changed files with 1,466 additions and 3,225 deletions.
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,29 +1,26 @@
/* 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>[];
images: Image[];
};
// @snippet:end

type ErrorProps = {
error: any;
Expand Down Expand Up @@ -54,7 +51,7 @@ export default function ComfyImageToImagePage() {
// 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 @@ export default function ComfyImageToImagePage() {
setElapsedTime(0);
};

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

Expand All @@ -83,7 +80,7 @@ export default function ComfyImageToImagePage() {
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,7 +99,7 @@ export default function ComfyImageToImagePage() {
},
},
);
setResult(getImageURL(result));
setResult(getImageURL(data));
} catch (error: any) {
setError(error);
} finally {
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,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>[];
images: Image[];
};
// @snippet:end

type ErrorProps = {
error: any;
Expand All @@ -50,7 +46,7 @@ export default function ComfyImageToVideoPage() {
// 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 @@ export default function ComfyImageToVideoPage() {
setElapsedTime(0);
};

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

Expand All @@ -79,7 +75,7 @@ export default function ComfyImageToVideoPage() {
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,7 +93,7 @@ export default function ComfyImageToVideoPage() {
},
},
);
setResult(getImageURL(result));
setResult(getImageURL(data));
} catch (error: any) {
setError(error);
} finally {
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>[];
images: Image[];
};
// @snippet:end

type ErrorProps = {
error: any;
Expand Down Expand Up @@ -53,7 +49,7 @@ export default function ComfyTextToImagePage() {
// 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 @@ export default function ComfyTextToImagePage() {
setElapsedTime(0);
};

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

Expand All @@ -82,22 +78,25 @@ export default function ComfyTextToImagePage() {
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

0 comments on commit 543b920

Please sign in to comment.