diff --git a/.babelrc.js b/.babelrc.js new file mode 100644 index 000000000..c5154858d --- /dev/null +++ b/.babelrc.js @@ -0,0 +1,14 @@ +module.exports = { + babelrcRoots: ["packages/*"], + plugins: ["babel-plugin-transform-vite-meta-env"], + presets: [ + "@babel/preset-env", + "@babel/preset-typescript", + [ + "@babel/preset-react", + { + runtime: "automatic", + }, + ], + ], +}; diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 000000000..092cb736d --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,7 @@ +{ + "mcpServers": { + "nx-mcp": { + "url": "http://localhost:9057/sse" + } + } +} \ No newline at end of file diff --git a/.cursor/rules/documentation.mdc b/.cursor/rules/documentation.mdc new file mode 100644 index 000000000..0c82c40d1 --- /dev/null +++ b/.cursor/rules/documentation.mdc @@ -0,0 +1,396 @@ +--- +description: Best practices for writing documentation approach +globs: **/*.mdx, **/*.md +alwaysApply: false +--- + +# Documentation Best Practices + +## General Guidelines + +- We're using docusaurus and its features to build our documentation +- Keep documentation clear, concise, and up-to-date +- Use consistent language and tone throughout +- Write for your audience (developers, end-users, etc.) +- Follow a logical structure with proper headings +- Use active voice and present tense +- Include examples for complex concepts +- Limit each document to a single topic or concern +- Split # and ## headings with --- separator + +## Inspirations + +The documentation approach in this project is inspired by the best practices found in the React and Stripe +documentation. Both of these projects are renowned for their: + +- **Clarity and Simplicity**: Information is presented in a clear, concise, and approachable manner, making it easy for + developers of all experience levels to understand and use. +- **Logical Structure**: Content is organized with a strong hierarchy, intuitive navigation, and consistent formatting, + helping users quickly find what they need. +- **Comprehensive Examples**: Both documentations provide real-world, copy-pastable code examples that are always + up-to-date and verified against the latest version of the library. +- **Developer-Centric Focus**: The needs of developers are prioritized, with detailed guides, API references, and + troubleshooting sections that address common use cases and pain points. +- **Visual Aids and Interactivity**: Use of diagrams, interactive elements, and visual cues to enhance understanding of + complex concepts. + +By drawing inspiration from React and Stripe, this project aims to deliver documentation that is not only technically +accurate but also highly usable, discoverable, and enjoyable for all users. + + +## Structure + +1. Title +2. Description +3. Admonition with types: + - :::tip is good for docs and "Purpose" (Purpose - should contain numbered list of features with bold parts of text) + - :::secondary for guides and "What you'll learn" + - :::success for the end of guide, or summaries of what you learnt + - :::caution / :::warning for important and possibly unsafe details + - :::danger for places that can cause errors or unexpected behavior +This admonition should usually have the numbered list +1. Other sections... + +## Markdown Usage + +- Use proper heading hierarchies (H1 > H2 > H3) +- Under each heading there should be some description or text +- Apply consistent formatting for code blocks with language specification +- Use lists for sequential or grouped information +- Utilize tables for structured data comparison +- Include properly sized and labeled images when helpful +- Use blockquotes for important notes or warnings +- Add horizontal rules to separate major sections +- Use the import directive syntax `(@import {packageName} {componentName} {content type})` to selectively include + essential information from autogenerated documentation. +- Use admonitions for warnings, notes, tips, and important information, it is important to leave empty space around it: +##### Bad use of admonitions +```md +:::note Some admonition title +Some admonition description +::: +``` +#### Good use of admonitions +```md +:::note Some admonition title + +Some admonition description + +::: +``` + +## Importer Usage Guide + +The documentation system includes a powerful import directive that allows embedding auto-generated API documentation +into markdown files. This helps maintain consistency between code and documentation. + +### Basic Syntax + +``` +(@import {packageName} {componentName} {contentType}) +``` + +- **packageName**: Name of the package from `/packages` directory (e.g., `core`, `client`, `react`) +- **componentName**: Type of component to import (e.g., `class`, `function`, `interface`, `type`) +- **contentType**: What aspect of the component to import + +### Finding Component Names + +Component names for import directives can be found by browsing the `/documentation/docs/api` directory. Each file in +this directory represents a component from the codebase, and the filename corresponds to the component name that should +be used in the import directive. This makes it easy to discover available components for documentation imports. + +### Available Content Types + +| Content Type | Description | +| -------------- | ------------------------------------ | +| definition | Complete definition of the component | +| npm | NPM installation information | +| import | Import statement for the component | +| details | Detailed component information | +| description | Component description from JSDoc | +| generics | Generic parameters information | +| method | Single method documentation | +| methods | All methods documentation | +| name | Component name only | +| parameters | Function parameters | +| preview | Brief component preview | +| properties | Object/interface properties | +| returns | Return value information | +| returnsPreview | Brief return value preview | +| signature | Function/method signature | +| sources | Source code information | +| type | Type information | + +### Usage + +(@import core Client import) (@import core Client description) + +### Best Practices + +- Only import essential information to keep documentation concise +- Use preview content types for overview sections +- Use specific content types (parameters, returns, etc.) for detailed documentation +- Combine multiple imports to create comprehensive but organized documentation +- Always verify that imported content appears correctly in the rendered documentation + +## Documentation Links + +When creating links between documentation pages, follow these guidelines to ensure consistency and maintainability: + +### Link Types + +The documentation supports two primary ways to create links between pages: + +1. **Markdown Links** + + ```md + [Link Text](mdc:documentation/docs/path/to/file.md) + ``` + + - Always use relative paths from the root + - All paths should start with `/documentation/docs/` + - Use descriptive link text that indicates the target content + +2. **LinkCard Component** + + ```jsx + + ``` + + - Use for featured links that deserve visual emphasis + - Use different types depending on the link source - guides | docs | integrations | promo | api + - Provide a clear title and concise description + - Use for the links taking whole paragraph + - Follow the same path rules as markdown links + +### Best Practices for Links + +- Ensure all links lead to files within the `/documentation/docs` directory +- Verify links work correctly after documentation structure changes +- Use LinkCards for navigation between major sections or to highlight important content +- Use standard markdown links for inline references within text +- Keep link text descriptive and meaningful (avoid "click here" or "read more") +- Consider adding a brief context about what the reader will find in the linked document + +### Link Path Requirements + +- All link paths must follow this pattern: `/documentation/docs/[path]/[filename]` +- Never omit the file extension, as this can break documentation navigation and versioning +- This applies to both standard Markdown links and LinkCard component paths + +## Documentation Types and Locations + +- The project features three distinct types of documentation: + + 1. **Core Documentation** (`/documentation/docs`) + - Covers overall ideas behind modules + - Provides limited but focused examples + - Explains the purpose, important features, methods and functionalities + - Contains links to other important documentation sections (guides, API references) + 2. **Guides** (`/documentation/docs/guides`) + - Problem-focused with detailed explanations + - Provides complete code examples with syntax highlighting + - Includes step-by-step instructions for implementation + - Addresses specific use cases and solutions + 3. **API References** (`/documentation/docs/api`) + - Autogenerated documentation that cannot be directly edited + - To enhance API docs, add JSDoc comments to the relevant code in package files + - Document entities, modules, parameters, and other code elements at their source + +- Essential information for the packages are: how to install, how to use, what it's doing and how is it helpful +- Each entity from the packages should have it's Purpose section + +## API Documentation + +- Document all public APIs with consistent formatting +- Include parameter descriptions, types, and default values +- Provide return value information +- Add examples showing typical usage +- Document possible errors and edge cases +- Use JSDoc or similar standards for code documentation +- Keep method documentation close to its implementation + +## README Files + +- Include a clear project description +- Document installation and setup instructions +- Provide getting started examples +- List prerequisites and dependencies +- Include troubleshooting sections for common issues +- Add references to additional documentation +- Maintain contributing guidelines and code of conduct + +## Documentation Organization + +- Create a logical hierarchy of documentation +- Implement proper navigation between related documents +- Include a search feature if documentation is extensive +- Create an index or table of contents for larger docs +- Group related documentation together +- Use consistent filenames and folder structure +- Link to related documentation when appropriate - they always start with "/docs" and should lead to the .md or .mdx + file to keep the versioning abilities + +## Interactive Documentation + +- Implement collapsible sections for complex documentation +- Use tabs to organize related but separate content +- Add interactive diagrams to explain complex workflows - could be with mermaid or other +- Include working demos of components and features +- Enable dark/light mode toggle for documentation +- Consider implementing interactive tutorials for key features + +## Accessibility + +- Use descriptive link text (avoid "click here") +- Add alt text to all images +- Create scannable content with proper headings +- Write in plain language, avoiding jargon when possible +- Define acronyms and technical terms +- Ensure documentation is responsive for mobile devices + +## Code Examples + +- Provide complete, working code examples +- Avoid large code example blocks or mixing multiple examples in a single snippet +- Use syntax highlighting with proper language tags +- Make examples copy-pastable when possible +- Include comments to explain complex parts +- Show both basic and advanced use cases +- Update examples when the API changes +- Test all code examples to ensure they work +- Ensure examples are highly accessible by using appropriate syntax highlighting, clear formatting, code diffs and + sufficient color contrast for improved readability + +- **Keep examples brief and focused** - Each example should demonstrate a single concept or feature rather than trying + to showcase multiple capabilities at once +- **Base examples on actual code** - All code examples must be based on the actual implementation in the `/packages` + directory +- **Do not create hypothetical examples** - Don't create examples that haven't been verified against the actual codebase +- **Reference existing implementations** - If unsure how something works, examine the source code in the packages + directory first +- **Request clarification when needed** - If you need more information about implementation details, ask for specific + paths or reference code +- **Test all examples** - Verify that code examples actually work with the current library version before including them +- **Include imports** - Always show the necessary imports for the examples to work + +### Code Example Accuracy Requirements + +- **Never hallucinate code examples** - All examples must be based on real, working code from the `/packages` directory +- **Examine the source code first** - Before writing an example, thoroughly review the actual implementation in the + codebase, search for the tests which can give you better idea on how something works +- **Reproduce real-world usage patterns** - Follow the patterns and conventions used in the actual codebase +- **Respect types and signatures** - Ensure all parameter types and return values match the actual implementation +- **When in doubt, ask** - If you're unsure how a feature works, ask for specific paths to reference code instead of + guessing +- **Document limitations** - Be clear about any constraints or edge cases in the implementation +- **Indicate version compatibility** - Note which versions of the library the example works with + +### Example Verification Process + +1. Locate the relevant code in the `/packages` directory, each have `__tests__` directory with many examples of the code +2. Understand the implementation and API surface +3. Create a minimal working example that demonstrates the feature +4. Test the example to ensure it works as expected +5. Document any dependencies or prerequisites +6. Include complete import statements and configuration +7. Add explanatory comments for complex parts +8. Verify the example against the latest version of the library + +### Code Highlighting in Examples + +To emphasize specific lines or sections in code examples, use the following comment markers: + +```js +// highlight-start +// ... code to highlight ... +// highlight-end + +// highlight-next-line +const important = true; +``` + +- Place `// highlight-start` before the code you want to highlight and `// highlight-end` after. +- Use `// highlight-next-line` to highlight only the line immediately following the comment. +- This works for JavaScript, TypeScript, and other languages that support `//` comments. For other languages, use the + appropriate comment syntax (e.g., `#` for Python). +- Use highlighting sparingly to draw attention to the most important parts of an example, such as key logic, + configuration, or changes. +- Do not use highlighting for entire code blocks unless every line is essential. +- Always ensure highlighted code is accurate and up-to-date with the actual implementation. + +### Red Highlighting for Errors + +To visually indicate lines that will cause an error (red highlight), use the following comment: + +```js +// error-next-line +throw new Error(1); +// Throws the Error(1) + +// error-start +function error() { + throw new Error(1); +} +// error-end +// Throws the Error(1) +``` + +- Place `// error-...` directly above the line that should be highlighted in red as an error. +- Use this only for lines that are expected to throw or demonstrate incorrect usage. +- This helps readers quickly identify problematic code in examples. +- Leave helpful comments with error details under it + +### Showing Code Diffs in Examples + +To illustrate code changes (diffs) in examples, use the following comment markers: + +```js +// diff-add-next-line +const newValue = 2; + +// diff-remove-next-line +const oldValue = 1; + +// diff-add-start +const foo = 1; +const bar = 2; +// diff-add-end + +// diff-remove-start +const oldFoo = 0; +const oldBar = 0; +// diff-remove-end +``` + +- Use `// diff-remove-...` to indicate a line that should be shown as removed in a diff. +- Use `// diff-add-...` to indicate a line that should be shown as added in a diff. +- Place the comments directly above the relevant lines. +- This helps readers quickly understand what has changed between code versions or in upgrade guides. +- Only use these markers when demonstrating code changes, migrations, or upgrade steps. + +### Creating Live Code Examples + +To provide interactive, live-editable code examples in the documentation, use the `live` option in your code block: + +```tsx live +function Example() { + return
Hello, world!
; +} +``` + +```ts live +console.log(123); +``` + +- Add `live` after the language identifier (e.g., `{lang} live`) to enable live editing and preview. +- Use this for React or TypeScript code that benefits from interactive demonstration. +- Keep live examples minimal and focused on a single concept. +- Ensure all live examples are functional and error-free. +- Only use live examples where interactivity adds value for the reader. diff --git a/.cursor/rules/hyper-fetch-react.mdc b/.cursor/rules/hyper-fetch-react.mdc new file mode 100644 index 000000000..e70f873ee --- /dev/null +++ b/.cursor/rules/hyper-fetch-react.mdc @@ -0,0 +1,324 @@ +--- +description: +globs: *.tsx +alwaysApply: false +--- + +# Hyper Fetch - Best Practices for React + +This document outlines the best practices for using hyper-fetch in React applications, covering various aspects such as +code organization, performance considerations, security, and testing. Following these guidelines ensures consistency, +maintainability, and optimal usage of HyperFetch's features. + +## 1. Code Organization and Structure + +### Directory Structure Best Practices + +- **Feature-based Organization:** Group hyper-fetch requests and related components within feature-specific directories. + This improves modularity and maintainability. + +``` + src/ + ├── features/ + │ ├── users/ + │ │ ├── components/ + │ │ │ ├── user-list.tsx + │ │ │ └── user-details.tsx + │ │ ├── api/ + │ │ │ └── users-api.ts + │ │ └── types/ + │ │ └── user.ts + │ ├── products/ + │ └── ... + ├── ... + +``` + +- **Dedicated API Service Layer:** Abstract API interaction logic into separate modules. This allows for easier testing + and decoupling of components from specific API implementations. Consider using a dedicated `api` directory within each + feature. + +### File Naming Conventions + +- **Consistent Naming:** Follow a consistent naming convention for hyper-fetch hooks. Prefix requests with method like + `get` | `create` | `update` | `delete` and postfix with `Request` to clearly indicate their purpose (e.g., + `getUserRequest`, `createNoteRequest`). + +- **Descriptive Names:** Use descriptive names for files and variables to improve code readability. For example, + `getUsersRequest.ts` is more informative than `usersRequest.ts`. + +--- + +--- + +## 2. Common Patterns and Anti-patterns + +### 2.1 Design Patterns Specific to hyper-fetch + +- **Custom Hooks for Data Fetching:** As highlighted earlier, encapsulating hyper-fetch logic within custom hooks. This + promotes reusability and separation of concerns. These hooks will typically return the result of a `useQuery` or + `useMutation` call. + +- **Optimistic Updates:** Implement optimistic updates to improve perceived performance. This involves updating the UI + before the API request completes, and then reverting the changes if the request fails. hyper-fetch provides utilities + like `onMutate` to handle this. + +- **Pessimistic Updates:** Update the UI only after a successful response from the API. Simpler to implement but + provides a less snappy user experience. + +### 2.2 Recommended Approaches for Common Tasks + +- **Prefetching Data:** Prefetch data for routes or components that the user is likely to visit next. This can + significantly improve the user experience. Use `request.send()`. + +- **Dependent Queries:** Fetch data based on the result of a previous query. Use the `enabled` option in `useQuery` to + conditionally execute queries. + + ```typescript + const { data: user } = useFetch(getUser); + const { data: posts } = useFetch(getPosts.setParams({ userId: user?.id }), { disabled: !user }); + ``` + +### 2.3 Anti-patterns and Code Smells to Avoid + +- **Directly Calling API in Components:** Avoid making API calls directly within components. This makes testing + difficult and tightly couples components to specific API implementations. + +- **Ignoring Error Handling:** Always handle errors properly. Display user-friendly error messages and provide options + for retrying requests. + +- **Over-fetching Data:** Fetch only the data that is required by the component. Use GraphQL or API query parameters to + reduce the amount of data transferred. + +- **Deeply Nested Queries:** Avoid deeply nesting queries, as this can lead to performance issues and make the code + difficult to understand. Consider combining queries or using a different approach. + +### 2.4 State Management Best Practices + +- **Local vs. Global State:** Determine whether data should be stored in local component state or in a global state + management solution. Use local state for component-specific data and global state for data that needs to be shared + across multiple components. + +- **hyper-fetch as a State Manager:** Leverage hyper-fetch's built-in caching and state management capabilities. Avoid + using external state management libraries for data that is already managed by hyper-fetch. + +### 2.5 Error Handling Patterns + +- **Centralized Error Handling:** Implement centralized error handling to provide consistent error messages and logging. + +- **Retry Logic:** Implement retry logic to automatically retry failed requests. hyper-fetch provides options for + configuring retry behavior. + +- **Error Boundaries:** Use Error Boundaries to catch errors that occur during rendering. This prevents the entire + application from crashing. + +## 3. Performance Considerations + +### 3.1 Optimization Techniques - Hooks + +- **Query Invalidation:** Invalidate queries when data changes. This ensures that the UI is always up-to-date. + +- **Stale-While-Revalidate:** Use the `staleTime` and `cacheTime` options to configure how long data should be + considered fresh. `Stale While Revalidate` allows the UI to display cached data while fetching fresh data in the + background. This can be passed to the hook like: `useFetch(request, { revalidate: true })` + +- **Window Focus Refetching:** Configure refetching on window focus to keep data fresh when the user switches back to + the application. + +- **Polling/Refetch Intervals:** Use `refreshTime` to periodically refetch data. This is useful for data that changes + frequently. + +### 3.2 Memory Management + +- **Query Cache Management:** Understand how hyper-fetch manages its cache. Configure the `staleTime` option to control + how long data is stored in the cache. + +- **Garbage Collection:** Ensure that unused queries are garbage collected properly. Use the `cacheTime` option to + configure how long inactive queries should be kept in the cache. + +### 3.3 Rendering Optimization + +- **Memoization:** Use `React.memo` to prevent unnecessary re-renders of components. This is especially important for + components that display data fetched from hyper-fetch. + +- **Virtualization:** Use virtualization techniques (e.g., `react-window`, `react-virtualized`) to efficiently render + large lists of data. + +### 3.4 Bundle Size Optimization + +- **Tree Shaking:** Ensure that your build process is configured for tree shaking. This removes unused code from the + final bundle. + +- **Code Splitting:** As mentioned earlier, use code splitting to reduce the initial load time. + +### 3.5 Lazy Loading Strategies + +- **Lazy Load Components:** Use `React.lazy` to lazy load components that are not immediately needed. + +- **Lazy Load Data:** Fetch data only when it is needed. Use dependent queries to fetch data based on user interactions. + +## 4. Security Best Practices + +### Common Vulnerabilities and How to Prevent Them + +- **Cross-Site Scripting (XSS):** Sanitize user input to prevent XSS attacks. Use a library like DOMPurify to sanitize + HTML. + +- **Cross-Site Request Forgery (CSRF):** Implement CSRF protection to prevent attackers from performing actions on + behalf of the user. Use a library or framework that provides CSRF protection. + +- **Injection Attacks:** Protect against injection attacks by validating user input and using parameterized queries. + +### Input Validation + +- **Client-Side Validation:** Implement client-side validation to provide immediate feedback to the user. + +- **Server-Side Validation:** Always validate user input on the server-side to prevent malicious data from being stored + in the database. + +### Authentication and Authorization Patterns + +- **JSON Web Tokens (JWT):** Use JWTs for authentication. Store the JWT in a secure cookie or in local storage (with + caution). Use `httpOnly` flag on cookies containing JWTs when possible to prevent client-side script access. + +- **Role-Based Access Control (RBAC):** Implement RBAC to control access to different parts of the application. Use + middleware or custom hooks to check user roles. + +### Data Protection Strategies + +- **Encryption:** Encrypt sensitive data at rest and in transit. Use HTTPS to encrypt data in transit. Encrypt sensitive + data in the database. + +- **Data Masking:** Mask sensitive data in logs and reports. This prevents sensitive data from being exposed to + unauthorized users. + +### Secure API Communication + +- **HTTPS:** Use HTTPS for all API communication. This encrypts data in transit and prevents eavesdropping. + +- **API Rate Limiting:** Implement API rate limiting to prevent abuse. + +- **CORS:** Configure CORS properly to prevent cross-origin requests from unauthorized domains. + +## 5. Testing Approaches + +### Unit Testing Strategies + +- **Test Custom Hooks:** Unit test custom hyper-fetch hooks to ensure they are working correctly. Mock the API calls + using libraries like `msw` (Mock Service Worker). + +- **Test Components in Isolation:** Unit test components in isolation to ensure they render correctly and handle user + interactions properly. Use libraries like `react-testing-library`. + +### Integration Testing + +- **Test Data Flow:** Integration test the data flow between components and APIs. Verify that data is fetched correctly + and displayed properly. + +- **Test Error Handling:** Integration test error handling scenarios to ensure that errors are handled properly. + +### End-to-End Testing + +- **Simulate User Interactions:** Use end-to-end testing frameworks like Cypress or Playwright to simulate user + interactions and verify that the application is working correctly from the user's perspective. + +- **Test Critical Paths:** Focus on testing critical user flows, such as login, registration, and checkout. + +### Test Organization + +- **Colocate Tests with Components:** Colocate tests with the components they are testing. This makes it easier to find + and maintain tests. + +- **Use Descriptive Test Names:** Use descriptive test names to clearly indicate what each test is verifying. + +### Mocking and Stubbing + +- **Build-in:** Use built in abilities of HyperFetch to mock requests. + +```ts +const request = client.createRequest()({ endpoint: "shared-base-endpoint" }); + +// Set mock is the only method that modify reference +request.setMock( + () => { + // Data part + return { + data: { mocking: "is fun" }, + status: 200, + }; + }, + { + // Config part + requestTime: 40, + responseTime: 60, + totalUploaded: 1000, + totalDownloaded: 1000, + }, +); +``` + +- **Mock API Calls:** Use mocking libraries like `msw` to mock API calls during testing. This allows you to test + components in isolation without relying on a real API. + +- **Stub External Dependencies:** Stub external dependencies to isolate components and make tests more predictable. + +## 6. Common Pitfalls and Gotchas + +- **Forgetting to Invalidate Queries:** Failing to invalidate queries when data changes can lead to stale data being + displayed in the UI. + +- **Incorrect Cache Configuration:** Incorrectly configuring the `cacheTime` and `staleTime` options can lead to + performance issues or stale data. + +- **Not Handling Errors Properly:** Not handling errors properly can lead to unexpected behavior and a poor user + experience. + +- **Over-relying on Default Configuration:** Customizing hyper-fetch to match specific needs is essential. + +- **Ignoring Devtools:** The hyper-fetch devtools are invaluable for debugging and understanding what is happening under + the hood. + +## 7. Tooling and Environment + +### 7.1 Recommended Development Tools + +- **VS Code:** Use VS Code with extensions like ESLint, Prettier, and TypeScript to improve developer productivity. + +- **React Developer Tools:** Use the React Developer Tools browser extension to inspect React components and state. + +- **Hyper Fetch Devtools - HyperFlow:** Use the HyperFlow Devtools to inspect the hyper-fetch cache, network and track + query and mutation status. + +### 7.2 Build Configuration + +- **Webpack or Parcel:** Use a bundler like Webpack or Parcel to bundle your code for production. Configure the bundler + for tree shaking and code splitting. + +- **Babel:** Use Babel to transpile your code to older versions of JavaScript. This ensures that your code is compatible + with older browsers. + +### 7.3 Linting and Formatting + +- **ESLint:** Use ESLint to enforce coding standards and prevent errors. Install dedicated linter package to avoid + common typescript issues. `@hyper-fetch/eslint` + +- **Prettier:** Use Prettier to automatically format your code. This ensures that your code is consistently formatted + and easy to read. + +### 7.4 Deployment Best Practices + +- **CDN:** Use a CDN to serve static assets. This improves performance and reduces the load on your server. + +- **Caching:** Configure caching properly on your server and CDN. This reduces the number of requests to your server and + improves performance. + +### 7.5 CI/CD Integration + +- **Automated Testing:** Integrate automated testing into your CI/CD pipeline. This ensures that your code is tested + automatically before it is deployed. + +- **Automated Deployment:** Automate the deployment process to reduce the risk of errors and improve efficiency. + +``` + +``` diff --git a/.cursor/rules/hyper-fetch.mdc b/.cursor/rules/hyper-fetch.mdc new file mode 100644 index 000000000..386ab00cf --- /dev/null +++ b/.cursor/rules/hyper-fetch.mdc @@ -0,0 +1,277 @@ +--- +description: +globs: +alwaysApply: true +--- + +# Hyper Fetch Best Practices + +This document outlines the best practices for using hyper-fetch in React applications, covering various aspects such as +code organization, performance considerations, security, and testing. Following these guidelines ensures consistency, +maintainability, and optimal usage of HyperFetch's features. + +## Core Concepts + +HyperFetch is a powerful data-exchange framework designed for type-safety and ease of use. The main components you will +interact with are: + +- **`Client`**: The central point of configuration for all requests. It holds URL of server, all modules like Cache, + Dispatchers, it is builder of Requests (`createRequest()({...})`) +- **`Request`**: A class that defines a specific API endpoint, including its method, parameters, and data transformation + logic - everything typesafe (response, payload, params, query params, endpoint string etc) + +- **`useFetch` (React)**: A React hook that executes a request and provides the state of the request (data, error, + loading). +- **`useSubmit` (React)**: A React hook that executes a request on call (for example button click) and provides the + state of the request (data, error, loading). + +- **`Adapters`**: Pluggable modules that handle the actual data fetching logic (e.g., `axios`, `graphql`, `firebase`). + +## 1. Client Configuration + +The `Client` instance should be created and configured once, then exported for use throughout the application. + +Use the `createClient` from `@hyper-fetch/core`. + +### Best Practices + +- **Singleton Client**: Create a single `Client` instance for your application to ensure consistent configuration. +- **Base URL**: Always configure the `url` on the client. Avoid hardcoding base URLs in individual requests. +- **Feature Configuration**: Configure features like `cache`, `queue`, and `storage` at the client level. + +#### Example: + +```typescript +import { createClient } from "@hyper-fetch/core"; + +export const client = createClient({ + url: "https://api.example.com", +}); +``` + +## 2. Request Definition + +Requests should be defined in a structured and reusable way. + +### Best Practices + +- **Typed Requests**: Always define the types for request parameters, response data, and errors. This is the core + strength of HyperFetch. +- **Request Factory**: Use functions to create requests. This allows for dynamic parameters and keeps the code clean. +- **Colocation**: Keep requests related to a specific feature or data model grouped together. +- **Error Handling**: Define error types for requests to handle API errors gracefully. +- **Data Transformation**: Use `setResponseMapper` to transform the raw API response into the desired data structure. + +#### Examples: + +##### All Generic Types + +```ts +const request = client.createRequest<{ + // what we get in response + response: SomeData; + // What we send to backend + payload: SomePayload; + // Adds ability to specify query params + queryParams?: { search?: string }; + // It will extend global error and add ability to specify errors for particular endpoint only + error: ZodValidationErrors; +}>()({ + endpoint: "/some-endpoint", + method: "GET", +}); +``` + +##### example of static params passed with method + +```ts +import { client } from "./client"; // Import the configured client + +interface User { + id: number; + name: string; +} + +const getUser = client.createRequest<{ response: User }>()({ + endpoint: "/users/:userId", + method: "GET", +}); + +// Using the request with the client +const { data, error, success, extra, responseTime, requestTime } = getUser + .setParams({ userId: 1 }) // taken from endpoint string `:userId` + .send(); +``` + +##### dynamicly pass the params to send({ }) and use all of the callbacks + +```ts +import { client } from "./client"; // Import the configured client + +interface User { + id: number; + name: string; +} + +const getUser = client.createRequest<{ response: User }>()({ + endpoint: "/users/:userId", + method: "GET", +}); + +// Using the request with the client +const { data, error, success, extra, responseTime, requestTime } = getUser.send({ + // pass params dynamically + params: { userId: 1 }, + + onBeforeSent: ({ response, requestId }) => { + console.log(`Request ${requestId} has settled. Final response:`, response); + // Called right before the request is sent—useful for quickly accessing the requestId for tracking or logging + }, + onStart: ({ requestId, request }) => { + console.log(`Request ${requestId} has started.`); + // Called when the request is started—useful for showing a loading indicator + }, + onUploadProgress: ({ progress, timeLeft, sizeLeft, total, loaded, startTimestamp, request, requestId }) => { + console.log(`Request ${requestId} upload progress:`, progress); + // Called when the upload progress is updated—useful for showing a progress bar + }, + onDownloadProgress: ({ progress, timeLeft, sizeLeft, total, loaded, startTimestamp, request, requestId }) => { + console.log(`Request ${requestId} download progress:`, progress); + // Called when the download progress is updated—useful for showing a progress bar + }, + onResponse: ({ response, requestId }) => { + console.log(`Request ${requestId} finished with data:`, response.data); + // Called when the request succeeds—useful for processing successful data + }, + onRemove: ({ request, requestId }) => { + console.log(`Request ${requestId} removed.`); + // Called when the request is removed—useful for cleaning up + }, +}); +``` + +## 3. Component Usage (React) + +When using HyperFetch with React, follow these guidelines for the `useFetch` hook. + +### Best Practices + +- **Hook Usage**: Use the `useFetch` hook to bind requests to your components' lifecycle. +- **State Management**: Rely on the `data`, `error`, and `loading` states returned by `useFetch`. Avoid creating extra + state variables to track this. +- **Dependencies**: Use the `dependencies` array in `useFetch` to automatically re-fetch data when component props or + other state changes. +- **Manual Fetching**: The `revalidate` function returned by `useFetch` can be used to manually trigger a re-fetch. + +#### Example: + +```tsx +import { useFetch } from "@hyper-fetch/react"; +import { client } from "./client"; +import { getUser } from "./requests"; + +function UserProfile({ userId }) { + const { data, loading, error, extra, success, responseTime, requestTime, onSuccess, onError, onFinished } = useFetch( + client.request(getUser).setParams({ userId }), + ); + + onSuccess(({ response, request }) => { + console.log(response, request); + }); + + onError(({ response, request }) => { + console.log(response, request); + }); + + onFinished(({ response, request }) => { + console.log(response, request); + }); + + if (loading) return

Loading...

; + if (error) return

Error loading user!

; + if (!data) return null; + + return

{data.name}

; +} +``` + +```tsx +import { useFetch } from "@hyper-fetch/react"; +import { client } from "./client"; +import { createUser } from "./requests"; + +function CreateUser({ userId }) { + const { + submit, + data, + submitting, + error, + extra, + success, + responseTime, + requestTime, + onSubmitSuccess, + onSubmitError, + onSubmitFinished, + } = useSubmit(client.request(createUser).setParams({ userId })); + + onSubmitSuccess(({ response, request }) => { + console.log(response, request); + }); + + onSubmitError(({ response, request }) => { + console.log(response, request); + }); + + onSubmitFinished(({ response, request }) => { + console.log(response, request); + }); + + + return - - - ); -}; -``` - ---- - -## Passing data and params - -Data and parameters can be passed in several ways. One option is to use methods on the -[`Request`](/documentation/02-core/request.mdx), such as `setData` or `setParams`. - -```tsx -const { submit } = useSubmit(patchUser.setParams({ userId: 1 }).setData({ name: "New Name" })); -``` - -However, you may need to pass parameters dynamically, which requires using `submit` function options. - -```tsx -const { submit } = useSubmit(patchUser); - -const handleSubmit = (id: number, name: string) => { - submit({ data: { name }, params: { userId: id }, queryParams: { search: "test" } }); -}; -``` - ---- - -## Options - -These configuration options should be provided as a second parameter: - -```tsx -const { ... } = useSubmit(request, options) -``` - -{@import React UseSubmitOptionsType returns} - ---- - -## Returns - -Returned values from this hook: - -```tsx -const values = useSubmit(request); -``` - -{@import React useSubmit returns} diff --git a/documentation/docs/documentation/04-react/03-sockets/use-emitter.mdx b/documentation/docs/documentation/04-react/03-sockets/use-emitter.mdx deleted file mode 100644 index 6db05daae..000000000 --- a/documentation/docs/documentation/04-react/03-sockets/use-emitter.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -sidebar_position: 4 -title: Sockets emitter -sidebar_label: useEmitter ---- - -# useEmitter - - - ---- - -## Introduction - -This hook **send events** to the server. The minimum requirement for `useEmitter` is a prepared -[`Emitter`](/documentation/03-sockets/emitter.mdx). - -If you intend to `listen` to events from the server, we recommend choosing the -[`useListener`](/documentation/04-react/03-sockets/use-listener.mdx) hook. - ---- - -## Initialization - -```tsx -const { emit, timestamp, connected, onEvent, onError, onReconnecting } = useEmitter(postLogin); -``` - ---- - -## How it works? - -**`useEmitter`** executes a Emitter when a `emit()` function returned from it gets triggered. It uses dependency -tracking to limit re-rendering and improve performance. Under the hood, communication with the core systems is -established by event emitters. Many `"helper hooks"` (such as `onEvent`, `onError`, `onReconnecting`, etc.) are -returned; these will help handle the request flow and lifecycle. This approach avoids overloading the base hook with -callback logic. It also helps improve code readability, decreases code complication, and promotes more organized code. - -```tsx -import { socketInstance } from "./socket"; - -export const emitMessage = socketInstance.createEmitter()({ - endpoint: "chat-message", // endpoint of the event -}); -``` - -#### Use it in your component. - -```tsx -import { useEmitter } from "@hyper-fetch/react"; -import { emitMessage } from "./api"; - -const MessageComponent: React.FC = () => { - const { emit, timestamp, connected, onEvent, onError, onReconnecting } = useEmitter(postLogin); - - onEvent((emitter) => { - // Event before we send event message - console.log(emitter); // Emitter instance - }); - - onError((error) => { - console.log(error); // Error Event - }); - - onReconnecting((reconnectingAttempt) => { - console.log(reconnectingAttempt); // 1 - }); - - const onSubmit = (values: Values) => { - // ResponseDataType is automatically inherited from Emitter class - const acknowledge = (error: Error, data: ResponseDataType) => { - if (error) { - alert("No server response!"); - } else { - alert("Message received on server."); - } - }; - - emit({ data: values }, acknowledge); - }; - - return ( - -
- - - -
- ); -}; -``` - ---- - -## Passing data and params - -Data and parameters can be passed in several ways. One option is to use `setData` method on the -[`Emitter`](/documentation/03-sockets/emitter.mdx). - -```tsx -const { emit } = useEmitter(emitMessage.setData({ message: "New message" })); -``` - -However, you may need to pass parameters dynamically, which requires using `emit` function options. - -```tsx -const { emit } = useEmitter(emitMessage); - -const handleSubmit = (id: number, name: string) => { - // ResponseDataType is automatically inherited from Emitter class - emit({ data: { name } }, (error: Error, data: ResponseDataType) => { - if (error) { - alert("No server response!"); - } else { - alert("Message received on server."); - } - }); -}; -``` - ---- - -## Options - -These configuration options should be provided as a second parameter: - -```tsx -const { ... } = useEmitter(emitter, options) -``` - -{@import React UseEmitterOptionsType returns} - ---- - -## Returns - -Returned values from this hook: - -```tsx -const values = useEmitter(emitter); -``` - -{@import React useEmitter returns} diff --git a/documentation/docs/documentation/04-react/03-sockets/use-event-messages.mdx b/documentation/docs/documentation/04-react/03-sockets/use-event-messages.mdx deleted file mode 100644 index 1c4adbcad..000000000 --- a/documentation/docs/documentation/04-react/03-sockets/use-event-messages.mdx +++ /dev/null @@ -1,105 +0,0 @@ ---- -sidebar_position: 3 -title: Sockets event messages -sidebar_label: useEventMessages ---- - -# useEventMessages - - - ---- - -## Introduction - -This hook is created to **listen to ALL events** received from the server. - -If you intend to `send` events to the server, we recommend choosing the -[`useEmitter`](/documentation/04-react/03-sockets/use-emitter.mdx) hook. - ---- - -## Initialization - -```tsx -const { data, timestamp, connected, connecting, onEvent, onError, onReconnecting } = useEventMessages(onChatMessage); -``` - ---- - -## How it works? - -**`useEventMessages`** hooks into Socket adapter and start listening to given event when a component is mounted. It uses -dependency tracking to limit re-rendering and help with performance. - -Under the hood, communication with the core systems is established by event emitters. There are many `"helper hooks"` -that get returned from the hook, like `onEvent`, `onError`, and `onReconnecting` (among others). They will help you -handle various events in the lifecycle of sockets communication. - -We used this approach to avoid overloading the base hook with callback logic, which causes low code readability and -increases complexity. - -```tsx -import { useEventMessages } from "@hyper-fetch/react"; -import { onChatMessage } from "server"; - -const UsersListPage: React.FC = () => { - const [messages, setMessages] = useState([]) - const { data, connected, connecting, onEvent, onError, onReconnecting } = useEventMessages(onChatMessage); - - onEvent(({ data: message }) => { - setMessages((prev) => [...prev, message]); // [ MessageType, MessageType, MessageType ] - }); - - onError((error) => { - console.log(error); // Error Event - }); - - onReconnecting((reconnectingAttempt) => { - console.log(reconnectingAttempt); // 1 - }) - - if(error && !connecting) { - return {error.message} - } - - return ( -
- {connecting && } - {!messages.length &&
No messages
} - {!messages.length &&
{messages.map(message =>
{message}
)}
} -
- ); -}; -``` - ---- - -## Options - -Configuration options for `useEventMessages` must be provided as the second parameter. - -```tsx -const { ... } = useEventMessages(listener, options) -``` - -{@import React UseEventMessagesOptionsType returns} - ---- - -## Returns - -Returned values from this hook. - -```tsx -const values = useEventMessages(listener); -``` - -{@import React useEventMessages returns} diff --git a/documentation/docs/documentation/04-react/03-sockets/use-listener.mdx b/documentation/docs/documentation/04-react/03-sockets/use-listener.mdx deleted file mode 100644 index 5a0aad041..000000000 --- a/documentation/docs/documentation/04-react/03-sockets/use-listener.mdx +++ /dev/null @@ -1,115 +0,0 @@ ---- -sidebar_position: 3 -title: Sockets listener -sidebar_label: useListener ---- - -# useListener - - - ---- - -## Introduction - -This hook is created to **listen to events** received from the server. - -If you intend to `send` events to the server, we recommend choosing the -[`useEmitter`](/documentation/04-react/03-sockets/use-emitter.mdx) hook. - ---- - -## Initialization - -```tsx -const { data, timestamp, connected, connecting, onEvent, onError, onReconnecting } = useListener(onChatMessage); -``` - ---- - -## How it works? - -**`useListener`** hooks into Socket adapter and start listening to given event when a component is mounted. It uses -dependency tracking to limit re-rendering and help with performance. - -Under the hood, communication with the core systems is established by event emitters. There are many `"helper hooks"` -that get returned from the hook, like `onEvent`, `onError`, and `onReconnecting` (among others). They will help you -handle various events in the lifecycle of sockets communication. - -We used this approach to avoid overloading the base hook with callback logic, which causes low code readability and -increases complexity. - -```tsx -import { socketInstance } from "./socket"; - -export const onChatMessage = socketInstance.createListener()({ - endpoint: "chat-message", // endpoint of the event -}); -``` - -#### Use it in your component. - -```tsx -import { useListener } from "@hyper-fetch/react"; -import { onChatMessage } from "server"; - -const UsersListPage: React.FC = () => { - const [messages, setMessages] = useState([]) - const { data, connected, connecting, onEvent, onError, onReconnecting } = useListener(onChatMessage); - - onEvent(({ data: message }) => { - setMessages((prev) => [...prev, message]); // [ MessageType, MessageType, MessageType ] - }); - - onError((error) => { - console.log(error); // Error Event - }); - - onReconnecting((reconnectingAttempt) => { - console.log(reconnectingAttempt); // 1 - }) - - if(error && !connecting) { - return {error.message} - } - - return ( -
- {connecting && } - {!messages.length &&
No messages
} - {!messages.length &&
{messages.map(message =>
{message}
)}
} -
- ); -}; -``` - ---- - -## Options - -Configuration options for `useListener` must be provided as the second parameter. - -```tsx -const { ... } = useListener(listener, options) -``` - -{@import React UseListenerOptionsType returns} - ---- - -## Returns - -Returned values from this hook. - -```tsx -const values = useListener(listener); -``` - -{@import React useListener returns} diff --git a/documentation/docs/documentation/05-adapters/01-overview.mdx b/documentation/docs/documentation/05-adapters/01-overview.mdx deleted file mode 100644 index 66e837e4c..000000000 --- a/documentation/docs/documentation/05-adapters/01-overview.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -sidebar_position: 1 -title: Adapters -sidebar_label: Overview ---- - ---- - -## About - -Adapter is a package wrapping another existing package or framework, enhancing it with all the HF features, and -enhancing HF with all the possibilities of the adapted package. Writing an adapter is easy as HF allows for easy extension of `BaseAdapterType` and -our prepared set of methods called *adapter bindings*. diff --git a/documentation/docs/documentation/05-adapters/_category_.json b/documentation/docs/documentation/05-adapters/_category_.json deleted file mode 100644 index 8f184fda8..000000000 --- a/documentation/docs/documentation/05-adapters/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Adapters" -} diff --git a/documentation/docs/documentation/05-adapters/axios/01-introduction.mdx b/documentation/docs/documentation/05-adapters/axios/01-introduction.mdx deleted file mode 100644 index 6d78fc81e..000000000 --- a/documentation/docs/documentation/05-adapters/axios/01-introduction.mdx +++ /dev/null @@ -1,36 +0,0 @@ ---- -sidebar_position: 1 -title: Axios introduction -sidebar_label: Introduction ---- - -Hyper Fetch `axios` adapter is a simple integration that allows Hyper Fetch users to utilize a lot of features -of the `axios` library along with all the features available in Hyper Fetch. - -## Getting Started - -In order to set `axios` adapter, you have to import it from the `@hyper-fetch/axios` package: - -```tsx -import { Client } from "@hyper-fetch/core"; -import { axiosAdapter } from "@hyper-fetch/axios"; - -const client = new Client({ url: "base-url" }).setAdapter(axiosAdapter); -``` - -...and voila! It's done. Now you can set all the axios options, either during the request initialization or via -`setOptions` method: - -```tsx -const client = new Client({ url: "base-url" }).setAdapter(axiosAdapter); -const request = client.createRequest()({ - endpoint: "/shared-endpoint", - options: {} // here -}); - -const requestWithOptions = request.setOptions({}) // or here -``` - -## Differences - -1. You should not pass the `baseUrl`, `method`, and `url` in the axios options. They will be overwritten by the Hyper Fetch adapter. diff --git a/documentation/docs/documentation/05-adapters/firebase/01-introduction.mdx b/documentation/docs/documentation/05-adapters/firebase/01-introduction.mdx deleted file mode 100644 index d689a9a92..000000000 --- a/documentation/docs/documentation/05-adapters/firebase/01-introduction.mdx +++ /dev/null @@ -1,91 +0,0 @@ ---- -sidebar_position: 1 -title: Firebase introduction -sidebar_label: Introduction ---- - -Hyper Fetch `firebase` packages offer complete integration of `realtime database` and `firestore`, both for frontend and -backend, with a single, unified approach for all of them. - -## Getting Started - -In order to start using the firebase adapter, you have to initialize the firestore/realtime database and set the correct -adapter on the `client`. - -There are two packages to chose from with two adapters each: - -1. `@hyper-fetch/firebase` - for working with web realtime and firestore databases: - 1. `firebaseAdapter` - for standard REST requests. - 2. `firebaseSocketsAdapter` - for realtime listening. -2. `@hyper-fetch/firebase-admin` - for admin versions of the packages: - 1. `firebaseAdminAdapter` - for standard REST requests. - 2. `firebaseSocketsAdminAdapter` - for realtime listening. - -Both packages have the same unified interface. - -```tsx -import { firebaseAdapter } from "@hyper-fetch/firebase"; -import { initializeApp } from "firebase/app"; -import { getFirestore } from "firebase/firestore"; - -// Firebase firestore database initialization -const app = initializeApp({ - projectId: "demo-test-firestore", -}); -const db = getFirestore(app); - -// Setting up the HyperFetch with a firebase adapter -const client = new Client({ url: "teas/" }).setAdapter(() => firebaseAdapter(db)); -const getReq = client.createRequest()({ - endpoint: "", - method: "getDocs", -}); - -// Checking the results -const { data, status, extra, success, error } = await getReq.send(); -``` - -In case of firebase admin, the only difference is the import: - -```tsx -import { firebaseAdminAdapter } from "@hyper-fetch/firebase-admin"; -... -``` - -Resulting `response` is an object that contains the following properties: - -1. `data` - data returned from the database. Each object returned from the database is enhanced also by the `__key` param that equals id of a given document. -2. `status` - status indicating whether a request ended with `success`, `error` or `emptyResource`. `emptyResource` - occurs when the request to firebase succeeded but no data was returned. -3. `success` - general information if overall request succeeded (`true`) or failed (`false`) -4. `error` - contains error object if the request failed. -5. `extra` - contains additional properties, depending on a method used. For instance, for `getDocs` method - it allows - to access `ref` and `snapshot` from firestore firebase. - -## Differences from 'raw' firebase - -1. If we store an array in firebase, for instance `[a, b, c]`, the query would return the same array. However, if the - array stops being sequential - for instance, if `a` and `b` were deleted, firebase would return and object: {2: 'c', - 4: 'e'}. Hyper Fetch always returns and array. Each object in an array has an additional `__key` property, that indicates the id of a given object. - -## Filtering queries - -In order to standardize the interface across both admin/web firestore/realtime, the filtering and limiting queries is -done via setting the `constraints` queryParam: - -```tsx -import { $limit, $orderBy, $where } from "constraints"; - -const req = client.createRequest()({ - endpoint: "", - method: "getDocs", -}); // or via setQueryParams method -const { data } = await req.send({ - queryParams: { constraints: [$where("type", "==", "Green"), $orderBy("year"), $limit(1)] }, -}); -``` - -User can pass the array of constrains and filters. Please pay attention to the fact that you need to filter via method -wrappers provided via `adapter-firebase` package: `$where`, `$orderBy`, `$limit`, `$startAt`, `$startAfter`, `$endAt`, -`$endAfter`, `$orderByChild`, `$orderByKey`, `$orderByValue`, `$limitToFirst`, `$limitToLast`, `$equalTo`. All of these -methods work exactly the same as their corresponding firebase equivalents. diff --git a/documentation/docs/documentation/05-adapters/firebase/02-firestore.mdx b/documentation/docs/documentation/05-adapters/firebase/02-firestore.mdx deleted file mode 100644 index 1cf1c8e6f..000000000 --- a/documentation/docs/documentation/05-adapters/firebase/02-firestore.mdx +++ /dev/null @@ -1,105 +0,0 @@ ---- -sidebar_position: 2 -title: Working with Firestore -sidebar_label: Firestore ---- - -## Available methods - -After setting the firestore adapter, we can start performing requests! We should select the appropriate method, corresponding with -firebase methods. If you want to learn more about available methods - please refer to the firebase documentation. - -Due to its nature, we've solved the realtime listening a bit differently, and thus - this method is not allowed in a standard adapter. -For the `onSnapshot`-like usage, please check how to **[listen to queries](/docs/documentation/adapters/firebase/realtime-queries)**. - -```tsx -const getReq = client.createRequest()({ - endpoint: "", - method: "getDocs", // "addDoc" | "getDoc" | "getDocs" | "setDoc" | "updateDoc" | "deleteDoc" -}); - -``` - -### getDocs - -```tsx -const getReq = client.createRequest()({ - endpoint: "", - method: "getDocs", -}); - -const { data, status, extra, success, error } = await req.send(); -// Data is an array of objects, each object also contains the __key param. -``` - -`extra`: -1. `ref` - collection reference endpoint -2. `snapshot` - 'raw' `getDocs` firestore collection/query reference snapshot - -### getDoc - -```tsx - const req = client - .createRequest()({ - endpoint: ":teaId", - method: "getDoc", - }) - .setParams({ teaId: 1 }); - -const { data, status, extra, success, error } = await req.send(); - // setParams can be also passed in the send() method instead -``` - -`extra`: -1. `ref` - document reference endpoint -2. `snapshot` - 'raw' firestore document reference snapshot - -### setDoc - -```tsx -const newData = { origin: "Poland", type: "Green", year: 2023, name: "Pou Ran Do Cha", amount: 10 } -const setReq = client - .createRequest()({ - endpoint: ":teaId", - method: "setDoc", - // can also pass options: {merge: true} for achieving the firebase 'merge' option - }) - .setParams({ teaId: 1 }) - .setData(newData); - -await setReq.send(); -const { data } = await getReq.send(); -``` - -In case of `setDoc` - data returned is the same data as passed for setting. - -### addDoc - -```tsx -const addDocReq = client - .createRequest()({ - endpoint: "", - method: "addDoc", - options: {}, - }) - .setData(newData); - -await addDocReq.send() -``` - -In case of `addDoc` - data returned is the same as passed data for setting + the `__key` param that is id of a newly created document. - -### deleteDoc - -```tsx -const deleteDocReq = client - .createRequest()({ - endpoint: ":teaId", - method: "deleteDoc", - }) - .setParams({ teaId: 1 }); - -await deleteDocReq -``` - -In case of `deleteDoc` - returned data equals `null` diff --git a/documentation/docs/documentation/05-adapters/firebase/03-realtime.mdx b/documentation/docs/documentation/05-adapters/firebase/03-realtime.mdx deleted file mode 100644 index 2251aa70e..000000000 --- a/documentation/docs/documentation/05-adapters/firebase/03-realtime.mdx +++ /dev/null @@ -1,104 +0,0 @@ ---- -sidebar_position: 3 -title: Working with Realtime database -sidebar_label: Realtime Database ---- - -## Available methods - -Our Realtime Database adapter provides all the methods the original offers. Due to its nature, we've solved the realtime listening a bit differently, and thus - this method is not allowed in standard adapter. -For the `onValue`-like usage, please check how to **[listen to queries](/docs/documentation/adapters/firebase/realtime-queries)**. - -### get - -```tsx -import {firebaseAdapter} from "@hyper-fetch/firebase"; - -const client = new Client({ url: "teas/" }).setAdapter(() => firebaseAdapter(realtimeDbWeb)); - const req = client.createRequest()({ - endpoint: "", - method: "get", - }); - -const { data, status, extra, success, error } = await req.send(); -``` - -`extra`: -1. `ref` - reference to the endpoint -2. `snapshot` - 'raw' snapshot from the result - -### set - -```tsx -const setReq = client - .createRequest()({ - endpoint: ":teaId", - method: "set", - }) - .setParams({ teaId: 1 }) - .setData(newData); - -const { data, status, extra, success, error } = await req.send(); -``` - -In case of `set` - data returned is the same data as passed for setting. - -`extra`: -1. ref - database reference to the path - -You can also **remove data** via set by sending the `{data: null}` object: -```tsx -const setReq = client.createRequest()({ - endpoint: ":teaId", - method: "set", -}) -.setParams({ teaId: 1 }) -.setData({data: null}); -``` - -### push - -```tsx -const pushReq = client - .createRequest()({ - endpoint: "", - method: "push", - options: {}, - }) - .setData(newData); -``` - -In case of `push` - data returned is the same data as passed for setting + the `__key` param that equals id of a newly created document. - -`extra`: -1. ref - database reference to the path -2. key - key of the newly created resource - -### update - -```tsx -const updateReq = client - .createRequest()({ - endpoint: ":teaId", - method: "update", - }) - .setData(newData); -``` - -In case of `update` - data returned is the same data as passed for setting. - -`extra`: -1. ref - database reference to the path - -### remove - -```tsx -const removeReq = client - .createRequest()({ - endpoint: ":teaId", - method: "remove", - }) - .setParams({ teaId: 1 }); -``` - -In case of `remove` - returned data equals `null` diff --git a/documentation/docs/documentation/05-adapters/graphql/01-introduction.mdx b/documentation/docs/documentation/05-adapters/graphql/01-introduction.mdx deleted file mode 100644 index b407e4488..000000000 --- a/documentation/docs/documentation/05-adapters/graphql/01-introduction.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -sidebar_position: 1 -title: Graphql introduction -sidebar_label: Introduction ---- - -Hyper Fetch GraphQL is a powerful adapter designed to seamlessly handle GraphQL requests in both server and browser -environments. Built with efficiency and ease-of-use in mind, to simplify process of integration. - -## Getting Started - -Whether you're developing a server-side GraphQL API or a client-side application that consumes GraphQL data, you can use -Hyper Fetch GraphQL to interact with it. Process of setting up the adapter is very simple and straightforward. - -```tsx -import { graphqlAdapter } from "@hyper-fetch/graphql"; - -// Initialize Client with adapter -const client = new Client({ url: "http://localhost:3000/grahql" }).setAdapter(graphqlAdapter); - -// It's ready to use! -const getUser = client.createRequest()({ - endpoint: gql` - query GetUser { - username { - username - firstName - } - } - `, -}); - -const { data, status, extra, success, error } = await getUser.send(); -``` diff --git a/documentation/docs/documentation/05-adapters/graphql/02-queries.mdx b/documentation/docs/documentation/05-adapters/graphql/02-queries.mdx deleted file mode 100644 index d1cda72f2..000000000 --- a/documentation/docs/documentation/05-adapters/graphql/02-queries.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -sidebar_position: 1 -title: Working with queries -sidebar_label: Queries ---- - -To make queries, follow these simple steps after ensuring you have the necessary prerequisites in place: a query schema -and a client set up with our GraphQL adapter. - -## Getting Started - -```tsx -import { graphqlAdapter } from "@hyper-fetch/graphql"; - -// Initialize Client with adapter -const client = new Client({ url: "http://localhost:3000/grahql" }).setAdapter(graphqlAdapter); - -// It's ready to use! -const getUser = client.createRequest()({ - endpoint: gql` - query GetUser { - username { - username - firstName - } - } - `, -}); - -const { data, status, extra, success, error } = await getUser.send(); -``` - -## Typescript - -Depending on your schema and preferences, you can enhance the query process by incorporating types. - -```tsx -type User = { - username: string; - firstName: string; -}; -type Variables = { - filter: string; -}; - -const getUser = client.createRequest()({ - endpoint: gql` - query GetUser { - username(filter: $filter) { - username - firstName - } - } - `, -}); - -const { data, status, extra, success, error } = await getUser.setData({ filter: "Some filter" }).send(); -``` diff --git a/documentation/docs/documentation/05-adapters/graphql/03-mutations.mdx b/documentation/docs/documentation/05-adapters/graphql/03-mutations.mdx deleted file mode 100644 index 98a7385ee..000000000 --- a/documentation/docs/documentation/05-adapters/graphql/03-mutations.mdx +++ /dev/null @@ -1,40 +0,0 @@ ---- -sidebar_position: 1 -title: Working with mutations -sidebar_label: Mutations ---- - -To make mutations you need a query schema and a client set up with our GraphQL adapter. - -## Getting Started - -```tsx -import { graphqlAdapter } from "@hyper-fetch/graphql"; - -// Initialize Client with adapter -const client = new Client({ url: "http://localhost:3000/grahql" }).setAdapter(graphqlAdapter); - -type Variables = { - username: string; - password: string; -}; - -// It's ready to use! -const login = client.createRequest()({ - endpoint: gql` - mutation Login($username: String!, $password: String!) { - login(username: $username, password: $password) { - username - password - } - } - `, -}); - -const { data, status, extra, success, error } = await getUser - .setData({ - username: "Some username", - password: "Some password", - }) - .send(); -``` diff --git a/documentation/docs/documentation/05-adapters/graphql/04-graphql-tag.mdx b/documentation/docs/documentation/05-adapters/graphql/04-graphql-tag.mdx deleted file mode 100644 index 76fb43a38..000000000 --- a/documentation/docs/documentation/05-adapters/graphql/04-graphql-tag.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -sidebar_position: 2 -title: Using graphql-tag -sidebar_label: Graphql-tag ---- - -## graphql-tag - -You can use tagging libraries, as we use the graphql `print` method to make sure the query is transformed to a string. - -```ts -const getUser = client.createRequest()({ - endpoint: gql` - query GetUser { - username { - username - firstName - } - } - `, -}); - -const { data, status, extra, success, error } = await getUser.send(); -``` diff --git a/documentation/docs/documentation/06-generators/01-overview.mdx b/documentation/docs/documentation/06-generators/01-overview.mdx deleted file mode 100644 index 0162105f1..000000000 --- a/documentation/docs/documentation/06-generators/01-overview.mdx +++ /dev/null @@ -1,11 +0,0 @@ ---- -sidebar_position: 1 -title: Code Generators -sidebar_label: Overview ---- - ---- - -## About - -Code generators are used to facilitate the life of a developer and save development time. diff --git a/documentation/docs/documentation/06-generators/_category_.json b/documentation/docs/documentation/06-generators/_category_.json deleted file mode 100644 index 733e38f3f..000000000 --- a/documentation/docs/documentation/06-generators/_category_.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "label": "Code Generators" -} diff --git a/documentation/docs/documentation/06-generators/openapi/01-introduction.mdx b/documentation/docs/documentation/06-generators/openapi/01-introduction.mdx deleted file mode 100644 index a8861b1b8..000000000 --- a/documentation/docs/documentation/06-generators/openapi/01-introduction.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -sidebar_position: 1 -title: Openapi generator introduction -sidebar_label: Introduction ---- - -:::info - -Openapi Hyper Fetch code generator is in beta. Please let us know in case of any problems. - -::: - -:::info - -Note: Right now this code generator accepts only V3 version of the openapi schema. - -::: - - -## Getting Started - -In order to start using the request generator, you need only one thing: OpenApi V3 JSON schema file or link to it. Then, -you can simply call the package via `npx`: - -```tsx -npx @hyper-fetch/codegen-openapi --schema https://petstore3.swagger.io/api/v3/openapi.json -``` - -This command will generate the `openapi.client.ts` that contains generated Hyper Fetch client, requests, and types: - -```tsx -... -export const client = new Client({ url: "/api/v3" }); - -... -export type UpdatePetRequestBody = Paths.UpdatePet.RequestBody; -export type UpdatePetResponseType = Paths.UpdatePet.Responses.$200; - -export type AddPetRequestBody = Paths.AddPet.RequestBody; -export type AddPetResponseType = Paths.AddPet.Responses.$200; - -... - -export const updatePet = client.createRequest()({ - method: "PUT", - endpoint: "/pet", -}); - -export const addPet = client.createRequest()({ - method: "POST", - endpoint: "/pet", -}); - -``` - -Declared variables with a created request always take their name from camel-cased `operationId`. -The types naming always follows the convention - pascalCased `operationId` + {`ResponseType`/`RequestBody`/`QueryParams`}, e.g.: - -- operationId: `addPet` -- declared variable: `addPet` -- response type: `AddPetResponseType` -- request body: `AddPetRequestBody` - -## Renaming the file - -The command also accepts a second, optional argument: `--name`, that allows to indicate the name of a resulting file: - -```tsx -npx @hyper-fetch/codegen-openapi --schema https://petstore3.swagger.io/api/v3/openapi.json --name hyper-fetch.requests.ts -``` - -## Providing base url - -By default, we take the base url from the first `server` param from the json schema: -```json -{ -"servers": [{ "url": "/api/v3" }] -} -``` - -However, you can provide the `--url` option for the cli that will be used instead: - -```tsx -npx @hyper-fetch/codegen-openapi --schema https://petstore3.swagger.io/api/v3/openapi.json --name hyper-fetch.requests.ts --url https://petstore3.swagger.io -``` - -Then, the client from the generated file will use it: - -```tsx -... // generated file -export const client = new Client({url: "https://petstore3.swagger.io"}) -``` diff --git a/documentation/docs/documentation/01-getting-started/_category_.json b/documentation/docs/getting-started/_category_.json similarity index 100% rename from documentation/docs/documentation/01-getting-started/_category_.json rename to documentation/docs/getting-started/_category_.json diff --git a/documentation/docs/getting-started/comparison.mdx b/documentation/docs/getting-started/comparison.mdx new file mode 100644 index 000000000..5b24a1435 --- /dev/null +++ b/documentation/docs/getting-started/comparison.mdx @@ -0,0 +1,930 @@ +--- +sidebar_position: 8 +title: Why Hyper Fetch is Unique +sidebar_label: Comparison +--- + +import { Info } from "lucide-react"; +import Tippy from "@tippyjs/react"; +import "tippy.js/dist/tippy.css"; + +# Comparison + +The biggest difference between **`Hyper Fetch`** and other libraries is the opinionated architecture and assumptions on +which it is built. Hyper Fetch’s core features are written in TS **without dependencies**; because of this, it can work +in many environments without a specific connection to a given framework. + +Hyper Fetch is not just a wrapper; it offers full control over the flow and observation of data exchange. Our main goals +were to introduce: + +- a data storage standard, +- offer an embedded HTTP adapter, +- reduce setup times, +- solve major architectural difficulties. + +:::info + +Producing an accurate and unbiased comparison is quite a challenge. Libraries develop quickly, so if you notice that our +data needs to be corrected, please let us know by opening an issue. + +::: + +--- + +## Why Hyper Fetch Is Unique + +- Easy upload/download progress and ETA tracking +- Supports request queueing and many [dispatching strategies](/core/dispatcher.mdx#dispatching-modes)! +- [Command](/core/request.mdx) / [Builder](/core/client.mdx) pattern gives you amazing control over requests at any time +- Code-sharing architecture lets [testers to hook into development setup and types](/getting-started/testing.mdx) +- Features flat side-effects [helper hooks annotation](/react/01-overview.mdx#helper-hooks) and promotes readable code +- Provides structure [recipes](/getting-started/development.mdx#structure) for large scale applications that + significantly limit maintenance costs. +- Core features include [offline](/guides/02-advanced/offline.mdx) and + [persistence](/guides/02-advanced/persistence.mdx) support, giving you full control over your data + +--- + +## Features + +
✅ - Documented support
+
🚧 - Work in progress
+
🔵 - Require additional Plugin/Coding
+
🔴 - Not supported / Not documented
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Hyper Fetch + + Query + + + SWR + + Apollo +
Supported environmentsAnyAnyReactAny
ProtocolsAnyAnyAnyGraphQL
Caching ApproachRequest SchemaHierarchical Key > ValueUnique Key > ValueNormalized Schema
Cache Key StrategyRequest KeyJSONJSONGraphQL Query
Data Change DetectionDeep ComparisonDeep ComparisonDeep ComparisonDeep Comparison
Data MemoizationNormalized IdentityFull Structural SharingIdentityNormalized Identity
Queue Key StrategyRequest KeyN/AN/AN/A
Devtools🔵
+ +
+ + Server Connection Setup +
+
+
🔵🔵
+ +
+ + Shared Request +
+
+
🔵🔵
+ +
+ + Dependency tracking +
+
+
Cache Persistence
+ +
+ + Requests Persistence +
+
+
🔴🔴🔴
+ +
+ + Download ETA +
+
+
🔵🔵🔵
+ +
+ + Uploading ETA +
+
+
🔵🔵🔵
+ +
+ + Pooling +
+
+
+ +
+ + Dependent Queries +
+
+
Paginated Queries
+ +
+ + Query Params Parsing +
+
+
🔵🔵🔵
+ +
+ + Queueing +
+
+
🔴🔴🔴
+ +
+ + Retries +
+
+
🔵
+ +
+ + Default Adapter +
+
+
🔴🔴
+ +
+ + Infinite Queries +
+
+
+ +
+ + SSR +
+
+
+ +
+ + Initial Data +
+
+
+ +
+ + Cache Hydration +
+
+
🔴
+ +
+ + Garbage Collecting +
+
+
🔴🔴
+ +
+ + Pre Request Intercepting +
+
+
🔵🔵
+ +
+ + Post Request Intercepting +
+
+
🔵🔵
+ +
+ + Prefetching +
+
+
🔵
+ +
+ + Cancellation +
+
+
🔴🔴
+ +
+ + Queue Cancellation +
+
+
🔴🔴🔴
+ +
+ + Authentication +
+
+
🔵🔵
+ +
+ + Stale While Revalidate +
+
+
Refresh Data
+ +
+ + Offline Request Pause +
+
+
🔴🔴
+ +
+ + Network Status Re-fetching +
+
+
+ +
+ + Window Focus Re-fetching +
+
+
🔴
+ +
+ + Normalized Caching +
+
+
🔴🔴
+ +
+ + Automatic Re-fetch After Mutation +
+
+
🔵🔵
+ +
+ + Query Matching +
+
+
🔴🔴
+ +
+ + Cache Matching +
+
+
+ +
+ + Query Lifecycle Events +
+
+
🔴🔴
+ +
+ + Data Flow Standard +
+
+
🔴🔴
+ +
+ + Request Start/Stop +
+
+
🔴🔴🔴
+ +
+ + Request Queue Start/Stop/Pause +
+
+
🔴🔴🔴
+ +
+ + Request Data Mapping +
+
+
🔵🔵N/A
+ +
+ + Cache Invalidation +
+
+
+ +
+ + File Uploading +
+
+
🔵🔵
+ +
+ + Global Response Side-Effects +
+
+
🔴🔴🔴
+ +
+ + Scroll Recovery +
+
+
+ +
+ + Simple Request Execution +
+
+
🔴🔴🔴
+ +
+ + Requests Manager +
+
+
🔴🔴🔴
+ +
+ + Tabs Storages Synchronization +
+
+
🚧🚧🔴🔴
+ +
+ + Tabs Dispatching Synchronization +
+
+
🚧🔴🔴🔴
+ +
+ + Websocket +
+
+
🔴🔴🔴
+ +
+ + Server Sent Events +
+
+
🔴🔴🔴
+
+ +--- + +## Typescript features + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Hyper Fetch + + Query + + + SWR + + Apollo +
+ +
+ + Response types +
+
+
+ +
+ + Request data types +
+
+
+ +
+ + Global error types +
+
+
🔴🔴
+ +
+ + Local error types +
+
+
🔴🔴🔴
+ +
+ + Query params types +
+
+
🔴🔴
+ +
+ + Params types +
+
+
🔴🔴
+ +
+ + Request state tracking types +
+
+
🔴🔴
+
diff --git a/documentation/docs/getting-started/components/cards.tsx b/documentation/docs/getting-started/components/cards.tsx new file mode 100644 index 000000000..29cbbbddc --- /dev/null +++ b/documentation/docs/getting-started/components/cards.tsx @@ -0,0 +1,36 @@ +/* eslint-disable react/no-array-index-key */ +import { useSidebar } from "@site/src/hooks/use-sidebar"; +import { Description, Title, Noise, DocsCard } from "@site/src/components"; + +export const Cards = () => { + const { sidebar } = useSidebar(); + + return ( +
+ {sidebar.map((item, index) => { + return ( + + + +
+
+
+ +
+
+ + {item.name} + + + {item.description} + +
+
+
+ ); + })} +
+ ); +}; diff --git a/documentation/docs/getting-started/components/environments.tsx b/documentation/docs/getting-started/components/environments.tsx new file mode 100644 index 000000000..fb20acc8d --- /dev/null +++ b/documentation/docs/getting-started/components/environments.tsx @@ -0,0 +1,71 @@ +/* eslint-disable react/no-array-index-key */ +import { Description, Title, Noise, DocsCard } from "@site/src/components"; +import { cn } from "@site/src/lib/utils"; + +const environments = [ + { + name: "Web & PWA", + description: "Fully supported Web and PWA environments.", + icon: "✅", + }, + { + name: "React", + description: "Fully supported React environments with hooks and integrations.", + icon: "✅", + }, + { + name: "React Native", + description: "Fully supported React Native environments with hooks and integrations.", + icon: "✅", + }, + { + name: "SSR", + description: "Fully supported SSR environments (Next.js, Astro, Tanstack Router) with hydration abilities.", + icon: "✅", + }, + { + name: "Node.js", + description: "Supporting Node.js environments with server integrations.", + icon: "✅", + }, + { + name: "Svelte", + description: "Hyper Fetch is working with Svelte, but do not expose any hooks yet.", + icon: "⚙️", + }, + { + name: "Vue", + description: "Hyper Fetch is working with Vue, but do not expose any hooks yet.", + icon: "⚙️", + }, +]; +export const Environments = () => { + return ( +
+ {environments.map((item, index) => { + return ( + + +
+
+
+ {item.icon} +
+
+ + {item.name} + + + {item.description} + +
+
+ ); + })} +
+ ); +}; diff --git a/documentation/docs/getting-started/development.mdx b/documentation/docs/getting-started/development.mdx new file mode 100644 index 000000000..b94806a93 --- /dev/null +++ b/documentation/docs/getting-started/development.mdx @@ -0,0 +1,63 @@ +--- +sidebar_position: 4 +title: Development +sidebar_label: Development +--- + +# Development + +To help you get started with Hyper Fetch, we've outlined a few best practices for structuring your project. These are +suggestions, feel free to adapt them to your workflow. + +--- + +## 1. Project Structure + +1. **Organize by Feature:** Start by creating an `/api` directory to house your **client** and related **requests**. + +2. **Entity-Based Directories:** For each entity in your system (e.g., users, todos, categories, products, groups), + create a separate directory within `/api` to store the corresponding requests. + +3. **Maximize Reusability:** By leveraging classes and an object-oriented approach, you can easily access and reuse + requests throughout your application. This structure also allows your tests to utilize existing configurations + without duplication. + +**Example Structure:** + +``` +src +│ +├── api +│ ├── client.ts +│ ├── users +│ │ └── users.api.ts +│ ├── products +│ │ └── products.api.ts +│ └── ... +├── ... +└── ... +``` + +--- + +## 2. Debugging + +Preferred to debug is by using [Hyper Flow](docs/hyper-flow) - dedicated devtools for Hyper Fetch. It allows you to see +all the requests, responses, and errors in one place - manage cache and see detailed statistics. + +![Hyper Flow](/img/previews/network-details.png) + + + +### Alternatively you can debug apps in one of two ways: + +1. **Use the built-in logger** + +Use the client `setDebug(true)` method for this. It will start logging actions in the console. To adjust what type of +logs will be shown, use `setLogLevel(3)` to get debug logs displayed in the console. Those will show you the exact data +and information flow in the library while working on your application. + +2. **Create your own logger** + +You can create your own dev tools based on events sent from the [requestManager](/core/managers.mdx#requestmanager). +There is no limitation, and you can receive all necessary data to create everything you may need. diff --git a/documentation/docs/getting-started/index.mdx b/documentation/docs/getting-started/index.mdx new file mode 100644 index 000000000..060679abb --- /dev/null +++ b/documentation/docs/getting-started/index.mdx @@ -0,0 +1,77 @@ +--- +sidebar_position: 1 +title: Getting started +sidebar_label: Intro +--- + +import { Cards } from "./components/cards"; +import { Environments } from "./components/environments"; + +> **`Hyper Fetch`** is a modern, open source framework for request-based and real-time communication, purpose-built to +> eliminate boilerplate and streamline data handling across any TypeScript environment—whether you're working in the +> browser, on the server, or in native platforms like React Native or Electron. + +## Modules + +Our framework consist of the core modules which are responsible for the request-based and real-time communication or +connecting to particular framework - like Hyper-Fetch React. + + + +## Environments + +Core library should work in any JavaScript environment. + +For frontend frameworks and libraries, we release additional packages that enrich core logic and add appropriate API +connections for the given environment (as with the addition of hooks in React). + + + +## Integrations + +We also provide integrations for some of the most popular data-exchange libraries or services like Firebase. + + + +## Our Motivation + +We got the idea for Hyper Fetch after leading several React projects. There are many great libraries using for fetching +data like `Axios`, `SWR`, and `React Query`. However, the logic that they provide is detached from each other – i.e. the +hooks are detached from the fetchers. So you often need to mix them together in order to fetch the data. + +This gives users a lot of customization options, but it also comes with some issues - for example how to track +upload/download progress, how to call the requests outside of the components while still using the caching and other +features. It is hard to inform the hooks about the request status inside of the components or SSR environments. + +Usually you’d need to build your logic every time you start developing a new application. And there is no +`standard schema` nor any standards on how to manage this. It usually causes the challenges on the project level in how +to name things, how to organize requests and of course you have to build the whole logic around it. + +We decided to overcome this issue by creating a _straightforward_, _opinionated_, yet _flexible_ fetching solution. + +## Our Goals + +1. Establish a consistent, neutral standard for seamless data exchange. +2. Accelerate development by reusing logic and configurations, eliminating repetitive work across projects. +3. Benefit from a comprehensive set of built-in fetching capabilities, easily extendable to meet custom requirements. +4. Centralize server configurations to avoid duplication and ensure consistency between and within projects. +5. Boost development speed: set up fetchers and dependencies in minutes, drastically reducing setup time. +6. Effortlessly monitor request progress and manage queues with intuitive tracking tools. +7. Leverage advanced offline support and data persistence features for robust application behavior. +8. Improve developer experience and code quality through automated processes that prevent common errors. + +## Guides + +We have many examples and guides to help you get started. + + diff --git a/documentation/docs/getting-started/installation.mdx b/documentation/docs/getting-started/installation.mdx new file mode 100644 index 000000000..b28ba09ff --- /dev/null +++ b/documentation/docs/getting-started/installation.mdx @@ -0,0 +1,63 @@ +--- +sidebar_position: 3 +title: Installation +sidebar_label: Installation +--- + +An installation of Hyper Fetch can be run `clean` or for a specific environment. However, we only currently support +React. + +:::info + +The main `@hyper-fetch/core` package is required for other sub packages to work. + +::: + +--- + +## Install Core packages + +For minimal installation, you need to install the core package and eslint plugin. + +```bash npm2yarn2pnpm +npm install --save @hyper-fetch/core eslint-plugin-hyper-fetch +``` + +:::warning + +[Eslint plugin](../integrations/plugin-eslint/) is required for maximum type safety. It enhances the abilities of the +typescript via checking the object generic types to match exact schema. + +::: + +--- + +## Sockets + +Sockets is created for handling websockets or server sent events. + +```bash npm2yarn2pnpm +npm install --save @hyper-fetch/sockets +``` + +--- + +## React + +React allows the installation of the base library with hooks, offering interfaces that facilitate library usage and +support the application lifecycle. + +```bash npm2yarn2pnpm +bun add @hyper-fetch/core @hyper-fetch/react +``` + +--- + +## Integrations + + diff --git a/documentation/docs/getting-started/quick-start.mdx b/documentation/docs/getting-started/quick-start.mdx new file mode 100644 index 000000000..458551ca4 --- /dev/null +++ b/documentation/docs/getting-started/quick-start.mdx @@ -0,0 +1,66 @@ +--- +sidebar_position: 2 +title: Quick Start +sidebar_label: Quick Start +--- + +# Quick Start + +Welcome to Hyper Fetch! Our library is designed to be modular, so you can choose the packages that best suit your +project's needs. We offer several Quick Start guides to help you get up and running as smoothly as possible. + +--- + +## Quick Starts + +Each guide is tailored to a specific package, providing you with focused instructions and examples to get you started. + +### Core + +Hyper Fetch's core is framework-agnostic, giving you the power to handle data fetching in any JavaScript environment. +This guide will walk you through the fundamentals of setting up the client and making your first requests. + + + +--- + +### Sockets + +Learn how to integrate real-time communication in your application using Hyper Fetch Sockets. This guide covers setting +up listeners and emitters for seamless, bi-directional data flow with your server. + + + +--- + +### React + +Unlock the full potential of Hyper Fetch in your React applications. This guide will show you how to use our powerful +hooks to manage server state with ease, simplifying data fetching, submission, and state management. + + + +--- + +## More guides + +For more guides, check out the guides page. You will find there guides for each package. + + diff --git a/documentation/docs/guides/01-basic/dispatching.mdx b/documentation/docs/guides/01-basic/dispatching.mdx deleted file mode 100644 index a7db630b5..000000000 --- a/documentation/docs/guides/01-basic/dispatching.mdx +++ /dev/null @@ -1,90 +0,0 @@ ---- -sidebar_position: 1 -title: Guide - Dispatching -sidebar_label: Dispatching ---- - -# Dispatching - ---- - -### Trigger request - -We can execute queries using the built-in `send()` function or use React hooks. - -```tsx -import { postLogin } from "server/auth"; - -... - -const handleLogin = async (values: {email: string, password: string}) => { - const { data, error, status } = await postLogin.setData(values).send(); - - if(data) { - // perform login - ... - } else { - // handle error - ... - } - -} - -... - -``` - -### Method `send()` - -The send method takes full advantage of the potential of our features. We have implemented `debouncing`, `retries`, -`cancellation` and `offline awaiting` solutions. - -We can also dynamically transfer data to execute the request. - -```tsx -import { postData } from "server/auth"; - -... - -const handleSend = async (values: ValuesType) => { - const { data, error, status } = await postData.send({ - data: values, - params: { accountId: 2 }, - queryParams: { param1: "test", param2: [1,2,3] } - }) - - if(data) { - // perform success action - ... - } else { - // handle error - ... - } - -} - -... - -``` - -### Lifecycle - -`send()` method allow us to hook into request lifecycle. We can do it with one of following methods. - -{@import Hyper-Fetch FetchSendActionsType preview} - -#### Example - -```ts -const { data, error, status } = await getData.send({ - onSettle: (requestId) => { - console.log(`Starting request: ${requestId}`); - }, - onDownloadProgress: (values) => { - console.log(`Download progress: ${values}`); - }, - onResponse: (response) => { - console.log(`Got response: ${response}`); - }, -}); -``` diff --git a/documentation/docs/guides/01-basic/global-defaults.mdx b/documentation/docs/guides/01-basic/global-defaults.mdx deleted file mode 100644 index 22a4161c6..000000000 --- a/documentation/docs/guides/01-basic/global-defaults.mdx +++ /dev/null @@ -1,55 +0,0 @@ ---- -sidebar_position: 10 -title: Guide - Global Defaults -sidebar_label: Global Defaults ---- - -# Global Defaults - ---- - -### Use global defaults for request - -Adding global configs can significantly limit application setup. For the greatest flexibility in configurations created for larger and more complex applications, -global configs are added through callbacks based on request data. This allows us the flexibility to, for example, -make one default configuration for requests using the get method and another default config for the requests using other methods. - -Remember: Global configurations are overwritten by request settings, so the options that we set will be applied only to requests that do not have settings specified in their options. - ---- - -### Example - -```ts -export const client = new Client({ url }).setRequestDefaultOptions((requestOptions) => { - if (requestOptions.method === "GET") { - return { - deduplicate: true, - cacheTime: 20000, - retry: 3, - }; - } - - return { - deduplicate: false, - cache: false, - retry: 0, - }; -}); -``` - -### Add your own key mappers - -Data and requests made within our library are organized by keys. Each key is generated from request metadata, such as -parameters, query parameters, endpoints, and methods. They can be replaced by the way you choose to segregate and organize data -with your own key mappers. - -Note: Keys must be a string value. - - -```ts -client.setQueueKeyMapper((request) => `Custom_Key_${request.method}_${request.endpoint}`); -client.setAbortKeyMapper((request) => `Abort_Key_${request.method}_${request.endpoint}`); -client.setCacheKeyMapper((request) => `Abort_Key_${request.method}_${request.endpoint}`); -client.setEffectKeyMapper((request) => `Abort_Key_${request.method}_${request.endpoint}`); -``` diff --git a/documentation/docs/guides/01-basic/query-params.mdx b/documentation/docs/guides/01-basic/query-params.mdx deleted file mode 100644 index 7c6645cd3..000000000 --- a/documentation/docs/guides/01-basic/query-params.mdx +++ /dev/null @@ -1,52 +0,0 @@ ---- -sidebar_position: 4 -title: Guide - Query Params -sidebar_label: Query Params ---- - -# Query Params - ---- - -### Request query parameters - -The use of query params is based on built-in parser. We can freely change its options and format to match our needs. - ---- - -### Setting query params - -You can set query params by using the `setQueryParams` method on request. It's type can be configured on request -creation. - -```ts -type QueryParamsType = { - search: string; - sort: string; -}; - -const getUsers = client.createRequest()({ endpoint: "/users" }); - -// Setting the query params - -const request = getUsers.setQueryParams({ search: "John", sort: "age" }); -console.log(request.endpoint); // Output: "/users?search=John&sort=age" -``` - -```ts -const getUsers = client.createRequest()({ endpoint: "/users" }); - -const request = getUsers.setQueryParams("search=John&sort=age"); -console.log(request.endpoint); // Output: "/users?search=John&sort=age" -``` - -### Custom query params format - -We can setup custom config with instructions how to stringify the values. It can be setup on the client with -`setQueryParamsConfig` method. - -#### Available options: - -{@import Hyper-Fetch QueryStringifyOptionsType returns} - ---- diff --git a/documentation/docs/guides/01-basic/setup.mdx b/documentation/docs/guides/01-basic/setup.mdx deleted file mode 100644 index da50e3787..000000000 --- a/documentation/docs/guides/01-basic/setup.mdx +++ /dev/null @@ -1,60 +0,0 @@ ---- -sidebar_position: 0 -title: Guides - Setup -sidebar_label: Setup ---- - -# Setup - ---- - -### Initialize Client - -The first step is to initialize the **[Client](/documentation/02-core/client.mdx)**. It manage the basic configuration -for connection to the server and all the elements that make up Hyper Fetch, which are instances of - **dispatchers**, -**cache** and **app managers**. We start by determining the `url` of our server. - -```tsx title="/src/server/client.ts" -import { Client } from "@hyper-fetch/core"; - -export const client = new Client({ url: "http://localhost:3000" }); -``` - -### Create Request - -Then, having already prepared connection setup for the server, we use the client method to create -**[requests](/documentation/02-core/request.mdx)** and assign types to them. - -:::caution - -We are using currying to achieve auto generated types for the endpoint string.
This solution will be removed once -[https://github.com/microsoft/TypeScript/issues/10571](https://github.com/microsoft/TypeScript/issues/10571) get -resolved. - -::: - -```tsx title="/src/server/auth/auth.ts" -import { client } from "../client.ts"; - -type ResponseType = { token: string; refreshToken: string }; -type RequestType = { email: string; password: string }; - -const postLogin = client.createRequest()({ method: "POST", endpoint: "/auth/login" }); -``` - -## Simple Requests - -```ts -const getRepositoryDetails = client.createRequest()({ - method: "GET", - url: "/repos/BetterTyped/hyper-fetch", -}); - -const { data, error, status, extra } = await getRepositoryDetails.send(); - -const { data, error, status, extra } = await postLogin.send({ - data: { email: "test@test.com", password: "Password123$" }, // Please, do not use this password ;) -}); -``` - -## Congratulations! You're ready to use **Hyper Fetch**! 🎊 diff --git a/documentation/docs/guides/03-sockets/setup.mdx b/documentation/docs/guides/03-sockets/setup.mdx deleted file mode 100644 index e259a8704..000000000 --- a/documentation/docs/guides/03-sockets/setup.mdx +++ /dev/null @@ -1,59 +0,0 @@ ---- -sidebar_position: 1 -title: Socket Setup Guide -sidebar_label: Setup ---- - -# Setup - ---- - -### Initialize Socket - -The first step is to initialize the **[Socket](/documentation/03-sockets/socket.mdx)**. It manage the basic -configuration for connection to the server and all the sub-systems. We start by determining the `url` of our server. - -```tsx title="/src/server/socket.ts" -import { Socket } from "@hyper-fetch/sockets"; - -export const socket = new Socket({ url: "ws://localhost:3000" }); -``` - -### Create Listener - -Then, having already prepared connection setup for the server, we use the socket method to create -**[Listeners](/documentation/03-sockets/listener.mdx)** and assign types to them. - -```tsx -type ChatMessageType = { - message: string; -}; - -export const onChatMessage = socketInstance.createListener()({ - endpoint: "chat-message", // endpoint of the event -}); -``` - -### Create Emitter - -Then, having already prepared connection setup for the server, we use the socket method to create -**[Emitter](/documentation/03-sockets/emitter.mdx)** and assign types to them. - -```tsx -import { socketInstance } from "./socket"; - -type ChatMessageType = { - message: string; -}; - -// Optional -type AcknowledgementResponseType = { - value: string; -}; - -export const sendChatMessage = socketInstance.createEmitter()({ - endpoint: "chat-message", // endpoint of the event -}); -``` - -## You're ready to use Hyper Fetch Sockets! 🎊 diff --git a/documentation/docs/guides/01-basic/_category_.json b/documentation/docs/guides/core/01-basics/_category_.json similarity index 100% rename from documentation/docs/guides/01-basic/_category_.json rename to documentation/docs/guides/core/01-basics/_category_.json diff --git a/documentation/versioned_docs/version-4.x.x/guides/01-basic/authentication.mdx b/documentation/docs/guides/core/01-basics/authentication.mdx similarity index 86% rename from documentation/versioned_docs/version-4.x.x/guides/01-basic/authentication.mdx rename to documentation/docs/guides/core/01-basics/authentication.mdx index 3651ec200..cd7acf117 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/01-basic/authentication.mdx +++ b/documentation/docs/guides/core/01-basics/authentication.mdx @@ -6,8 +6,8 @@ sidebar_label: Authentication # Authentication -Authentication in Hyper Fetch consists of two steps. We will introduce changes in the -**[Client](/documentation/02-core/client.mdx)** instance and subsequent requests created from it. +Authentication in Hyper Fetch consists of two steps. We will introduce changes in the **[Client](/core/client.mdx)** +instance and subsequent requests created from it. --- @@ -43,8 +43,8 @@ That's it, from now on, each request made using this request will be authenticat ### Refresh token -To refresh the token, we can add a special `onError` interceptor to our **[Client](/documentation/02-core/client.mdx)**. -It intercepts errors received in our requests and it is asynchronous, which allows for efficient handling of such cases. +To refresh the token, we can add a special `onError` interceptor to our **[Client](/core/client.mdx)**. It intercepts +errors received in our requests and it is asynchronous, which allows for efficient handling of such cases. To properly **`avoid the infinite refresh loop`** of the token, we need to protect ourselves by setting the `used` field to **true** thanks to the appropriate method `setUsed`. This will allow us to easily verify whether our commend has @@ -61,7 +61,7 @@ export const client = new Client({ url }).onError(async (response, request) => { // not go into infinite loop and trigger this operation only once if (!request.used && refreshToken && status === 401) { // Prepare the refresh token request - const postRefreshToken = client.createRequest()({ + const postRefreshToken = client.createRequest<{ response: LoginResponse; payload: LoginData }>()({ endpoint: "/refresh-token", method: "POST", }); diff --git a/documentation/docs/guides/01-basic/data-mapping.mdx b/documentation/docs/guides/core/01-basics/data-mapping.mdx similarity index 75% rename from documentation/docs/guides/01-basic/data-mapping.mdx rename to documentation/docs/guides/core/01-basics/data-mapping.mdx index d492c23bd..770c66729 100644 --- a/documentation/docs/guides/01-basic/data-mapping.mdx +++ b/documentation/docs/guides/core/01-basics/data-mapping.mdx @@ -12,12 +12,13 @@ sidebar_label: Data Mapping We often encounter the need to `map` data before sending it to the server. This is usually required for two reasons. -One of them is `breaking changes to the server API`. This may require many updates to our existing code, -which can be dangerous for the application and introduce **regression**. +One of them is `breaking changes to the server API`. This may require many updates to our existing code, which can be +dangerous for the application and introduce **regression**. Another case is sending `FormData`, which is impossible to represent in the form of an exact interface in terms of the fields it contains. In order not to send something that is equivalent to the type `any`, we can specify the correct type -in the request and then map everything to `FormData` just before it gets added to the **[Dispatcher](/documentation/02-core/dispatcher.mdx)**. +in the request and then map everything to `FormData` just before it gets added to the +**[Dispatcher](/core/dispatcher.mdx)**. This ensures very `type-safe` and `flexible` application development. @@ -27,7 +28,7 @@ The following example shows the implementation for `FormData`: ```ts export const postUserProfile = client - .createRequest()({ + .createRequest<{ response: boolean; payload: UserProfile }>()({ method: "PATCH", endpoint: "/users/profile/:userId", }) @@ -47,13 +48,13 @@ export const postUserProfile = client ### Map whole request The example below shows the implementation of request mapping. We can make each `postUserProfile` request add custom -headers (or any other value) to the request before sending it later (when we use it in our application). -This allows us to implement mappers, validators, and any async logic. Throwing an error makes the request class return an error, -just as we’d get from the "real" query. +headers (or any other value) to the request before sending it later (when we use it in our application). This allows us +to implement mappers, validators, and any async logic. Throwing an error makes the request class return an error, just +as we’d get from the "real" query. ```ts export const postUserProfile = client - .createRequest()({ + .createRequest<{ response: boolean; payload: UserProfile }>()({ method: "PATCH", endpoint: "/users/profile/:userId", }) diff --git a/documentation/docs/guides/core/01-basics/dispatching.mdx b/documentation/docs/guides/core/01-basics/dispatching.mdx new file mode 100644 index 000000000..077f531c0 --- /dev/null +++ b/documentation/docs/guides/core/01-basics/dispatching.mdx @@ -0,0 +1,90 @@ +--- +sidebar_position: 1 +title: Guide - Dispatching +sidebar_label: Dispatching +--- + +# Dispatching + +--- + +### Trigger request + +We can execute queries using the built-in `send()` function or use React hooks. + +```tsx +import { postLogin } from "server/auth"; + +... + +const handleLogin = async (values: {email: string, password: string}) => { + const { data, error, status } = await postLogin.setData(values).send(); + + if(data) { + // perform login + ... + } else { + // handle error + ... + } + +} + +... + +``` + +### Method `send()` + +The send method takes full advantage of the potential of our features. We have implemented `debouncing`, `retries`, +`cancellation` and `offline awaiting` solutions. + +We can also dynamically transfer data to execute the request. + +```tsx +import { postData } from "server/auth"; + +... + +const handleSend = async (values: ValuesType) => { + const { data, error, status } = await postData.send({ + data: values, + params: { accountId: 2 }, + queryParams: { param1: "test", param2: [1,2,3] } + }) + + if(data) { + // perform success action + ... + } else { + // handle error + ... + } + +} + +... + +``` + +### Lifecycle + +`send()` method allow us to hook into request lifecycle. We can do it with one of following methods. + +(@import core RequestSendType type=preview) + +#### Example + +```ts +const { data, error, status } = await getData.send({ + onBeforeSent: (requestId) => { + console.log(`Starting request: ${requestId}`); + }, + onDownloadProgress: (values) => { + console.log(`Download progress: ${values}`); + }, + onResponse: (response) => { + console.log(`Got response: ${response}`); + }, +}); +``` diff --git a/documentation/versioned_docs/version-4.x.x/guides/01-basic/error-handling.mdx b/documentation/docs/guides/core/01-basics/error-handling.mdx similarity index 89% rename from documentation/versioned_docs/version-4.x.x/guides/01-basic/error-handling.mdx rename to documentation/docs/guides/core/01-basics/error-handling.mdx index 3e6fe363d..14c19ab80 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/01-basic/error-handling.mdx +++ b/documentation/docs/guides/core/01-basics/error-handling.mdx @@ -31,7 +31,7 @@ type GlobalErrorType = { status: 400 | 404 | 500; }; -export const client = new Client({ url }); +export const client = new Client<{error: GlobalErrorType} >({ url }); ``` Now the error type of our request will reflect `GlobalErrorType`. @@ -60,7 +60,7 @@ type LocalErrorType = { }; }; -const postUser = client.createRequest()({ +const postUser = client.createRequest<{response: ResponseType, payload: RequestType, localError: LocalErrorType}>()({ method: "POST", endpoint: "/users", }); diff --git a/documentation/docs/guides/core/01-basics/global-defaults.mdx b/documentation/docs/guides/core/01-basics/global-defaults.mdx new file mode 100644 index 000000000..ddc8ee2df --- /dev/null +++ b/documentation/docs/guides/core/01-basics/global-defaults.mdx @@ -0,0 +1,62 @@ +--- +sidebar_position: 10 +title: Guide - Global Defaults +sidebar_label: Global Defaults +--- + +{/* setRequestDefaults */} + +{/* setAdapterDefaults */} + +{/* keys generation */} + +# Global Defaults + +--- + +### Use global defaults for request + +Adding global configs can significantly limit application setup. For the greatest flexibility in configurations created +for larger and more complex applications, global configs are added through callbacks based on request data. This allows +us the flexibility to, for example, make one default configuration for requests using the get method and another default +config for the requests using other methods. + +Remember: Global configurations are overwritten by request settings, so the options that we set will be applied only to +requests that do not have settings specified in their options. + +--- + +### Example + +```ts +export const client = new Client({ url }).setRequestDefaultOptions((requestOptions) => { + if (requestOptions.method === "GET") { + return { + deduplicate: true, + staleTime: 20000, + retry: 3, + }; + } + + return { + deduplicate: false, + cache: false, + retry: 0, + }; +}); +``` + +### Add your own key mappers + +Data and requests made within our library are organized by keys. Each key is generated from request metadata, such as +parameters, query parameters, endpoints, and methods. They can be replaced by the way you choose to segregate and +organize data with your own key mappers. + +Note: Keys must be a string value. + +```ts +client.setQueryKeyMapper((request) => `Custom_Key_${request.method}_${request.endpoint}`); +client.setAbortKeyMapper((request) => `Abort_Key_${request.method}_${request.endpoint}`); +client.setCacheKeyMapper((request) => `Abort_Key_${request.method}_${request.endpoint}`); +client.setEffectKeyMapper((request) => `Abort_Key_${request.method}_${request.endpoint}`); +``` diff --git a/documentation/docs/guides/01-basic/headers.mdx b/documentation/docs/guides/core/01-basics/headers.mdx similarity index 100% rename from documentation/docs/guides/01-basic/headers.mdx rename to documentation/docs/guides/core/01-basics/headers.mdx diff --git a/documentation/docs/guides/01-basic/parameters.mdx b/documentation/docs/guides/core/01-basics/parameters.mdx similarity index 100% rename from documentation/docs/guides/01-basic/parameters.mdx rename to documentation/docs/guides/core/01-basics/parameters.mdx diff --git a/documentation/versioned_docs/version-4.x.x/guides/01-basic/payload.mdx b/documentation/docs/guides/core/01-basics/payload.mdx similarity index 82% rename from documentation/versioned_docs/version-4.x.x/guides/01-basic/payload.mdx rename to documentation/docs/guides/core/01-basics/payload.mdx index 200e29cb2..87e1e867c 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/01-basic/payload.mdx +++ b/documentation/docs/guides/core/01-basics/payload.mdx @@ -20,7 +20,7 @@ transfer. To set data of our request we need to use method `.setData()`. ```ts -const createUser = client.createRequest()({ method: "POST", endpoint: "/users" }); +const createUser = client.createRequest<{response: ResponseType, payload: DataType}>()({ method: "POST", endpoint: "/users" }); const request = createUser.setData({ name: "Maciej" }); // Must match `DataType` console.log(request.data); // Output: { name: "Maciej" } diff --git a/documentation/docs/guides/core/01-basics/query-params.mdx b/documentation/docs/guides/core/01-basics/query-params.mdx new file mode 100644 index 000000000..d74d236f3 --- /dev/null +++ b/documentation/docs/guides/core/01-basics/query-params.mdx @@ -0,0 +1,52 @@ +--- +sidebar_position: 4 +title: Guide - Query Params +sidebar_label: Query Params +--- + +# Query Params + +--- + +### Request query parameters + +The use of query params is based on built-in parser. We can freely change its options and format to match our needs. + +--- + +### Setting query params + +You can set query params by using the `setQueryParams` method on request. It's type can be configured on request +creation. + +```ts +type QueryParamsType = { + search: string; + sort: string; +}; + +const getUsers = client.createRequest<{ response: Response; queryParams: QueryParamsType }>()({ endpoint: "/users" }); + +// Setting the query params + +const request = getUsers.setQueryParams({ search: "John", sort: "age" }); +console.log(request.endpoint); // Output: "/users?search=John&sort=age" +``` + +```ts +const getUsers = client.createRequest<{ response: Response; queryParams: string }>()({ endpoint: "/users" }); + +const request = getUsers.setQueryParams("search=John&sort=age"); +console.log(request.endpoint); // Output: "/users?search=John&sort=age" +``` + +### Custom query params format + +We can setup custom config with instructions how to stringify the values. It can be setup on the client with +`setQueryParamsConfig` method. + +#### Available options: + +(@import core QueryStringifyOptionsType type=returns) + +--- diff --git a/documentation/docs/guides/core/01-basics/request.mdx b/documentation/docs/guides/core/01-basics/request.mdx new file mode 100644 index 000000000..418455dc0 --- /dev/null +++ b/documentation/docs/guides/core/01-basics/request.mdx @@ -0,0 +1,9 @@ +--- +sidebar_position: 9 +title: Guide - Create Request +sidebar_label: Create Request +--- + +# Create Request + +--- diff --git a/documentation/docs/guides/01-basic/retries.mdx b/documentation/docs/guides/core/01-basics/retries.mdx similarity index 100% rename from documentation/docs/guides/01-basic/retries.mdx rename to documentation/docs/guides/core/01-basics/retries.mdx diff --git a/documentation/docs/guides/core/01-basics/setup.mdx b/documentation/docs/guides/core/01-basics/setup.mdx new file mode 100644 index 000000000..72f018c48 --- /dev/null +++ b/documentation/docs/guides/core/01-basics/setup.mdx @@ -0,0 +1,81 @@ +--- +sidebar_position: 0 +title: Guides - Setup +sidebar_label: Setup +--- + +:::secondary You'll learn + +- How to initialize the Client +- How to create a Request +- How to send a Request +- How to handle the Response +- How to handle the Error +- How to handle the Loading +- How to handle the Cache + +::: + +{/* show different types for setup */} + +{/* show what are the options params */} + +{/* move requests creation to the next section */} + +{/* show some options with examples */} + +--- + +### Initialize Client + +The first step is to initialize the **[Client](/core/client.mdx)**. It manage the basic configuration for connection to +the server and all the elements that make up Hyper Fetch, which are instances of - **dispatchers**, **cache** and **app +managers**. We start by determining the `url` of our server. + +```tsx title="/src/server/client.ts" +import { Client } from "@hyper-fetch/core"; + +export const client = new Client({ url: "http://localhost:3000" }); +``` + +### Create Request + +Then, having already prepared connection setup for the server, we use the client method to create +**[requests](/core/request.mdx)** and assign types to them. + +:::caution + +We are using currying to achieve auto generated types for the endpoint string.
This solution will be removed once +[https://github.com/microsoft/TypeScript/issues/10571](https://github.com/microsoft/TypeScript/issues/10571) get +resolved. + +::: + +```tsx title="/src/server/auth/auth.ts" +import { client } from "../client.ts"; + +type ResponseType = { token: string; refreshToken: string }; +type RequestType = { email: string; password: string }; + +const postLogin = client.createRequest<{ response: ResponseType; payload: RequestType }>()({ + method: "POST", + endpoint: "/auth/login", +}); +``` + +## Simple Requests + +```ts +const getRepositoryDetails = client.createRequest()({ + method: "GET", + url: "/repos/BetterTyped/hyper-fetch", +}); + +const { data, error, status, extra } = await getRepositoryDetails.send(); + +const { data, error, status, extra } = await postLogin.send({ + data: { email: "test@test.com", password: "Password123$" }, // Please, do not use this password ;) +}); +``` + +## Congratulations! You're ready to use **Hyper Fetch**! 🎊 diff --git a/documentation/docs/guides/02-advanced/_category_.json b/documentation/docs/guides/core/02-advanced/_category_.json similarity index 100% rename from documentation/docs/guides/02-advanced/_category_.json rename to documentation/docs/guides/core/02-advanced/_category_.json diff --git a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/cache-revalidation.mdx b/documentation/docs/guides/core/02-advanced/cache-revalidation.mdx similarity index 95% rename from documentation/versioned_docs/version-4.x.x/guides/02-advanced/cache-revalidation.mdx rename to documentation/docs/guides/core/02-advanced/cache-revalidation.mdx index eb92490ae..8b32a24eb 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/cache-revalidation.mdx +++ b/documentation/docs/guides/core/02-advanced/cache-revalidation.mdx @@ -5,9 +5,6 @@ sidebar_label: Cache Revalidation # Cache Revalidation -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - When using the application, `we often encounter the need to revalidate data`. This is because of various `dynamic changes` in the case of the users interacting with our applications. For example, when deleting a note from the list of notes in the database, we want the already stored data to be refreshed. In this case, we use cache revalidation. diff --git a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/cancellation.mdx b/documentation/docs/guides/core/02-advanced/cancellation.mdx similarity index 87% rename from documentation/versioned_docs/version-4.x.x/guides/02-advanced/cancellation.mdx rename to documentation/docs/guides/core/02-advanced/cancellation.mdx index 8d7161b6a..fda332448 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/cancellation.mdx +++ b/documentation/docs/guides/core/02-advanced/cancellation.mdx @@ -6,9 +6,6 @@ sidebar_label: Cancellation # Request Cancellation -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - --- Canceling requests is needed for various reasons. There are cases when we make the request and we want to abort it @@ -23,7 +20,7 @@ the new one and cancel previous at the same time to prevent `race-conditioning` we need to set the `cancelable: true` field to request options. ```ts -const getUsers = client.createRequest()({ endpoint: "/users", cancelable: true }); +const getUsers = client.createRequest<{response: UsersList}>()({ endpoint: "/users", cancelable: true }); // Make a request to server getUsers.send() @@ -85,10 +82,10 @@ postFile.send() // Stop the request from being send for later // Do not delete it from storage to trigger it later -client.fetchDispatcher.stop(postFile.queueKey) +client.fetchDispatcher.stop(postFile.queryKey) ... // Start sending again -client.fetchDispatcher.start(postFile.queueKey) +client.fetchDispatcher.start(postFile.queryKey) ``` diff --git a/documentation/docs/guides/02-advanced/custom-adapter.mdx b/documentation/docs/guides/core/02-advanced/custom-adapter.mdx similarity index 96% rename from documentation/docs/guides/02-advanced/custom-adapter.mdx rename to documentation/docs/guides/core/02-advanced/custom-adapter.mdx index 508224179..9c8e43508 100644 --- a/documentation/docs/guides/02-advanced/custom-adapter.mdx +++ b/documentation/docs/guides/core/02-advanced/custom-adapter.mdx @@ -107,7 +107,7 @@ const customHttpAdapter: BaseAdapterType< QueryParamsType > = (request: RequestInstance) => Promise.resolve({ data: null, error: null, status: 0 }); -const client = new Client({ url }).setAdapter(customHttpAdapter); +const client = new Client<{error: Error}>({ url }).setAdapter(customHttpAdapter); ``` #### Adapter Unions @@ -124,7 +124,7 @@ type MyAdapter = const customHttpAdapter: MyAdapter = (request: RequestInstance) => Promise.resolve({ data: null, error: null, status: 0 }); -const client = new Client({ url }).setAdapter(customHttpAdapter); +const client = new Client<{error: Error}>({ url }).setAdapter(customHttpAdapter); // We decide which type to used based on passed config diff --git a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/deduplication.mdx b/documentation/docs/guides/core/02-advanced/deduplication.mdx similarity index 92% rename from documentation/versioned_docs/version-4.x.x/guides/02-advanced/deduplication.mdx rename to documentation/docs/guides/core/02-advanced/deduplication.mdx index a91a0cb93..64e2bea9a 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/deduplication.mdx +++ b/documentation/docs/guides/core/02-advanced/deduplication.mdx @@ -36,4 +36,4 @@ getUsers.send(); ``` To learn more of `deduplication` and `alternative modes` of requesting you can visit -**[Dispatcher Docs](/documentation/02-core/dispatcher.mdx#dispatching-modes)**. +**[Dispatcher Docs](/core/dispatcher.mdx#dispatching-modes)**. diff --git a/documentation/docs/guides/02-advanced/intercepting.mdx b/documentation/docs/guides/core/02-advanced/intercepting.mdx similarity index 100% rename from documentation/docs/guides/02-advanced/intercepting.mdx rename to documentation/docs/guides/core/02-advanced/intercepting.mdx diff --git a/documentation/docs/guides/02-advanced/mapping.mdx b/documentation/docs/guides/core/02-advanced/mapping.mdx similarity index 94% rename from documentation/docs/guides/02-advanced/mapping.mdx rename to documentation/docs/guides/core/02-advanced/mapping.mdx index a49ebb78f..f06d63fdc 100644 --- a/documentation/docs/guides/02-advanced/mapping.mdx +++ b/documentation/docs/guides/core/02-advanced/mapping.mdx @@ -22,7 +22,7 @@ Mappers can be asynchronus. This way we can build up bigger responses. ```ts export const getUser = client - .createRequest()({ + .createRequest<{response: UserModel}>()({ method: "GET", endpoint: "/users/:userId", }) @@ -49,7 +49,7 @@ For example, the User class can parse dates from an ISO string into a Date objec ```ts export const getUser = client - .createRequest()({ + .createRequest<{response: UserModel}>()({ method: "GET", endpoint: "/users/:userId", }) @@ -77,7 +77,7 @@ Mappers can be asynchronous. This lets us build up bigger responses. ```ts export const postUser = client - .createRequest()({ + .createRequest<{response: UserModel, payload: UserData}>()({ method: "POST", endpoint: "/users/:userId", }) diff --git a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/offline.mdx b/documentation/docs/guides/core/02-advanced/offline.mdx similarity index 82% rename from documentation/versioned_docs/version-4.x.x/guides/02-advanced/offline.mdx rename to documentation/docs/guides/core/02-advanced/offline.mdx index 9756e7941..8b5e80eca 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/offline.mdx +++ b/documentation/docs/guides/core/02-advanced/offline.mdx @@ -12,9 +12,9 @@ Currently, the operation of this system is default, which means that the offline automated. Receiving the information that the application has entered the offline state, we automatically stop the requests being executed or queued, and then resume them when the connection is restored. -The offline operation is greatly influenced by one of the managers - -**[AppManager](/api/Hyper-Fetch/Class/AppManager.mdx)**, which is responsible for emitting events related to the current -state of the application - such as offline / online or focused / blurred. +The offline operation is greatly influenced by one of the managers - **[AppManager](/api/core/Classes/AppManager.mdx)**, +which is responsible for emitting events related to the current state of the application - such as offline / online or +focused / blurred. --- @@ -25,7 +25,7 @@ online state are based on the window event listeners, when we are talking about NetInfo as the source of the connection state. ```ts -export const client = new Client({ +export const client = new Client<{error: ServerErrorType}>({ url: environment.serverUrl, appManager: (instance) => new AppManager(instance, { diff --git a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/persistence.mdx b/documentation/docs/guides/core/02-advanced/persistence.mdx similarity index 93% rename from documentation/versioned_docs/version-4.x.x/guides/02-advanced/persistence.mdx rename to documentation/docs/guides/core/02-advanced/persistence.mdx index 161f456d1..5b3dcd9bc 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/02-advanced/persistence.mdx +++ b/documentation/docs/guides/core/02-advanced/persistence.mdx @@ -37,7 +37,7 @@ const persistenceStorage: CacheStorageType = { clear: () => storage.clearAll(), }; -export const client = new Client({ +export const client = new Client<{ error: ServerErrorType }>({ url: "localhost:3000", cache: (instance) => new Cache(instance, { @@ -52,7 +52,7 @@ Example below shows the **`IndexedDb`** persistence storage. This way we will us it to our components. Every time we use the `cache.get(cacheKey)` method, we send a request to our `lazyStorage` and at the same time return the last data we have, when our promise responds, we will receive an appropriate event that will propagate the data in our components. This way we are NOT loading whole persistent data into our memory, but only part -of it, not to mention that it will get garbage collected when it's cacheTime expire. +of it, not to mention that it will get garbage collected when it's staleTime expire. ```ts import { get, set, del, keys } from "idb-keyval"; @@ -64,7 +64,7 @@ const asyncStorage: CacheAsyncStorageType = { delete: (key) => del(key), }; -export const client = new Client({ +export const client = new Client<{ error: ServerErrorType }>({ url: "localhost:3000", cache: (instance) => new Cache(instance, { @@ -107,7 +107,7 @@ const persistenceStorage: DispatcherStorageType = { clear: () => storage.clearAll(), }; -export const client = new Client({ +export const client = new Client<{ error: ServerErrorType }>({ url: "localhost:3000", fetchDispatcher: (instance) => new Dispatcher(instance, { @@ -134,7 +134,7 @@ const persistenceStorage: DispatcherStorageType = { clear: () => storage.clearAll(), }; -export const client = new Client({ +export const client = new Client<{ error: ServerErrorType }>({ url: "localhost:3000", submitDispatcher: (instance) => new Dispatcher(instance, { diff --git a/documentation/docs/guides/02-advanced/prefetching.mdx b/documentation/docs/guides/core/02-advanced/prefetching.mdx similarity index 100% rename from documentation/docs/guides/02-advanced/prefetching.mdx rename to documentation/docs/guides/core/02-advanced/prefetching.mdx diff --git a/documentation/docs/guides/02-advanced/queueing.mdx b/documentation/docs/guides/core/02-advanced/queueing.mdx similarity index 100% rename from documentation/docs/guides/02-advanced/queueing.mdx rename to documentation/docs/guides/core/02-advanced/queueing.mdx diff --git a/documentation/docs/guides/02-advanced/validation.mdx b/documentation/docs/guides/core/02-advanced/validation.mdx similarity index 96% rename from documentation/docs/guides/02-advanced/validation.mdx rename to documentation/docs/guides/core/02-advanced/validation.mdx index b1931199c..98be7ad5d 100644 --- a/documentation/docs/guides/02-advanced/validation.mdx +++ b/documentation/docs/guides/core/02-advanced/validation.mdx @@ -53,7 +53,7 @@ const User = z.object({ }); export const postUser = client - .createRequest()({ + .createRequest<{response: UserModel, payload: UserData}>()({ method: "POST", endpoint: "/users/:userId", }) diff --git a/documentation/versioned_docs/version-3.x.x/documentation/01-getting-started/_category_.json b/documentation/docs/guides/getting-started/_category_.json similarity index 100% rename from documentation/versioned_docs/version-3.x.x/documentation/01-getting-started/_category_.json rename to documentation/docs/guides/getting-started/_category_.json diff --git a/documentation/docs/guides/getting-started/additional-resources.mdx b/documentation/docs/guides/getting-started/additional-resources.mdx new file mode 100644 index 000000000..dbd9b3f29 --- /dev/null +++ b/documentation/docs/guides/getting-started/additional-resources.mdx @@ -0,0 +1,17 @@ +--- +sidebar_position: 3 +title: Additional Resources +sidebar_label: Additional Resources +--- + +Expanding your knowledge about data fetching is essential for building robust web applications. Below are some highly +recommended resources to help you understand the fundamentals and best practices: + +- [MDN Web Docs: Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) + - Comprehensive guide to the Fetch API, including usage examples, browser compatibility, and advanced features. +- [MDN Web Docs: XMLHttpRequest](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) + - Legacy API reference for XMLHttpRequest, useful for understanding older codebases. +- [JavaScript.info: Fetch](https://javascript.info/fetch) + - In-depth tutorial on using the Fetch API with practical examples and explanations. + +> _Tip: Always refer to the official documentation for the most up-to-date and accurate information._ diff --git a/documentation/docs/guides/getting-started/community.mdx b/documentation/docs/guides/getting-started/community.mdx new file mode 100644 index 000000000..3f9ceb0b4 --- /dev/null +++ b/documentation/docs/guides/getting-started/community.mdx @@ -0,0 +1,29 @@ +--- +sidebar_position: 2 +title: Community +sidebar_label: Community +--- + +We welcome and encourage the community to contribute courses, tutorials, and learning resources! If you have created or +found a valuable course, please add it here to help others learn and grow. + +> **No community courses have been added yet.** +> +> _Be the first to contribute a course!_ + +--- + +## Community Resources + +Below is a list of community-created resources, such as blog posts, videos, and guides. If you have a resource to share, +please add it to the list! + +- _No resources have been added yet._ +- _You can add your blog post, video, or guide here!_ + +--- + +### How to Contribute + +Want to help the community? Add your course or resource by editing this page and submitting a pull request. We +appreciate your contributions and look forward to seeing this list grow! diff --git a/documentation/docs/guides/getting-started/index.mdx b/documentation/docs/guides/getting-started/index.mdx new file mode 100644 index 000000000..67bccfaa3 --- /dev/null +++ b/documentation/docs/guides/getting-started/index.mdx @@ -0,0 +1,55 @@ +--- +sidebar_position: 1 +title: Overview +sidebar_label: Overview +--- + +# Guides Overview + +> Welcome to the **Hyper Fetch Guides**! This section is your go-to resource for mastering Hyper Fetch through +> practical, problem-focused tutorials. Whether you're setting up your first request or building complex, real-time +> features, our guides provide step-by-step instructions and real-world examples to help you succeed. + +:::secondary What you'll learn + +- How to set up and configure Hyper Fetch for your project +- Core concepts like creating requests, handling authentication, and managing state +- Advanced techniques including caching, offline support, and real-time communication with sockets +- Best practices for integrating Hyper Fetch with React +- Effective testing strategies for your Hyper Fetch applications + +::: + +--- + +## Guides + +Our guides are designed to be problem-focused, offering detailed explanations and complete code examples. They provide +step-by-step instructions for implementing various features, from basic setup to advanced use cases. We aim to provide +comprehensive guides for each core package, covering everything you need to build robust applications. + +--- + +## Integrations + +Our integration guides are located alongside their respective documentation. This approach ensures that all the +information you need, from setup to advanced usage, is available in one convenient place. This helps you quickly +understand the nuances of each integration and get started with minimal friction. + + + +--- + +:::tip Pro Tip + +If you're new, start with the **Getting Started** guide. For specific problems or advanced features, feel free to jump +directly to the relevant section. + +Explore the guides to level up your Hyper Fetch skills and build robust, scalable applications with confidence! + +::: diff --git a/documentation/docs/documentation/04-react/02-core/_category_.json b/documentation/docs/guides/react/01-basics/_category_.json similarity index 100% rename from documentation/docs/documentation/04-react/02-core/_category_.json rename to documentation/docs/guides/react/01-basics/_category_.json diff --git a/documentation/docs/guides/04-react/01-core/config-provider.mdx b/documentation/docs/guides/react/01-basics/config-provider.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/config-provider.mdx rename to documentation/docs/guides/react/01-basics/config-provider.mdx diff --git a/documentation/docs/guides/04-react/01-core/dependent-requests.mdx b/documentation/docs/guides/react/01-basics/dependent-requests.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/dependent-requests.mdx rename to documentation/docs/guides/react/01-basics/dependent-requests.mdx diff --git a/documentation/docs/guides/react/01-basics/fetching.mdx b/documentation/docs/guides/react/01-basics/fetching.mdx new file mode 100644 index 000000000..930870cdb --- /dev/null +++ b/documentation/docs/guides/react/01-basics/fetching.mdx @@ -0,0 +1,33 @@ +--- +sidebar_position: 1 +title: How to fetch with React hook useFetch +sidebar_label: Fetching +--- + +# Fetching + +For fetching data use the [`useFetch`](/react/03-hooks/use-fetch.mdx) hook that automates request handling. There are +tons of [options](/react/03-hooks/use-fetch.mdx#options) to adjust this hook so check out the +[documentation](/react/03-hooks/use-fetch.mdx). + +--- + +### Examples + +```tsx +import { getUsers, getUser } from "server/user"; + +... + +const { data, error, loading } = useFetch(getUsers) + +``` + +```tsx +import { getUsers, getUser } from "server/user"; + +... + +const { data, error, loading } = useFetch(getUser.setParams({ userId: 1 })) + +``` diff --git a/documentation/docs/guides/04-react/01-core/infinite-scroll.mdx b/documentation/docs/guides/react/01-basics/infinite-scroll.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/infinite-scroll.mdx rename to documentation/docs/guides/react/01-basics/infinite-scroll.mdx diff --git a/documentation/docs/guides/04-react/01-core/optimistic-approach.mdx b/documentation/docs/guides/react/01-basics/optimistic-approach.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/optimistic-approach.mdx rename to documentation/docs/guides/react/01-basics/optimistic-approach.mdx diff --git a/documentation/docs/guides/04-react/01-core/pagination.mdx b/documentation/docs/guides/react/01-basics/pagination.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/pagination.mdx rename to documentation/docs/guides/react/01-basics/pagination.mdx diff --git a/documentation/docs/guides/04-react/01-core/progress.mdx b/documentation/docs/guides/react/01-basics/progress.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/progress.mdx rename to documentation/docs/guides/react/01-basics/progress.mdx diff --git a/documentation/docs/guides/04-react/01-core/queues.mdx b/documentation/docs/guides/react/01-basics/queues.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/queues.mdx rename to documentation/docs/guides/react/01-basics/queues.mdx diff --git a/documentation/docs/guides/04-react/01-core/refreshing.mdx b/documentation/docs/guides/react/01-basics/refreshing.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/refreshing.mdx rename to documentation/docs/guides/react/01-basics/refreshing.mdx diff --git a/documentation/docs/guides/04-react/01-core/request-lifecycle.mdx b/documentation/docs/guides/react/01-basics/request-lifecycle.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/request-lifecycle.mdx rename to documentation/docs/guides/react/01-basics/request-lifecycle.mdx diff --git a/documentation/versioned_docs/version-4.x.x/guides/04-react/01-core/submitting.mdx b/documentation/docs/guides/react/01-basics/submitting.mdx similarity index 91% rename from documentation/versioned_docs/version-4.x.x/guides/04-react/01-core/submitting.mdx rename to documentation/docs/guides/react/01-basics/submitting.mdx index 9c6c66458..4c6fa7d6d 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/04-react/01-core/submitting.mdx +++ b/documentation/docs/guides/react/01-basics/submitting.mdx @@ -6,9 +6,9 @@ sidebar_label: Submitting # Submitting data -For submitting and mutating data on server use the [`useSubmit`](/documentation/04-react/02-core/use-submit.mdx) hook -that automates request handling. There are tons of [options](/documentation/04-react/02-core/use-fetch.mdx#options) to -adjust this hook so check out the [documentation](/documentation/04-react/02-core/use-submit.mdx). +For submitting and mutating data on server use the [`useSubmit`](/react/03-hooks/use-submit.mdx) hook that automates +request handling. There are tons of [options](/react/03-hooks/use-fetch.mdx#options) to adjust this hook so check out +the [documentation](/react/03-hooks/use-submit.mdx). --- diff --git a/documentation/docs/guides/04-react/01-core/window-focus-blur.mdx b/documentation/docs/guides/react/01-basics/window-focus-blur.mdx similarity index 100% rename from documentation/docs/guides/04-react/01-core/window-focus-blur.mdx rename to documentation/docs/guides/react/01-basics/window-focus-blur.mdx diff --git a/documentation/docs/documentation/03-sockets/_category_.json b/documentation/docs/guides/react/02-sockets/_category_.json similarity index 100% rename from documentation/docs/documentation/03-sockets/_category_.json rename to documentation/docs/guides/react/02-sockets/_category_.json diff --git a/documentation/docs/guides/04-react/02-sockets/emitting.mdx b/documentation/docs/guides/react/02-sockets/emitting.mdx similarity index 100% rename from documentation/docs/guides/04-react/02-sockets/emitting.mdx rename to documentation/docs/guides/react/02-sockets/emitting.mdx diff --git a/documentation/docs/guides/04-react/02-sockets/listening.mdx b/documentation/docs/guides/react/02-sockets/listening.mdx similarity index 100% rename from documentation/docs/guides/04-react/02-sockets/listening.mdx rename to documentation/docs/guides/react/02-sockets/listening.mdx diff --git a/documentation/docs/documentation/04-react/_category_.json b/documentation/docs/guides/react/_category_.json similarity index 100% rename from documentation/docs/documentation/04-react/_category_.json rename to documentation/docs/guides/react/_category_.json diff --git a/documentation/docs/documentation/04-react/03-sockets/_category_.json b/documentation/docs/guides/sockets/_category_.json similarity index 100% rename from documentation/docs/documentation/04-react/03-sockets/_category_.json rename to documentation/docs/guides/sockets/_category_.json diff --git a/documentation/docs/guides/03-sockets/authentication.mdx b/documentation/docs/guides/sockets/authentication.mdx similarity index 100% rename from documentation/docs/guides/03-sockets/authentication.mdx rename to documentation/docs/guides/sockets/authentication.mdx diff --git a/documentation/docs/guides/03-sockets/emitting.mdx b/documentation/docs/guides/sockets/emitting.mdx similarity index 100% rename from documentation/docs/guides/03-sockets/emitting.mdx rename to documentation/docs/guides/sockets/emitting.mdx diff --git a/documentation/docs/guides/03-sockets/listening.mdx b/documentation/docs/guides/sockets/listening.mdx similarity index 100% rename from documentation/docs/guides/03-sockets/listening.mdx rename to documentation/docs/guides/sockets/listening.mdx diff --git a/documentation/docs/guides/03-sockets/query-params.mdx b/documentation/docs/guides/sockets/query-params.mdx similarity index 100% rename from documentation/docs/guides/03-sockets/query-params.mdx rename to documentation/docs/guides/sockets/query-params.mdx diff --git a/documentation/docs/guides/sockets/setup.mdx b/documentation/docs/guides/sockets/setup.mdx new file mode 100644 index 000000000..9ac1e9f59 --- /dev/null +++ b/documentation/docs/guides/sockets/setup.mdx @@ -0,0 +1,59 @@ +--- +sidebar_position: 1 +title: Socket Setup Guide +sidebar_label: Setup +--- + +# Setup + +--- + +### Initialize Socket + +The first step is to initialize the **[Socket](/sockets/socket.mdx)**. It manage the basic configuration for connection +to the server and all the sub-systems. We start by determining the `url` of our server. + +```tsx title="/src/server/socket.ts" +import { Socket } from "@hyper-fetch/sockets"; + +export const socket = new Socket({ url: "ws://localhost:3000" }); +``` + +### Create Listener + +Then, having already prepared connection setup for the server, we use the socket method to create +**[Listeners](/sockets/listener.mdx)** and assign types to them. + +```tsx +type ChatMessageType = { + message: string; +}; + +export const onChatMessage = socketInstance.createListener()({ + endpoint: "chat-message", // endpoint of the event +}); +``` + +### Create Emitter + +Then, having already prepared connection setup for the server, we use the socket method to create +**[Emitter](/sockets/emitter.mdx)** and assign types to them. + +```tsx +import { socketInstance } from "./socket"; + +type ChatMessageType = { + message: string; +}; + +// Optional +type AcknowledgementResponseType = { + value: string; +}; + +export const sendChatMessage = socketInstance.createEmitter()({ + endpoint: "chat-message", // endpoint of the event +}); +``` + +## You're ready to use Hyper Fetch Sockets! 🎊 diff --git a/documentation/docs/guides/06-testing/_category_.json b/documentation/docs/guides/testing/_category_.json similarity index 100% rename from documentation/docs/guides/06-testing/_category_.json rename to documentation/docs/guides/testing/_category_.json diff --git a/documentation/docs/guides/testing/index.mdx b/documentation/docs/guides/testing/index.mdx new file mode 100644 index 000000000..f74baa7a1 --- /dev/null +++ b/documentation/docs/guides/testing/index.mdx @@ -0,0 +1,78 @@ +--- +sidebar_position: 1 +title: Testing +sidebar_label: Testing +--- + +Testing is one of the best things in Hyper Fetch. With our architecture and focus on global singleton structure, tests +can be largely based on the application’s configuration. This means tests are no longer sensitive to micro-changes (like +changing endpoints or types). Everything reacts and adapts to tests or shows the appropriate error – making tests easier +to maintain and faster to write. + +--- + +## Benefits + +- Our setup is always up-to-date with the production solution. +- No configuration or setup duplication. +- Easy test maintenance. +- Faster test builds. + +--- + +## Isolation + +The `Client` can become a global module, which will cause it to get mixed between test cases from different files if we +run them in many workers. Thus, we need to isolate it. This takes two steps: + +1. ** Clear the Client** + +Use the built-in method `.clear()` to ensure client submodules are initialized and clear of any changes. + +2. **Clear the node / runner cache** + +As you would with Jest, use the built-in method `.resetModules()` to clean the client global module and reinitialize it. +Depending on the libraries used, you can check out your environment runner methods or try libraries like **decache**. + +--- + +## Example + +In this example, we’re using the great mocking libraries [msw](https://mswjs.io/) and +[testing-library](https://testing-library.com/), but this solution works with other libraries like +[Cypress](https://www.cypress.io/). This allows us to simulate real requests and make our tests as close to the +production environment as possible. We can thus create a really powerful flow that allows us to easily develop tests. + +You can build a utilities set that takes a request and uses its method, endpoint and types to create functions that +allow you to intercept requests. + +```tsx +import { client } from "../api/client"; +import { getUsers } from "../api"; + +// 'createInterceptor' is some custom method for handling intercepting in the tests we write +// In our internal test we used MSW as main intercepting layer +const getUsersInterceptor = createInterceptor(getUsers, { + data: [ + { name: "John", age: 18 }, + { name: "Matthew", age: 27 }, + ], +}); + +beforeEach(() => { + // We need to reset modules cache as client is globally exported module + // This way it will not get mixed-up with other test cases running in parallel + jest.resetModules(); + // Clean the environment to make sure it's isolated + client.clear(); +}); + +it("My test", async () => { + // Start interceptor listener + const mock = getUsersInterceptor(); + + // Handle test + renderApp(); + expect(await screen.findByText(mock.data[0])).toBeVisible(); +}); +``` diff --git a/documentation/versioned_docs/version-4.x.x/guides/06-testing/isolation.mdx b/documentation/docs/guides/testing/isolation.mdx similarity index 97% rename from documentation/versioned_docs/version-4.x.x/guides/06-testing/isolation.mdx rename to documentation/docs/guides/testing/isolation.mdx index bdc17658e..156d1ff1a 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/06-testing/isolation.mdx +++ b/documentation/docs/guides/testing/isolation.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 1 +sidebar_position: 2 title: Testing Isolation sidebar_label: Isolation --- diff --git a/documentation/docs/guides/testing/mocking.mdx b/documentation/docs/guides/testing/mocking.mdx new file mode 100644 index 000000000..3a9c084b6 --- /dev/null +++ b/documentation/docs/guides/testing/mocking.mdx @@ -0,0 +1,8 @@ +--- +sidebar_position: 3 +title: Mocking +sidebar_label: Mocking +--- + +Mocking is a technique used to replace real objects with test objects. This is useful when you want to test a function +or method without actually calling the real object. diff --git a/documentation/versioned_docs/version-4.x.x/guides/06-testing/stubbing.mdx b/documentation/docs/guides/testing/stubbing.mdx similarity index 97% rename from documentation/versioned_docs/version-4.x.x/guides/06-testing/stubbing.mdx rename to documentation/docs/guides/testing/stubbing.mdx index dacb397d2..19a671427 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/06-testing/stubbing.mdx +++ b/documentation/docs/guides/testing/stubbing.mdx @@ -1,5 +1,6 @@ --- -title: Testing Stubbing +sidebar_position: 4 +title: Stubbing sidebar_label: Stubbing --- diff --git a/documentation/docs/guides/05-typescript/_category_.json b/documentation/docs/guides/typescript/_category_.json similarity index 100% rename from documentation/docs/guides/05-typescript/_category_.json rename to documentation/docs/guides/typescript/_category_.json diff --git a/examples/next/public/.gitkeep b/documentation/docs/guides/typescript/extend.mdx similarity index 100% rename from examples/next/public/.gitkeep rename to documentation/docs/guides/typescript/extend.mdx diff --git a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/global-error.mdx b/documentation/docs/guides/typescript/global-error.mdx similarity index 87% rename from documentation/versioned_docs/version-4.x.x/guides/05-typescript/global-error.mdx rename to documentation/docs/guides/typescript/global-error.mdx index 7bf014615..b73de6c50 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/global-error.mdx +++ b/documentation/docs/guides/typescript/global-error.mdx @@ -22,5 +22,5 @@ Remember to add the Error type, which is returned from internal errors like Canc ```ts type GlobalErrors = Error | { message: string }; -export const client = new Client({ url }); +export const client = new Client<{error: GlobalErrors}>({ url }); ``` diff --git a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/local-error.mdx b/documentation/docs/guides/typescript/local-error.mdx similarity index 84% rename from documentation/versioned_docs/version-4.x.x/guides/05-typescript/local-error.mdx rename to documentation/docs/guides/typescript/local-error.mdx index c03b5c03d..c494f6c1e 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/local-error.mdx +++ b/documentation/docs/guides/typescript/local-error.mdx @@ -25,7 +25,7 @@ type LocalErrorType = { }; }; -const postUser = client.createRequest()({ +const postUser = client.createRequest<{response: ResponseType, payload: RequestType, localError: LocalErrorType}>()({ method: "POST", endpoint: "/users", }); diff --git a/documentation/docs/guides/typescript/query-params.mdx b/documentation/docs/guides/typescript/query-params.mdx new file mode 100644 index 000000000..3c6203f45 --- /dev/null +++ b/documentation/docs/guides/typescript/query-params.mdx @@ -0,0 +1,25 @@ +--- +sidebar_position: 6 +title: Setting Query Params Type +sidebar_label: Query Params +--- + +# Query Params Type + +Query Params are very often untyped, which may cause maintenance problems. However, this is not the case here, and our +query params types can be easily added to the request. + +--- + +### Example + +```ts +type QueryParamsType = { + search?: string; + role?: "Admin" | "User"; +}; + +const getUsers = client.createRequest<{response: ResponseType, queryParams: QueryParamsType}>()({ + endpoint: "/users", +}); +``` diff --git a/documentation/docs/guides/05-typescript/request-extractors.mdx b/documentation/docs/guides/typescript/request-extractors.mdx similarity index 100% rename from documentation/docs/guides/05-typescript/request-extractors.mdx rename to documentation/docs/guides/typescript/request-extractors.mdx diff --git a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/request-payload-data.mdx b/documentation/docs/guides/typescript/request-payload-data.mdx similarity index 82% rename from documentation/versioned_docs/version-4.x.x/guides/05-typescript/request-payload-data.mdx rename to documentation/docs/guides/typescript/request-payload-data.mdx index 68ac86b5d..081aa9507 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/request-payload-data.mdx +++ b/documentation/docs/guides/typescript/request-payload-data.mdx @@ -20,7 +20,7 @@ type RequestPayloadType = { age: number; }; -const postUser = client.createRequest()({ +const postUser = client.createRequest<{response: ResponseType, payload: RequestPayloadType}>()({ method: "POST", endpoint: "/users", }); diff --git a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/response-type.mdx b/documentation/docs/guides/typescript/response-type.mdx similarity index 82% rename from documentation/versioned_docs/version-4.x.x/guides/05-typescript/response-type.mdx rename to documentation/docs/guides/typescript/response-type.mdx index d62173571..10928e823 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/response-type.mdx +++ b/documentation/docs/guides/typescript/response-type.mdx @@ -19,7 +19,7 @@ type ResponseType = Array<{ age: number; }>; -const getUsers = client.createRequest()({ +const getUsers = client.createRequest<{response: ResponseType}>()({ endpoint: "/users", }); ``` diff --git a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/url-parameters.mdx b/documentation/docs/guides/typescript/url-parameters.mdx similarity index 86% rename from documentation/versioned_docs/version-4.x.x/guides/05-typescript/url-parameters.mdx rename to documentation/docs/guides/typescript/url-parameters.mdx index 059e9f443..ce7c2cc52 100644 --- a/documentation/versioned_docs/version-4.x.x/guides/05-typescript/url-parameters.mdx +++ b/documentation/docs/guides/typescript/url-parameters.mdx @@ -14,7 +14,7 @@ the required parameters to execute the query itself. ### Example ```ts -const getUser = client.createRequest()({ +const getUser = client.createRequest<{response: ResponseType}>()({ endpoint: "/users/:userId", }); diff --git a/documentation/docs/hyper-flow/_category_.json b/documentation/docs/hyper-flow/_category_.json new file mode 100644 index 000000000..c50af513a --- /dev/null +++ b/documentation/docs/hyper-flow/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Devtools" +} diff --git a/documentation/docs/hyper-flow/download.mdx b/documentation/docs/hyper-flow/download.mdx new file mode 100644 index 000000000..e72894ac5 --- /dev/null +++ b/documentation/docs/hyper-flow/download.mdx @@ -0,0 +1,9 @@ +--- +sidebar_position: 2 +title: Download +sidebar_label: Download +--- + +# Download + +## Download diff --git a/documentation/docs/hyper-flow/getting-started.mdx b/documentation/docs/hyper-flow/getting-started.mdx new file mode 100644 index 000000000..e7859f80b --- /dev/null +++ b/documentation/docs/hyper-flow/getting-started.mdx @@ -0,0 +1,9 @@ +--- +sidebar_position: 3 +title: Getting Started +sidebar_label: Getting Started +--- + +# Getting Started + +## Getting Started diff --git a/documentation/docs/hyper-flow/index.mdx b/documentation/docs/hyper-flow/index.mdx new file mode 100644 index 000000000..e8d8e35b0 --- /dev/null +++ b/documentation/docs/hyper-flow/index.mdx @@ -0,0 +1,112 @@ +--- +sidebar_position: 1 +title: Hyper Flow +sidebar_label: Intro +--- + +# 🚀 Welcome to Hyper Flow + +> **Supercharge your development workflow with Hyper Flow — the ultimate DevTools suite for monitoring, debugging, and +> optimizing your application's network activity and cache.** + +Hyper Flow, part of the Hyper Fetch ecosystem, empowers you to: + +- **Visualize** your app's network and cache activity in real time +- **Debug** requests with ease +- **Optimize** performance and resource usage +- **Take control** of every request, queue, and cache entry + +--- + +## 🌟 Why Hyper Flow? + +Hyper Flow isn't just another DevTools panel. It's your command center for: + +- **Seamless debugging**: Instantly inspect requests, payloads, and responses +- **Performance insights**: Identify bottlenecks and optimize your app +- **Cache mastery**: View, manage, and debug cache entries effortlessly +- **Queue management**: Oversee and control all request queues + +--- + +## 🔍 Explore Hyper Flow Features + +This is a list of all the features that Hyper Flow offers. + +### 🗺️ Project Overview + +![Project General](../../static/img/previews/project-general.png) **See the big picture at a glance!** + +Get instant clarity on your app's health with a beautiful dashboard that visualizes network and cache stats. Spot +trends, catch anomalies, and understand your application's behavior in seconds — no more guesswork! + +### ⚡ Performance Insights + +![Project Performance](../../static/img/previews/project-performance.png) **Uncover hidden bottlenecks and boost your +app's speed!** + +Dive deep into request timings, throughput, and performance metrics. Hyper Flow helps you pinpoint slowdowns and +optimize your network layer for lightning-fast user experiences. + +### 🗃️ Cache Management + +![Project Cache](../../static/img/previews/project-cache.png) **Master your cache, maximize efficiency!** + +Visualize, inspect, and control your cache like never before. Instantly see what's stored, what's stale, and what needs +attention — all in one place. Say goodbye to mysterious cache bugs! + +### 📋 Queues List + +![Queues List](../../static/img/previews/queues-list.png) **Stay on top of every request!** + +Monitor all your request queues in real time. Whether you're handling retries, priorities, or concurrency, Hyper Flow +gives you the power to manage and troubleshoot with confidence. + +### 🔎 Queue Details + +![Queues Details](../../static/img/previews/queues-details.png) **Zoom in for total control!** + +Explore each queue's full history, status, and processing flow. Instantly debug stuck or failed requests and optimize +your queue strategies for flawless delivery. + +### 🌐 Network Requests List + +![Network List](../../static/img/previews/network-list.png) **Never miss a network event!** + +Watch every request as it happens. Filter, search, and analyze your app's traffic in real time — perfect for debugging, +QA, and performance tuning. + +### 🧩 Network Request Details + +![Network Details](../../static/img/previews/network-details.png) **See every byte, every header, every response!** + +Drill down into individual requests to inspect payloads, headers, and responses. Instantly spot issues, validate +integrations, and ensure your API calls are rock solid. + +### 🗂️ Cache List + +![Cache List](../../static/img/previews/cache-list.png) **Your cache, fully transparent!** + +Browse all cached items, check their status, and manage them with a click. No more black-box caching — take full control +and keep your app running smoothly. + +### 🔬 Cache Details + +![Cache Details](../../static/img/previews/cache-details.png) **Debug and optimize with surgical precision!** + +Examine every detail of specific cache entries. Track changes, debug issues, and fine-tune your caching strategy for +maximum performance. + +### 🏠 Application Panel + +![App Panel](../../static/img/previews/app.png) **Your DevTools HQ — all features, one place!** + +Access a unified dashboard that brings together every Hyper Flow feature. Jump between panels, correlate data, and +command your app's network and cache with ease. + +--- + +## ✨ Get Started with Hyper Flow + +Ready to transform your debugging and optimization workflow? Dive into Hyper Flow and experience the next level of +developer productivity! diff --git a/documentation/docs/documentation/05-adapters/axios/_category_.json b/documentation/docs/integrations/adapter-axios/_category_.json similarity index 100% rename from documentation/docs/documentation/05-adapters/axios/_category_.json rename to documentation/docs/integrations/adapter-axios/_category_.json diff --git a/documentation/docs/integrations/adapter-axios/overview.mdx b/documentation/docs/integrations/adapter-axios/overview.mdx new file mode 100644 index 000000000..3ba370540 --- /dev/null +++ b/documentation/docs/integrations/adapter-axios/overview.mdx @@ -0,0 +1,121 @@ +--- +sidebar_position: 1 +title: Axios Adapter +sidebar_label: Overview +--- + +[Read the API Reference »](/docs/api/adapter-axios/Variables/AxiosAdapter.mdx) + +Hyper Fetch's `axios` adapter is a simple integration that allows you to use the popular `axios` HTTP client for making +requests, while still benefiting from all of Hyper Fetch's features like caching, persistence, and request management. + +Using the `axios` adapter gives you the best of both worlds. + +--- + +:::tip Purpose + +1. **Leverage `axios`**: Use familiar `axios` options, headers, and error handling. +2. **Seamless Integration**: Combines `axios` with Hyper Fetch's caching, queuing, and persistence. +3. **Track Progress**: Get detailed upload and download progress events. +4. **Full Control**: Access `axios` response details and error objects when needed. + +::: + +--- + +## Getting Started + +To use the `axios` adapter: + +1. Install the package: + +```bash npm2yarn2pnpm +npm install @hyper-fetch/axios +``` + +2. Import it and set it on your `Client` instance: + +(@import adapter-axios AxiosAdapter type=import) + +```tsx +import { Client } from "@hyper-fetch/core"; +import { axiosAdapter } from "@hyper-fetch/axios"; + +const client = new Client({ url: "base-url" }).setAdapter(axiosAdapter); +``` + +3. ...and voila! It's done. Now you can set `axios` options on your requests. + +--- + +## Usage + +You can pass any valid `axios` request configuration options to your Hyper Fetch requests. These options can be set when +you create a request or when you send it. + +Here's an example of how to use some common `axios` options: + +```tsx +import { client } from "./client"; + +// Create a request with axios-specific options +const getUsers = client.createRequest()({ + endpoint: "/users", + options: { + // highlight-start + // Set a 5-second timeout for the request + timeout: 5000, + // Add authentication credentials + auth: { + username: "your-username", + password: "your-password", + }, + // highlight-end + }, +}); +``` + +Or use the `setOptions` method: + +```ts +import { client } from "./client"; + +const getUsers = client + .createRequest()({ + endpoint: "/users", + }) + .setOptions({ + // highlight-start + // Set a 5-second timeout for the request + timeout: 5000, + // Add authentication credentials + auth: { + username: "your-username", + password: "your-password", + }, + // highlight-end + }); +``` + +--- + +## See More + +For a full list of available `axios` request configuration options, please refer to the +[official axios documentation](https://axios-http.com/docs/req_config). + +:::caution Key Differences + +While you can use most `axios` options, some are managed by Hyper Fetch to ensure proper integration. You should **not** +pass the following options, as they will be overwritten by the adapter: + +- `url` +- `baseURL` +- `method` +- `data` (use `payload` in `send` method instead) +- `onUploadProgress` (use `onUploadProgress` in `send` method instead) +- `onDownloadProgress` (use `onDownloadProgress` in `send` method instead) +- `signal` (managed by Hyper Fetch for cancellation) + +::: diff --git a/documentation/docs/documentation/05-adapters/firebase/_category_.json b/documentation/docs/integrations/adapter-firebase-admin/_category_.json similarity index 100% rename from documentation/docs/documentation/05-adapters/firebase/_category_.json rename to documentation/docs/integrations/adapter-firebase-admin/_category_.json diff --git a/documentation/docs/integrations/adapter-firebase-admin/firestore.mdx b/documentation/docs/integrations/adapter-firebase-admin/firestore.mdx new file mode 100644 index 000000000..499919ba0 --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase-admin/firestore.mdx @@ -0,0 +1,109 @@ +--- +sidebar_position: 2 +title: Working with Firestore +sidebar_label: Firestore +--- + +## Available methods + +After setting the firestore adapter, we can start performing requests! We should select the appropriate method, +corresponding with firebase methods. If you want to learn more about available methods - please refer to the firebase +documentation. + +Due to its nature, we've solved the realtime listening a bit differently, and thus - this method is not allowed in a +standard adapter. For the `onSnapshot`-like usage, please check how to +**[listen to queries](/docs/integrations/adapter-firebase-admin/realtime-queries)**. + +```tsx +const getReq = client.createRequest()({ + endpoint: "", + method: "getDocs", // "addDoc" | "getDoc" | "getDocs" | "setDoc" | "updateDoc" | "deleteDoc" +}); +``` + +### getDocs + +```tsx +const getReq = client.createRequest()({ + endpoint: "", + method: "getDocs", +}); + +const { data, status, extra, success, error } = await req.send(); +// Data is an array of objects, each object also contains the __key param. +``` + +`extra`: + +1. `ref` - collection reference endpoint +2. `snapshot` - 'raw' `getDocs` firestore collection/query reference snapshot + +### getDoc + +```tsx +const req = client + .createRequest()({ + endpoint: ":teaId", + method: "getDoc", + }) + .setParams({ teaId: 1 }); + +const { data, status, extra, success, error } = await req.send(); +// setParams can be also passed in the send() method instead +``` + +`extra`: + +1. `ref` - document reference endpoint +2. `snapshot` - 'raw' firestore document reference snapshot + +### setDoc + +```tsx +const newData = { origin: "Poland", type: "Green", year: 2023, name: "Pou Ran Do Cha", amount: 10 }; +const setReq = client + .createRequest()({ + endpoint: ":teaId", + method: "setDoc", + // can also pass options: {merge: true} for achieving the firebase 'merge' option + }) + .setParams({ teaId: 1 }) + .setData(newData); + +await setReq.send(); +const { data } = await getReq.send(); +``` + +In case of `setDoc` - data returned is the same data as passed for setting. + +### addDoc + +```tsx +const addDocReq = client + .createRequest()({ + endpoint: "", + method: "addDoc", + options: {}, + }) + .setData(newData); + +await addDocReq.send(); +``` + +In case of `addDoc` - data returned is the same as passed data for setting + the `__key` param that is id of a newly +created document. + +### deleteDoc + +```tsx +const deleteDocReq = client + .createRequest()({ + endpoint: ":teaId", + method: "deleteDoc", + }) + .setParams({ teaId: 1 }); + +await deleteDocReq; +``` + +In case of `deleteDoc` - returned data equals `null` diff --git a/documentation/docs/integrations/adapter-firebase-admin/overview.mdx b/documentation/docs/integrations/adapter-firebase-admin/overview.mdx new file mode 100644 index 000000000..17ce752f9 --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase-admin/overview.mdx @@ -0,0 +1,93 @@ +--- +sidebar_position: 1 +title: Firebase Admin Adapter +sidebar_label: Overview +--- + +> Hyper Fetch `firebase` packages offer complete integration of `realtime database` and `firestore`, both for frontend +> and backend, with a single, unified approach for all of them. + +## Getting Started + +In order to start using the firebase adapter, you have to initialize the firestore/realtime database and set the correct +adapter on the `client`. + +There are two packages to chose from with two adapters each: + +1. `@hyper-fetch/firebase` - for working with web realtime and firestore databases: + 1. `firebaseAdapter` - for standard REST requests. + 2. `firebaseSocketsAdapter` - for realtime listening. +2. `@hyper-fetch/firebase-admin` - for admin versions of the packages: + 1. `firebaseAdminAdapter` - for standard REST requests. + 2. `firebaseSocketsAdminAdapter` - for realtime listening. + +Both packages have the same unified interface. + +```tsx +import { firebaseAdapter } from "@hyper-fetch/firebase"; +import { initializeApp } from "firebase/app"; +import { getFirestore } from "firebase/firestore"; + +// Firebase firestore database initialization +const app = initializeApp({ + projectId: "demo-test-firestore", +}); +const db = getFirestore(app); + +// Setting up the HyperFetch with a firebase adapter +const client = new Client({ url: "teas/" }).setAdapter(() => firebaseAdapter(db)); +const getReq = client.createRequest()({ + endpoint: "", + method: "getDocs", +}); + +// Checking the results +const { data, status, extra, success, error } = await getReq.send(); +``` + +In case of firebase admin, the only difference is the import: + +```tsx +import { firebaseAdminAdapter } from "@hyper-fetch/firebase-admin"; +... +``` + +Resulting `response` is an object that contains the following properties: + +1. `data` - data returned from the database. Each object returned from the database is enhanced also by the `__key` + param that equals id of a given document. +2. `status` - status indicating whether a request ended with `success`, `error` or `emptyResource`. `emptyResource` + occurs when the request to firebase succeeded but no data was returned. +3. `success` - general information if overall request succeeded (`true`) or failed (`false`) +4. `error` - contains error object if the request failed. +5. `extra` - contains additional properties, depending on a method used. For instance, for `getDocs` method - it allows + to access `ref` and `snapshot` from firestore firebase. + +## Differences from 'raw' firebase + +1. If we store an array in firebase, for instance `[a, b, c]`, the query would return the same array. However, if the + array stops being sequential - for instance, if `a` and `b` were deleted, firebase would return and object: + `{2: 'c', 4: 'e'}`. Hyper Fetch always returns and array. Each object in an array has an additional `__key` property, + that indicates the id of a given object. + +## Filtering queries + +In order to standardize the interface across both admin/web firestore/realtime, the filtering and limiting queries is +done via setting the `constraints` queryParam: + +```tsx +import { $limit, $orderBy, $where } from "constraints"; + +const req = client.createRequest()({ + endpoint: "", + method: "getDocs", +}); // or via setQueryParams method +const { data } = await req.send({ + queryParams: { constraints: [$where("type", "==", "Green"), $orderBy("year"), $limit(1)] }, +}); +``` + +User can pass the array of constrains and filters. Please pay attention to the fact that you need to filter via method +wrappers provided via `adapter-firebase` package: `$where`, `$orderBy`, `$limit`, `$startAt`, `$startAfter`, `$endAt`, +`$endAfter`, `$orderByChild`, `$orderByKey`, `$orderByValue`, `$limitToFirst`, `$limitToLast`, `$equalTo`. All of these +methods work exactly the same as their corresponding firebase equivalents. diff --git a/documentation/docs/documentation/05-adapters/firebase/04-realtime-queries.mdx b/documentation/docs/integrations/adapter-firebase-admin/realtime-queries.mdx similarity index 100% rename from documentation/docs/documentation/05-adapters/firebase/04-realtime-queries.mdx rename to documentation/docs/integrations/adapter-firebase-admin/realtime-queries.mdx diff --git a/documentation/docs/integrations/adapter-firebase-admin/realtime.mdx b/documentation/docs/integrations/adapter-firebase-admin/realtime.mdx new file mode 100644 index 000000000..ce847d9d0 --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase-admin/realtime.mdx @@ -0,0 +1,112 @@ +--- +sidebar_position: 3 +title: Working with Realtime database +sidebar_label: Realtime Database +--- + +## Available methods + +Our Realtime Database adapter provides all the methods the original offers. Due to its nature, we've solved the realtime +listening a bit differently, and thus - this method is not allowed in standard adapter. For the `onValue`-like usage, +please check how to **[listen to queries](/docs/integrations/adapter-firebase-admin/realtime-queries)**. + +### get + +```tsx +import { firebaseAdapter } from "@hyper-fetch/firebase"; + +const client = new Client({ url: "teas/" }).setAdapter(() => firebaseAdapter(realtimeDbWeb)); +const req = client.createRequest()({ + endpoint: "", + method: "get", +}); + +const { data, status, extra, success, error } = await req.send(); +``` + +`extra`: + +1. `ref` - reference to the endpoint +2. `snapshot` - 'raw' snapshot from the result + +### set + +```tsx +const setReq = client + .createRequest()({ + endpoint: ":teaId", + method: "set", + }) + .setParams({ teaId: 1 }) + .setData(newData); + +const { data, status, extra, success, error } = await req.send(); +``` + +In case of `set` - data returned is the same data as passed for setting. + +`extra`: + +1. ref - database reference to the path + +You can also **remove data** via set by sending the `{data: null}` object: + +```tsx +const setReq = client + .createRequest()({ + endpoint: ":teaId", + method: "set", + }) + .setParams({ teaId: 1 }) + .setData({ data: null }); +``` + +### push + +```tsx +const pushReq = client + .createRequest()({ + endpoint: "", + method: "push", + options: {}, + }) + .setData(newData); +``` + +In case of `push` - data returned is the same data as passed for setting + the `__key` param that equals id of a newly +created document. + +`extra`: + +1. ref - database reference to the path +2. key - key of the newly created resource + +### update + +```tsx +const updateReq = client + .createRequest()({ + endpoint: ":teaId", + method: "update", + }) + .setData(newData); +``` + +In case of `update` - data returned is the same data as passed for setting. + +`extra`: + +1. ref - database reference to the path + +### remove + +```tsx +const removeReq = client + .createRequest()({ + endpoint: ":teaId", + method: "remove", + }) + .setParams({ teaId: 1 }); +``` + +In case of `remove` - returned data equals `null` diff --git a/documentation/docs/integrations/adapter-firebase/_category_.json b/documentation/docs/integrations/adapter-firebase/_category_.json new file mode 100644 index 000000000..ee818d748 --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Firebase Admin" +} diff --git a/documentation/docs/integrations/adapter-firebase/firestore.mdx b/documentation/docs/integrations/adapter-firebase/firestore.mdx new file mode 100644 index 000000000..c4cd7148a --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase/firestore.mdx @@ -0,0 +1,109 @@ +--- +sidebar_position: 2 +title: Working with Firestore +sidebar_label: Firestore +--- + +## Available methods + +After setting the firestore adapter, we can start performing requests! We should select the appropriate method, +corresponding with firebase methods. If you want to learn more about available methods - please refer to the firebase +documentation. + +Due to its nature, we've solved the realtime listening a bit differently, and thus - this method is not allowed in a +standard adapter. For the `onSnapshot`-like usage, please check how to +**[listen to queries](/docs/integrations/adapter-firebase/realtime-queries)**. + +```tsx +const getReq = client.createRequest()({ + endpoint: "", + method: "getDocs", // "addDoc" | "getDoc" | "getDocs" | "setDoc" | "updateDoc" | "deleteDoc" +}); +``` + +### getDocs + +```tsx +const getReq = client.createRequest()({ + endpoint: "", + method: "getDocs", +}); + +const { data, status, extra, success, error } = await req.send(); +// Data is an array of objects, each object also contains the __key param. +``` + +`extra`: + +1. `ref` - collection reference endpoint +2. `snapshot` - 'raw' `getDocs` firestore collection/query reference snapshot + +### getDoc + +```tsx +const req = client + .createRequest()({ + endpoint: ":teaId", + method: "getDoc", + }) + .setParams({ teaId: 1 }); + +const { data, status, extra, success, error } = await req.send(); +// setParams can be also passed in the send() method instead +``` + +`extra`: + +1. `ref` - document reference endpoint +2. `snapshot` - 'raw' firestore document reference snapshot + +### setDoc + +```tsx +const newData = { origin: "Poland", type: "Green", year: 2023, name: "Pou Ran Do Cha", amount: 10 }; +const setReq = client + .createRequest()({ + endpoint: ":teaId", + method: "setDoc", + // can also pass options: {merge: true} for achieving the firebase 'merge' option + }) + .setParams({ teaId: 1 }) + .setData(newData); + +await setReq.send(); +const { data } = await getReq.send(); +``` + +In case of `setDoc` - data returned is the same data as passed for setting. + +### addDoc + +```tsx +const addDocReq = client + .createRequest()({ + endpoint: "", + method: "addDoc", + options: {}, + }) + .setData(newData); + +await addDocReq.send(); +``` + +In case of `addDoc` - data returned is the same as passed data for setting + the `__key` param that is id of a newly +created document. + +### deleteDoc + +```tsx +const deleteDocReq = client + .createRequest()({ + endpoint: ":teaId", + method: "deleteDoc", + }) + .setParams({ teaId: 1 }); + +await deleteDocReq; +``` + +In case of `deleteDoc` - returned data equals `null` diff --git a/documentation/docs/integrations/adapter-firebase/overview.mdx b/documentation/docs/integrations/adapter-firebase/overview.mdx new file mode 100644 index 000000000..f27b32482 --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase/overview.mdx @@ -0,0 +1,93 @@ +--- +sidebar_position: 1 +title: Firebase Adapter +sidebar_label: Overview +--- + +> Hyper Fetch `firebase` packages offer complete integration of `realtime database` and `firestore`, both for frontend +> and backend, with a single, unified approach for all of them. + +## Getting Started + +In order to start using the firebase adapter, you have to initialize the firestore/realtime database and set the correct +adapter on the `client`. + +There are two packages to chose from with two adapters each: + +1. `@hyper-fetch/firebase` - for working with web realtime and firestore databases: + 1. `firebaseAdapter` - for standard REST requests. + 2. `firebaseSocketsAdapter` - for realtime listening. +2. `@hyper-fetch/firebase-admin` - for admin versions of the packages: + 1. `firebaseAdminAdapter` - for standard REST requests. + 2. `firebaseSocketsAdminAdapter` - for realtime listening. + +Both packages have the same unified interface. + +```tsx +import { firebaseAdapter } from "@hyper-fetch/firebase"; +import { initializeApp } from "firebase/app"; +import { getFirestore } from "firebase/firestore"; + +// Firebase firestore database initialization +const app = initializeApp({ + projectId: "demo-test-firestore", +}); +const db = getFirestore(app); + +// Setting up the HyperFetch with a firebase adapter +const client = new Client({ url: "teas/" }).setAdapter(() => firebaseAdapter(db)); +const getReq = client.createRequest()({ + endpoint: "", + method: "getDocs", +}); + +// Checking the results +const { data, status, extra, success, error } = await getReq.send(); +``` + +In case of firebase admin, the only difference is the import: + +```tsx +import { firebaseAdminAdapter } from "@hyper-fetch/firebase-admin"; +... +``` + +Resulting `response` is an object that contains the following properties: + +1. `data` - data returned from the database. Each object returned from the database is enhanced also by the `__key` + param that equals id of a given document. +2. `status` - status indicating whether a request ended with `success`, `error` or `emptyResource`. `emptyResource` + occurs when the request to firebase succeeded but no data was returned. +3. `success` - general information if overall request succeeded (`true`) or failed (`false`) +4. `error` - contains error object if the request failed. +5. `extra` - contains additional properties, depending on a method used. For instance, for `getDocs` method - it allows + to access `ref` and `snapshot` from firestore firebase. + +## Differences from 'raw' firebase + +1. If we store an array in firebase, for instance `[a, b, c]`, the query would return the same array. However, if the + array stops being sequential - for instance, if `a` and `b` were deleted, firebase would return and object: + `{2: 'c', 4: 'e'}`. Hyper Fetch always returns and array. Each object in an array has an additional `__key` property, + that indicates the id of a given object. + +## Filtering queries + +In order to standardize the interface across both admin/web firestore/realtime, the filtering and limiting queries is +done via setting the `constraints` queryParam: + +```tsx +import { $limit, $orderBy, $where } from "constraints"; + +const req = client.createRequest()({ + endpoint: "", + method: "getDocs", +}); // or via setQueryParams method +const { data } = await req.send({ + queryParams: { constraints: [$where("type", "==", "Green"), $orderBy("year"), $limit(1)] }, +}); +``` + +User can pass the array of constrains and filters. Please pay attention to the fact that you need to filter via method +wrappers provided via `adapter-firebase` package: `$where`, `$orderBy`, `$limit`, `$startAt`, `$startAfter`, `$endAt`, +`$endAfter`, `$orderByChild`, `$orderByKey`, `$orderByValue`, `$limitToFirst`, `$limitToLast`, `$equalTo`. All of these +methods work exactly the same as their corresponding firebase equivalents. diff --git a/documentation/docs/integrations/adapter-firebase/realtime-queries.mdx b/documentation/docs/integrations/adapter-firebase/realtime-queries.mdx new file mode 100644 index 000000000..de398a566 --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase/realtime-queries.mdx @@ -0,0 +1,79 @@ +--- +sidebar_position: 4 +title: Realtime listening +sidebar_label: Realtime listening +--- + +## Listening to queries - realtime updates + +Depending on the product, firebase offers two realtime methods: + - `onValue` for Realtime Database + - `onSnapshot` for Firestore + +In Hyper Fetch, in order to make use of these two methods, we have to use our `hyper-fetch/sockets` package. + +```tsx +import {Socket} from "@hyper-fetch/sockets"; +import {firebaseSocketsAdapter} from "@hyper-fetch/firebase"; + +const firestoreWebDatabase = +const socket = new Socket({ url: "teas/", adapter: firebaseSocketsAdapter(firestoreWebDatabase) }); +const onSnapshotRequest = socket.createListener()({ + name: "", + options: { groupByChangeType: true }, +}); +const unmount = await onValueReq.listen({ callback: ({data, extra} => { + // do something with data and extra received each time socket gets new data +})}); +``` + +As you can see in the presented example: +1. First we need to initialize the socket itself. Here we need to pass an adapter: + - `firebaseSocketsAdapter` - for web version of Realtime Database and Firestore - from the `@hyper-fetch/firebase` package. + - `firebaseSocketsAdminAdapter` - for admin version of both databases - from the `@hyper-fetch/-firebase-admin` package. + +The adapter accepts initialized database. + +2. We need to create a `listener` - here we can pass the additional options, params, etc. + +3. We need to actually listen on the request and provide a callback that will fire each time new data arrives. In case of our firebase adapter, callback +can access two params: +- `data` - all the received elements. +- `extra` - additional data: + - `ref` - firebase reference to a path + - `snapshot` - firebase raw snapshot + - `status` - status indicating whether a query succeeded or failed + +Listener initialization returns an `unmount` function that allows for closing the channel. + +## Additional options + +Right now we have only one option for each database type: + +### Firestore + +1. `groupByChangeType` - if we add this option, the `extra` object will have additional object named `groupedResult` +```tsx +const {added, modified, removed} = extra.groupedResult +``` +Let's suppose we are listening on the `teas/` endpoint. Initially, we receive objects. Then, after some time, a new element +is added to the list. In the `data` object we receive all the existing elements - 11. However, the new element will be also available via +`extra.groupedResult.added` list. The same goes for modification of existing elements. All changes are reflected in `added`, `modified`, and `removed`. + +### Realtime + +1. `onlyOnce` - if we add this option, listener will query for data only once. + +## Filtering realtime queries + +You can filter realtime queries exactly in the same way as with 'standard' methods. The example below contains a listener that will return data only for +the object with `type` that equals `Green`: + +```tsx +// Should listen for changes only for Green teas +const onSnapshotReq = socket.createListener()({ + name: "", + options: { constraints: [$where("type", "==", "Green")] }, +}); + +``` diff --git a/documentation/docs/integrations/adapter-firebase/realtime.mdx b/documentation/docs/integrations/adapter-firebase/realtime.mdx new file mode 100644 index 000000000..f0e8149a7 --- /dev/null +++ b/documentation/docs/integrations/adapter-firebase/realtime.mdx @@ -0,0 +1,112 @@ +--- +sidebar_position: 3 +title: Working with Realtime database +sidebar_label: Realtime Database +--- + +## Available methods + +Our Realtime Database adapter provides all the methods the original offers. Due to its nature, we've solved the realtime +listening a bit differently, and thus - this method is not allowed in standard adapter. For the `onValue`-like usage, +please check how to **[listen to queries](/docs/integrations/adapter-firebase/realtime-queries)**. + +### get + +```tsx +import { firebaseAdapter } from "@hyper-fetch/firebase"; + +const client = new Client({ url: "teas/" }).setAdapter(() => firebaseAdapter(realtimeDbWeb)); +const req = client.createRequest()({ + endpoint: "", + method: "get", +}); + +const { data, status, extra, success, error } = await req.send(); +``` + +`extra`: + +1. `ref` - reference to the endpoint +2. `snapshot` - 'raw' snapshot from the result + +### set + +```tsx +const setReq = client + .createRequest()({ + endpoint: ":teaId", + method: "set", + }) + .setParams({ teaId: 1 }) + .setData(newData); + +const { data, status, extra, success, error } = await req.send(); +``` + +In case of `set` - data returned is the same data as passed for setting. + +`extra`: + +1. ref - database reference to the path + +You can also **remove data** via set by sending the `{data: null}` object: + +```tsx +const setReq = client + .createRequest()({ + endpoint: ":teaId", + method: "set", + }) + .setParams({ teaId: 1 }) + .setData({ data: null }); +``` + +### push + +```tsx +const pushReq = client + .createRequest()({ + endpoint: "", + method: "push", + options: {}, + }) + .setData(newData); +``` + +In case of `push` - data returned is the same data as passed for setting + the `__key` param that equals id of a newly +created document. + +`extra`: + +1. ref - database reference to the path +2. key - key of the newly created resource + +### update + +```tsx +const updateReq = client + .createRequest()({ + endpoint: ":teaId", + method: "update", + }) + .setData(newData); +``` + +In case of `update` - data returned is the same data as passed for setting. + +`extra`: + +1. ref - database reference to the path + +### remove + +```tsx +const removeReq = client + .createRequest()({ + endpoint: ":teaId", + method: "remove", + }) + .setParams({ teaId: 1 }); +``` + +In case of `remove` - returned data equals `null` diff --git a/documentation/docs/documentation/05-adapters/graphql/_category_.json b/documentation/docs/integrations/adapter-graphql/_category_.json similarity index 100% rename from documentation/docs/documentation/05-adapters/graphql/_category_.json rename to documentation/docs/integrations/adapter-graphql/_category_.json diff --git a/documentation/docs/integrations/adapter-graphql/graphql-tag.mdx b/documentation/docs/integrations/adapter-graphql/graphql-tag.mdx new file mode 100644 index 000000000..b43ecab18 --- /dev/null +++ b/documentation/docs/integrations/adapter-graphql/graphql-tag.mdx @@ -0,0 +1,51 @@ +--- +sidebar_position: 4 +title: Using graphql-tag +sidebar_label: graphql-tag +--- + +The `graphql-tag` library is a peer dependency of the GraphQL adapter and is used to parse GraphQL query strings into +the standard AST format. + +:::tip Why use `graphql-tag`? + +- **Standardization**: It ensures that your GraphQL queries are valid and conform to the official specification. +- **Tooling Support**: It enables better tooling support, such as syntax highlighting and autocompletion in your editor. +- **Readability**: It makes your queries more readable by allowing you to write them as multi-line strings. + +::: + +--- + +## Usage + +You should use the `gql` tag from `graphql-tag` to define all your queries and mutations. The GraphQL adapter will then +automatically handle the conversion to a printable string that can be sent to your server. + +```ts +import { Client } from "@hyper-fetch/core"; +import { graphqlAdapter } from "@hyper-fetch/graphql"; +import gql from "graphql-tag"; + +const client = new Client({ url: "http://localhost:3000/graphql" }).setAdapter(graphqlAdapter); + +interface User { + user: { + username: string; + firstName: string; + }; +} + +const getUser = client.createRequest()({ + endpoint: gql` + query GetUser { + user { + username + firstName + } + } + `, +}); + +const { data, error } = await getUser.send(); +``` diff --git a/documentation/docs/integrations/adapter-graphql/mutations.mdx b/documentation/docs/integrations/adapter-graphql/mutations.mdx new file mode 100644 index 000000000..70cc2cb04 --- /dev/null +++ b/documentation/docs/integrations/adapter-graphql/mutations.mdx @@ -0,0 +1,67 @@ +--- +sidebar_position: 3 +title: Working with Mutations +sidebar_label: Mutations +--- + +Mutations are used to modify data on your GraphQL server. This guide will show you how to create and execute mutations +using Hyper Fetch. + +:::tip What you'll learn + +- How to create a GraphQL mutation. +- How to pass variables to a mutation. +- How to handle mutation responses. + +::: + +--- + +## Basic Mutation + +Here is a basic example of how to create a mutation to log in a user. + +```tsx +import { Client } from "@hyper-fetch/core"; +import { graphqlAdapter } from "@hyper-fetch/graphql"; +import gql from "graphql-tag"; + +// 1. Initialize the client +const client = new Client({ url: "http://localhost:3000/graphql" }).setAdapter(graphqlAdapter); + +// 2. Define types for the variables +type Variables = { + username: string; + password: string; +}; + +// 3. Create the mutation request +const login = client.createRequest()({ + endpoint: gql` + mutation Login($username: String!, $password: String!) { + login(username: $username, password: $password) + } + `, +}); + +// 4. Send the mutation with variables +const { data, error } = await login.send({ + data: { + variables: { + username: "Some username", + password: "Some password", + }, + }, +}); +``` + +Similar to queries, you can also use the `.setData()` method to provide variables for your mutation. + +```ts +const { data, error } = await login + .setData({ + username: "Some username", + password: "Some password", + }) + .send(); +``` diff --git a/documentation/docs/integrations/adapter-graphql/overview.mdx b/documentation/docs/integrations/adapter-graphql/overview.mdx new file mode 100644 index 000000000..ab0f5c2b5 --- /dev/null +++ b/documentation/docs/integrations/adapter-graphql/overview.mdx @@ -0,0 +1,158 @@ +--- +sidebar_position: 1 +title: GraphQL Adapter +sidebar_label: Overview +--- + +> The Hyper Fetch GraphQL Adapter provides a seamless way to integrate GraphQL with Hyper Fetch, enabling you to make +> queries and mutations with ease. + +:::tip Purpose + +1. **Simplify GraphQL integration** in both server and browser environments. +2. **Provide a type-safe way** to handle GraphQL operations. +3. **Offer seamless integration** with the Hyper Fetch ecosystem. +4. **Support both queries and mutations** with a consistent API. + +::: + +--- + +## Installation + +To get started, you need to install the `@hyper-fetch/graphql` package. You will also need `graphql` and `graphql-tag` +which are peer dependencies. + +```bash +npm install @hyper-fetch/graphql graphql graphql-tag +``` + +--- + +## Getting Started + +Whether you're developing a server-side GraphQL API or a client-side application that consumes GraphQL data, you can use +Hyper Fetch GraphQL to interact with it. The setup process is simple and straightforward. + +```tsx +import { Client } from "@hyper-fetch/core"; +import { graphqlAdapter } from "@hyper-fetch/graphql"; +import gql from "graphql-tag"; + +// 1. Create a client +const client = new Client({ url: "http://localhost:3000/graphql" }).setAdapter(graphqlAdapter); + +// 2. Define an interface for your data +interface User { + username: { + username: string; + firstName: string; + }; +} + +// 3. Create a request +const getUser = client.createRequest()({ + endpoint: gql` + query GetUser { + user { + username + firstName + } + } + `, +}); + +// 4. Send the request and get the response +const { data, error } = await getUser.send(); +``` + +--- + +## Queries + +To perform a query, create a request with the `gql` tag and your GraphQL query string. + +```ts +import { Client } from "@hyper-fetch/core"; +import { graphqlAdapter } from "@hyper-fetch/graphql"; +import gql from "graphql-tag"; + +const client = new Client({ url: "http://localhost:3000/graphql" }).setAdapter(graphqlAdapter); + +const getUsers = client.createRequest()({ + endpoint: gql` + query GetUsers { + users { + id + name + } + } + `, +}); +``` + +--- + +## Mutations + +Mutations work just like queries. Define your mutation string and use it in a request. + +```ts +import { Client } from "@hyper-fetch/core"; +import { graphqlAdapter } from "@hyper-fetch/graphql"; +import gql from "graphql-tag"; + +const client = new Client({ url: "http://localhost:3000/graphql" }).setAdapter(graphqlAdapter); + +const addUser = client.createRequest()({ + endpoint: gql` + mutation AddUser($name: String!) { + addUser(name: $name) { + id + name + } + } + `, +}); +``` + +--- + +## Variables + +You can pass variables to your queries and mutations using the `data` option in the `send` method. + +```ts +const { data, error } = await addUser.send({ + data: { + variables: { name: "John Doe" }, + }, +}); +``` + +This will pass the `name` variable to the `AddUser` mutation. + +--- + +## Next Steps + +Now that you have a basic understanding of the GraphQL adapter, you can explore more advanced topics: + + + + diff --git a/documentation/docs/integrations/adapter-graphql/queries.mdx b/documentation/docs/integrations/adapter-graphql/queries.mdx new file mode 100644 index 000000000..f3cf0dee7 --- /dev/null +++ b/documentation/docs/integrations/adapter-graphql/queries.mdx @@ -0,0 +1,104 @@ +--- +sidebar_position: 2 +title: Working with Queries +sidebar_label: Queries +--- + +Queries are used to fetch data from your GraphQL API. With Hyper Fetch, you can define your queries using `graphql-tag` +and execute them with the configured client. + +:::tip What you'll learn + +- How to create a basic GraphQL query. +- How to use TypeScript with your queries. +- How to pass variables to your queries. + +::: + +--- + +## Basic Query + +Here's how to create a simple query to fetch a user's data. + +```tsx +import { Client } from "@hyper-fetch/core"; +import { graphqlAdapter } from "@hyper-fetch/graphql"; +import gql from "graphql-tag"; + +// 1. Initialize Client with adapter +const client = new Client({ url: "http://localhost:3000/graphql" }).setAdapter(graphqlAdapter); + +// 2. Define an interface for your data +interface User { + user: { + username: string; + firstName: string; + }; +} + +// 3. Create a request +const getUser = client.createRequest()({ + endpoint: gql` + query GetUser { + user { + username + firstName + } + } + `, +}); + +// 4. Send the request +const { data, error } = await getUser.send(); +``` + +--- + +## Query Variables + +You often need to pass variables to your queries to filter results or fetch specific data. You can do this by defining +variables in your GraphQL query and passing them in the `send` method. + +```tsx +import { Client } from "@hyper-fetch/core"; +import { graphqlAdapter } from "@hyper-fetch/graphql"; +import gql from "graphql-tag"; + +const client = new Client({ url: "http://localhost:3000/graphql" }).setAdapter(graphqlAdapter); + +// 1. Define types for the response and variables +type User = { + username: string; + firstName: string; +}; +type Variables = { + userId: string; +}; + +// 2. Create the request with variables in the query +const getUser = client.createRequest<{ user: User }, Variables>()({ + endpoint: gql` + query GetUserById($userId: ID!) { + user(id: $userId) { + username + firstName + } + } + `, +}); + +// 3. Pass variables when sending the request +const { data, error } = await getUser.send({ + data: { + variables: { userId: "1" }, + }, +}); +``` + +You can also use the `.setData()` method to set variables before sending the request. This is useful when you want to +separate setting the data from sending the request. + +```ts +const { data, error } = await getUser.setData({ userId: "1" }).send(); +``` diff --git a/documentation/docs/documentation/06-generators/openapi/_category_.json b/documentation/docs/integrations/codegen-openapi/_category_.json similarity index 100% rename from documentation/docs/documentation/06-generators/openapi/_category_.json rename to documentation/docs/integrations/codegen-openapi/_category_.json diff --git a/documentation/docs/integrations/codegen-openapi/overview.mdx b/documentation/docs/integrations/codegen-openapi/overview.mdx new file mode 100644 index 000000000..d565706c4 --- /dev/null +++ b/documentation/docs/integrations/codegen-openapi/overview.mdx @@ -0,0 +1,126 @@ +--- +sidebar_position: 1 +title: OpenAPI Codegen +sidebar_label: Overview +--- + +[Read the API Reference »](/docs/api/codegen-openapi/Classes/OpenapiRequestGenerator.mdx) + +This guide will walk you through using the Hyper Fetch OpenAPI code generator. This powerful tool automatically creates +type-safe Hyper Fetch clients, requests, and data models directly from your OpenAPI V3 schema. It streamlines the +process of integrating with REST APIs, saving you time and reducing boilerplate code. + +:::success Swagger to Hyper Fetch + +This tool is a great way to get started with Hyper Fetch. It will generate a client, requests and types from your API. + +::: + +--- + +:::tip Purpose + +By using the OpenAPI code generator, you can: + +1. **Automatically generate** a ready-to-use Hyper-Fetch client. +2. **Create typed requests** for all your API endpoints. +3. **Define data models** based on your OpenAPI schema components. +4. **Boost productivity** by eliminating manual setup for API integration. + +::: + +:::caution Supported Version + +Please note that the code generator currently supports **OpenAPI V3 schemas only**. Support for other versions is +planned for the future. + +::: + +--- + +## Getting Started + +To start using the request generator, all you need is an OpenAPI V3 JSON schema, available either as a local file or a +remote URL. You can run the generator using `npx`: + +```bash +npx @hyper-fetch/codegen-openapi --schema https://petstore3.swagger.io/api/v3/openapi.json +``` + +This command processes the schema and generates a `openapi.client.ts` file in your current directory. This file contains +a pre-configured Hyper Fetch client, typed request functions, and all the necessary TypeScript types for your API. + +Here's a snippet of what the generated file looks like: + +```typescript +// ... (imports and namespace definitions) + +export const client = new Client({ url: "/api/v3" }); + +// ... (type definitions for schemas) +export type UpdatePetRequestBody = Paths.UpdatePet.RequestBody; +export type UpdatePetResponseType = Paths.UpdatePet.Responses.$200; + +export type AddPetRequestBody = Paths.AddPet.RequestBody; +export type AddPetResponseType = Paths.AddPet.Responses.$200; + +// ... (request definitions) + +export const updatePet = client.createRequest()({ + method: "PUT", + endpoint: "/pet", +}); + +export const addPet = client.createRequest()({ + method: "POST", + endpoint: "/pet", +}); +``` + +### Naming Convention + +The generator follows a consistent naming convention based on the `operationId` from your OpenAPI schema: + +- **Request Function**: The function name is the camel-cased `operationId`. For example, `addPet`. +- **Type Definitions**: Types are named by combining the pascal-cased `operationId` with a suffix indicating the type of + data (`ResponseType`, `RequestBody`, `QueryParams`, etc.). For example, `AddPetResponseType` or `AddPetRequestBody`. + +--- + +## Configuration + +You can customize the generator's output with several command-line options. + +### Output File Name + +By default, the output file is named `openapi.client.ts`. You can specify a different name using the `--name` option: + +```bash +npx @hyper-fetch/codegen-openapi --schema --name hyper-fetch.requests.ts +``` + +### Base URL + +The generator attempts to extract the base URL from the `servers` field in your OpenAPI schema: + +```json +"servers": [ + { + "url": "/api/v3" + } +] +``` + +If you need to override this or if the `servers` field is not present, you can provide a base URL with the `--url` +option: + +```bash +npx @hyper-fetch/codegen-openapi --schema --url https://api.example.com +``` + +This will configure the generated client with the specified URL: + +```typescript +// ... generated file +export const client = new Client({ url: "https://api.example.com" }); +``` diff --git a/documentation/versioned_docs/version-4.x.x/documentation/01-getting-started/_category_.json b/documentation/docs/integrations/getting-started/_category_.json similarity index 100% rename from documentation/versioned_docs/version-4.x.x/documentation/01-getting-started/_category_.json rename to documentation/docs/integrations/getting-started/_category_.json diff --git a/documentation/docs/integrations/getting-started/community.mdx b/documentation/docs/integrations/getting-started/community.mdx new file mode 100644 index 000000000..9929cbdb8 --- /dev/null +++ b/documentation/docs/integrations/getting-started/community.mdx @@ -0,0 +1,3 @@ +# Community + +Add your own packages and integrations. diff --git a/documentation/docs/integrations/getting-started/index.mdx b/documentation/docs/integrations/getting-started/index.mdx new file mode 100644 index 000000000..f7b83ff2d --- /dev/null +++ b/documentation/docs/integrations/getting-started/index.mdx @@ -0,0 +1,9 @@ +--- +sidebar_position: 1 +title: Getting Started +sidebar_label: Getting Started +--- + +import { Integrations } from "./integrations/index"; + + diff --git a/documentation/docs/integrations/getting-started/integrations/card/card.tsx b/documentation/docs/integrations/getting-started/integrations/card/card.tsx new file mode 100644 index 000000000..a7232fb2e --- /dev/null +++ b/documentation/docs/integrations/getting-started/integrations/card/card.tsx @@ -0,0 +1,54 @@ +import Link from "@docusaurus/Link"; +import { Description, DocsCard, Noise, Title } from "@site/src/components"; +import { useSidebar } from "@site/src/hooks/use-sidebar"; +import { Section } from "@site/src/modules"; +import clsx from "clsx"; + +export type CardProps = { + section: Section; + className?: string; +}; + +export function IntegrationCard({ section, className }: CardProps) { + const { sidebar } = useSidebar(); + + const item = sidebar.find((element) => element.section.label === section.label); + + return ( + + + +
+ {item.section.img && ( +
+ + {item.section.featured && ( + + )} +
+ )} + + {item.name} + +
+
+ + {item.description} + +
+
+ + ); +} diff --git a/documentation/docs/integrations/getting-started/integrations/index.ts b/documentation/docs/integrations/getting-started/integrations/index.ts new file mode 100644 index 000000000..5eb5b72b0 --- /dev/null +++ b/documentation/docs/integrations/getting-started/integrations/index.ts @@ -0,0 +1 @@ +export * from "./integrations"; diff --git a/documentation/docs/integrations/getting-started/integrations/integrations.tsx b/documentation/docs/integrations/getting-started/integrations/integrations.tsx new file mode 100644 index 000000000..f985ecefb --- /dev/null +++ b/documentation/docs/integrations/getting-started/integrations/integrations.tsx @@ -0,0 +1,89 @@ +import { Description, DocsCard, Particles, Title } from "@site/src/components"; +import PageIllustration02 from "@site/static/img/page-illustration-02.svg"; + +import { IntegrationsList } from "./list/list"; + +const styles = ` +.theme-doc-breadcrumbs, article header, .theme-doc-version-badge { + display: none!important; +} +main > div > .row > .col { + width: 100%!important; + max-width: none!important; +} + + +`; + +export const Integrations = () => { + return ( +
+ +
+ {/* Graphic 02 */} + + + {/* Opacity layer */} +