API Calls

    You can find API endpoints under /src/app/api/route_name/route.ts

    We are using react-query to make NON GET API calls to get the maximum performance benefits read their docs here or continue we have got some examples.

    Protecting an API route

    if you don't want public access to a specific API route or you want certain roles only to be able to access a route you can use the middleware helper function

    1. Create a route under app/api/your_route_name/route.ts
    2. Import and use the middleware helper function like this

    /src/app/api/your_route_name/route.ts

    import { withAuthMiddleware } from "lib/auth/middleware";
    
    export const POST = withAuthMiddleware(
      async (request, { params }, { session, pathname, user }) => {
        // your code here
      },
    );
    
    1. To add only specific allowed roles

    /src/app/api/your_route_name/route.ts

    import { withAuthMiddleware } from "lib/auth/middleware";
    import { Role } from "@prisma/client";
    
    export const POST = withAuthMiddleware(
      async (request, { params }, { pathname, session, user }) => {
        // your code here
      },
      [Role.ADMIN],
    );
    
    1. To throw or handle an error we do something like this

    /src/app/api/your_route_name/route.ts

    import { withAuthMiddleware } from "lib/auth/middleware";
    import { Role } from "@prisma/client";
    import { APIError } from "types";
    import { getErrorResponse } from "utils/server";
    
    export const POST = withAuthMiddleware(
      async (request, { params }, { pathname, session, user }) => {
        try {
          // your code here
          if (expected_fail) {
            throw new APIError({
              pathname,
              message: "your error message",
              details: {}, // optional object you might want to send to the client side
              status: 400, // optional but it is always recommended to add the correct code
            });
          }
          if (unexpected_fail) {
            throw new Error("your error message");
          }
        } catch (error) {
          // Catches the error and logs unexpected errors to console for easy tracking
          return getErrorResponse(error, pathname);
        }
      },
      [Role.ADMIN],
    );
    

    Calling endpoints

    To call these endpoints we are using axios with interceptors and configured it to

    • Handle errors by showing a Toast message
    • Handle unauthenticated users by showing a log in dialog
    • Use the backend base url so we don't have to always add http://localhost:3000/api before each API route path

    You can import this configured axios instance from

    TypeScript

    import axios from "lib/axios";
    

    In general you can use axios directly anywhere you want but because we care about performance and clean code we are using react-query alongside axios

    for example if you want to make an api call and follow the same pattern we are following you can do something like this

    1. Create your api call under /src/lib/api

    /src/lib/api

    import axios from "lib/axios";
    
    export const example_api_call = async (payload) => {
      return (
        axios
          // notice we don't write /api at the beginning of the path
          // it is already included with our axios instance
          .post<ReturnType>("/example/request", payload)
          .then((res) => res.data)
      );
    };
    
    1. Use react-query's useMutation to make use of this POST request

    /src/app/example_page/page.tsx

    import { example_api_call } from "lib/api";
    import { useMutation } from "@tanstack/react-query";
    import { Button } from "components/ui/button";
    
    const ExamplePage = () => {
        // notice how you get isPending and isError so you don't have
        // to create a state for them
      const { mutate, isPending, isSuccess, isError } = useMutation({
        // pass your api call to mutationFn
        mutationFn: example_api_call,
        onSuccess: () => {
          // do something on success
          // e.g. show toast message
          // e.g. redirect to a different page
        },
        onError: () => {
          // do something on error
          // if you are using our axios instance
          // errors from api calls will automatically show
          // so do anything other than showing the message
        },
      });
    
      return (
        <div>
          <Button
            isLoading={isPending}
            disabled={isPending}
            onClick={() => mutate()}
          >
            trigger example_api_call()
          </Button>
    
          {isSuccess && <p>API Call Successful!</p>}
          {isError && <p>API Call Unsuccessful</p>}
        </div>
      );
    };
    
    export default ExamplePage;
    

    Fetch data on the client

    If your api call is a GET request and has to be fetched on the client side navigate to /src/lib/queries.ts and update it

    /src/lib/queries.ts

    import { queryOptions } from "@tanstack/react-query";
    import { getMyDataAPICall } from "lib/api";
    
    export const queries = {
      // ...
      getMyData: queryOptions({
        placeholderData: { data: "default value" }, // add default value of the response
        queryKey: ["my-data"], // add a key to cache the response (e.g. any keyword that represents your data)
        queryFn: getMyDataAPICall, // add your GET request call here
      }),
    };
    

    Then use it in your page/component like this

    /src/app/example_page/page.tsx

    "use client";
    
    import { useQuery } from "@tanstack/react-query";
    
    const ExamplePage = () => {
      const { data, isFetching } = useQuery(queries.getMyData);
    
      return <div>{isFetching ? "loading" : data}</div>;
    };
    
    export default ExamplePage;
    

    Fetching data on the server

    If your api call is a GET request and has to be fetched on the server side navigate to the page you want to fetch the data /src/app/example_page/page.tsx

    /src/app/example_page/page.tsx

    import { db } from "lib/db";
    
    // mark your page as async function
    const ExamplePage = async () => {
      // fetch the data directly from the database
      const orders = await db.order.findMany();
    
      // pass the data to any other component
      return <MyCustomComponent orders={orders} />;
    };
    
    export default ExamplePage;