@danstackme/apity
TypeScript icon, indicating that this package has built-in type declarations

0.5.7 • Public • Published

Apity

A type-safe API client generator for React applications with runtime validation.

Features

  • 🔒 Type-safe API endpoints with TypeScript
  • 📄 Define your API schema with Zod
  • 🔄 Static type generation for path and query parameters
  • 🎯 Runtime validation with Zod
  • ⚡️ React Query integration
  • 🔄 Import from OpenAPI/Swagger specifications

Installation

npm install @danstackme/apity

Quick Start

  1. Define your API endpoints:
// src/endpoints.ts
import { createApi, createApiEndpoint } from "@danstackme/apity";
import { z } from "zod";

// Define your Zod schemas
const UserSchema = z.object({
  id: z.string(),
  name: z.string(),
  email: z.string().email(),
});

const UsersResponseSchema = z.array(UserSchema);

const CreateUserSchema = z.object({
  name: z.string(),
  email: z.string().email(),
});

const QuerySchema = z.object({
  limit: z.number(),
  offset: z.number().optional(),
});

// Define your endpoints
const getUsersEndpoint = createApiEndpoint({
  method: "GET",
  response: UsersResponseSchema,
  query: QuerySchema,
});

const createUserEndpoint = createApiEndpoint({
  method: "POST",
  response: UserSchema,
  body: CreateUserSchema,
});

const getUserEndpoint = createApiEndpoint({
  method: "GET",
  response: UserSchema,
});

const updateUserEndpoint = createApiEndpoint({
  method: "PUT",
  response: UserSchema,
  body: CreateUserSchema,
});

const deleteUserEndpoint = createApiEndpoint({
  method: "DELETE",
  response: z.void(),
});

// Export your endpoints
export const fetchEndpoints = {
  "/users": [getUsersEndpoint],
  "/users/[id]": [getUserEndpoint],
} as const;

export const mutateEndpoints = {
  "/users": [createUserEndpoint],
  "/users/[id]": [updateUserEndpoint, deleteUserEndpoint],
} as const;

// Create and export the API instance
export const api = createApi({
  baseUrl: "https://api.example.com",
  fetchEndpoints,
  mutateEndpoints,
});
  1. Set up the type definitions in your application (typically in App.tsx):
// src/App.tsx
import { ApiProvider } from "@danstackme/apity";
import { api, fetchEndpoints, mutateEndpoints } from "./endpoints";

// Register your endpoints for type safety
declare module "@danstackme/apity" {
  interface Register {
    fetchEndpoints: typeof fetchEndpoints;
    mutateEndpoints: typeof mutateEndpoints;
  }
}

function App() {
  return (
    <ApiProvider api={api}>
      <YourApp />
    </ApiProvider>
  );
}
  1. Use the hooks in your components:
// src/components/UserList.tsx
import { useFetch, useMutate } from "@danstackme/apity";

export function UserList() {
  // Fetch users with required query parameters
  const { data: users, isLoading } = useFetch({
    path: "/users",
    query: {
      limit: 10,
      offset: 0,
    },
  });

  // Set up a mutation with path parameters
  const { mutate: createUser, isPending: isCreating } = useMutate({
    path: "/users/[id]",
    method: "PUT",
    params: { id: 1 },
  });

  // Another mutation example
  const { mutate: deleteUser } = useMutate({
    path: "/users/[id]",
    method: "DELETE",
    params: { id: 1 },
  });

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>Users</h1>

      {/* Create user form */}
      <form
        onSubmit={(e) => {
          e.preventDefault();
          const formData = new FormData(e.currentTarget);
          createUser({
            name: formData.get("name") as string,
            email: formData.get("email") as string,
          });
        }}
      >
        <input type="text" name="name" placeholder="Name" required />
        <input type="email" name="email" placeholder="Email" required />
        <button type="submit" disabled={isCreating}>
          {isCreating ? "Creating..." : "Create User"}
        </button>
      </form>

      {/* User list */}
      <div>
        {users?.map((user) => (
          <div key={user.id}>
            <h3>{user.name}</h3>
            <p>{user.email}</p>
            <button onClick={() => deleteUser({ params: { id: user.id } })}>
              Delete
            </button>
          </div>
        ))}
      </div>
    </div>
  );
}

Advanced Usage

Adding Middleware

You can add middleware for authentication, error handling, and more:

export const api = createApi({
  baseUrl: "https://api.example.com",
  fetchEndpoints,
  mutateEndpoints,
  middleware: {
    before: (config) => {
      // Add authentication header
      config.headers = {
        ...config.headers,
        Authorization: `Bearer ${localStorage.getItem("token")}`,
      };
      return config;
    },
    onError: (error) => {
      // Handle unauthorized errors
      if (error.response?.status === 401) {
        window.location.href = "/login";
      }
      return Promise.reject(error);
    },
  },
});

Path Parameters

For dynamic routes, use square brackets in the path and provide params:

const { data: user } = useFetch({
  path: "/users/[id]",
  params: { id: "123" },
});

OpenAPI/Swagger Import

You can automatically generate type-safe endpoints from your OpenAPI/Swagger specification using the built-in CLI tool:

npx @danstackme/apity <path-to-spec> --outDir <out-directory>

The --outDir defaults to /src

The tool supports both JSON and YAML specifications and will:

  • Automatically convert Swagger 2.0 to OpenAPI 3.0
  • Generate type-safe endpoints with Zod validation
  • Create path and query parameter types
  • Set up proper request/response validation

For example, given an OpenAPI spec like:

openapi: 3.0.0
paths:
  /users:
    get:
      parameters:
        - name: limit
          in: query
          schema:
            type: integer
      responses:
        200:
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/User"
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateUser"
      responses:
        200:
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"

It will generate a fully typed endpoints.ts file with proper Zod validation schemas that you can immediately use in your application.

License

MIT

Package Sidebar

Install

npm i @danstackme/apity

Weekly Downloads

43

Version

0.5.7

License

MIT

Unpacked Size

180 kB

Total Files

15

Last publish

Collaborators

  • dosmond37