Skip to content

API Setup

  1. Install Axios

    To manage HTTP requests efficiently, we will use Axios, a widely-used promise-based HTTP client.

    Terminal window
    pnpm add axios
  2. Install Bun

    Bun is a fast JavaScript runtime that will be used for executing server-side code.

    Terminal window
    npm install -g bun

    For detailed instructions, refer to the Bun Installation Guide.

  3. Configure Environment Variables

    In the root directory of your project, create a .env.local file and define the following variables:

    .env.local
    VITE_AUTH_TOKEN = <AUTH_TOKEN>
    VITE_DATABASE = <DATABASE>
    VITE_BASE_URL = <BASE_URL>
    VITE_FQ_LOCAL_SERVER_PORT = 4466
    VITE_FQ_LOCAL_SERVER = http://localhost:4466
    • Replace <DATABASE> with your database name.
    • Replace <AUTH> with your authentication credentials.
    • Replace <BASE_URL> with the base URL of your API server.
  4. API Service Configuration (src/services/Api.ts)

    Create or update the src/services/Api.ts file with the following content to set up a flexible API service:

    src/services/Api.ts
    import axios, { type AxiosRequestConfig } from "axios";
    import tokens from "./tokens.json";
    interface Tokens {
    [key: string]: string | false;
    }
    // Base64 encoding characters
    const base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    function toBase64(num: number): string {
    let result = "";
    const str = num.toString();
    for (let i = 0; i < str.length; i++) {
    const charCode = parseInt(str[i]);
    result += base64chars[charCode % 64];
    }
    return result;
    }
    const database = import.meta.env.VITE_DATABASE;
    const baseUrl = import.meta.env.VITE_BASE_URL;
    const localServer = import.meta.env.VITE_FQ_LOCAL_SERVER;
    type HttpMethod = "get" | "post" | "put" | "delete" | "sql";
    type RequestOptions = {
    loading?: boolean;
    body?: {
    sql: "string";
    params: [{ [key: string]: string | number }];
    };
    key?: string;
    page?: string;
    sort?: string;
    joins?: string;
    filter?: string;
    search?: string;
    nearby?: string;
    hidden?: string;
    fields?: string;
    session?: string;
    validation?: string;
    permissions?: string;
    };
    function uniqueKey(input: string) {
    let code = input.charCodeAt(0);
    for (let i = 0; i < input.length; i++) {
    const char = input.charCodeAt(i);
    code = (code << 5) - code + char;
    code &= code;
    }
    return toBase64(Math.abs(code)).substring(0, 8);
    }
    function getKey(method: HttpMethod, url: string, options: RequestOptions) {
    if (!localServer) throw new Error("localServer is not defined");
    const _url = localServer + url;
    const parsed_url = new URL(_url);
    const pathname = parsed_url.pathname;
    const request: any = {
    fields: options?.fields,
    hidden: options?.hidden,
    filter: options?.filter,
    nearby: options?.nearby,
    collections: options?.joins,
    permissions: options?.permissions,
    validation: options?.validation,
    };
    request["body_is_array"] = Array.isArray(options.body || {});
    let tokenStr = pathname;
    for (const key in request) {
    if (request[key as keyof typeof request]) {
    tokenStr += key + ":" + request[key as keyof typeof request];
    }
    }
    const key = method + ":" + pathname + ">" + uniqueKey(tokenStr);
    return key;
    }
    const makeRequest = async (method: HttpMethod, endpoint: string, options: RequestOptions = {}): Promise<unknown> => {
    const {
    body,
    page,
    sort,
    joins,
    hidden,
    fields,
    filter,
    search,
    nearby,
    session,
    validation,
    permissions,
    loading = true,
    } = options;
    const headers: any = {};
    if (hidden) headers.hidden = hidden;
    if (filter) headers.filter = filter;
    if (fields) headers.fields = fields;
    if (session) headers.session = session;
    if (nearby) headers.nearby = nearby;
    if (joins) headers.collections = joins;
    if (validation) headers.validation = validation;
    if (permissions) headers.permissions = permissions;
    const key = getKey(method, endpoint, options);
    const token = (tokens as Tokens)[key] || false;
    if (!token) {
    headers["key"] = key;
    } else {
    headers.token = token;
    }
    const params: { [key: string]: string | number | boolean | object | undefined } = {
    page: page,
    sort: sort,
    search: search,
    };
    try {
    if (loading) {
    console.log("Loading started...");
    }
    const axiosInstance = axios.create({
    baseURL: token ? baseUrl : localServer,
    headers: { app: database },
    });
    const requestConfig: AxiosRequestConfig = {
    method,
    params,
    headers,
    data: body,
    url: endpoint,
    };
    const response = await axiosInstance(requestConfig);
    return response.data;
    } catch (error: any) {
    console.error(`${method.toUpperCase()} Error:`, error.message);
    throw error;
    } finally {
    if (loading) {
    console.log("Loading completed.");
    }
    }
    };
    const Api = {
    get: async (endpoint: string, options?: RequestOptions): Promise<any> => makeRequest("get", endpoint, options),
    put: async (endpoint: string, options?: RequestOptions): Promise<any> => makeRequest("put", endpoint, options),
    post: async (endpoint: string, options?: RequestOptions): Promise<any> => makeRequest("post", endpoint, options),
    delete: async (endpoint: string, options?: RequestOptions): Promise<any> => makeRequest("delete", endpoint, options),
    sql: async (endpoint: string, options?: RequestOptions): Promise<any> =>
    makeRequest("post", `/sql-${endpoint.replace("/", "")}`, options),
    };
    export default Api;
  5. Set Up API Server (src/services/server.js)

    Create or modify the src/services/server.js file to configure the local API server using Bun:

    src/services/server.js
    import { serve, file, write } from "bun";
    const port = import.meta.env.VITE_FQ_LOCAL_SERVER_PORT;
    const baseUrl = import.meta.env.VITE_BASE_URL;
    const hostname = baseUrl.split("://")[1];
    const basicAuth = "Basic " + import.meta.env.VITE_AUTH_TOKEN;
    const tokensPath = "src/services/tokens.json";
    const CORS_HEADERS = {
    headers: {
    "Access-Control-Allow-Origin": "*",
    "Access-Control-Allow-Methods": "*",
    "Access-Control-Allow-Headers": "*",
    },
    };
    serve({
    port,
    async fetch(req) {
    if (req.method === "OPTIONS") {
    return new Response("Departed", CORS_HEADERS);
    }
    const url = new URL(req.url);
    const method = req.method;
    const bodyText = await req.text();
    const tokensFile = file(tokensPath);
    const tokens = await tokensFile.json();
    const key = req.headers.get("key");
    url.port = 443;
    url.protocol = "https:";
    url.hostname = hostname;
    req.headers.delete("host");
    req.headers.set("Accept-Encoding", "br");
    req.headers.append("Authorization", basicAuth);
    const response = await fetch(url, {
    method: method,
    body: bodyText,
    headers: req.headers,
    });
    const body = await response.json();
    if (key && body?.token) {
    tokens[key] = body.token;
    await write(tokensPath, JSON.stringify(tokens, null, 2));
    }
    return new Response(JSON.stringify(body), CORS_HEADERS);
    },
    });
    console.log(`FrontQL dev server is running on http://localhost:${port}`);
  6. Create an Empty Tokens File

    Create an empty JSON file at src/services/tokens.json:

    {}
  7. Organize API Calls

    To maintain a clean project structure, it is recommended to organize your API calls into separate modules. For example, for the ‘users’ related API calls, create a src/apis/users.js file:

    src/apis/users.js
    import Api from "services/Api";
    export async function getUsers() {
    const response = await Api.get("/users");
    return response;
    }
  8. Consolidate API Calls in a Single File (apis/index.js)

    Create an index.js file in the src/apis directory to group and export all your API functions:

    src/apis/index.js
    import { getUsers } from "./users";
    export { getUsers };
  9. Start the Local Server with Bun

    To run the API server locally, use the following command:

    Terminal window
    bun src/services/server.js