diff --git a/.vale.ini b/.vale.ini index d3a8f06b6..a980d22dc 100644 --- a/.vale.ini +++ b/.vale.ini @@ -12,4 +12,4 @@ IgnoredScopes = script, code, tt, pre, figure, blockquote BasedOnStyles = Vale, write-good, alex, Readability -write-good.E-Prime = NO \ No newline at end of file +write-good.E-Prime = NO diff --git a/.vale/styles/Vocab/Blog/accept.txt b/.vale/styles/Vocab/Blog/accept.txt index 95bbe8241..c9169dd3e 100644 --- a/.vale/styles/Vocab/Blog/accept.txt +++ b/.vale/styles/Vocab/Blog/accept.txt @@ -17,4 +17,24 @@ Todo Baz Hasura fets -createClient \ No newline at end of file +createClient +tuvalSimha +Spotify +topTracks +openapi +OpenAPI +api +API +TuvalSimha +time_range +short_term +async +spotify +feTS +Sportstats +openApi +json +useQuery +isLoading +useState +selectedClub \ No newline at end of file diff --git a/website/blogs.json b/website/blogs.json index 07070f861..34878c640 100644 --- a/website/blogs.json +++ b/website/blogs.json @@ -1,7 +1,17 @@ [ + { + "title": "Empowering REST API Dev with feTS", + "description": "feTS - type safety, efficiency & seamless TypeScript/OpenAPI", + "tags": ["typescript", "openapi", "rest", "api", "feTS"], + "authors": ["tuvalSimha"], + "link": "/blog/type-safe-your-api", + "image": "/blog-assets/type-safe-your-api/banner.png", + "date": "2023-08-14", + "thumbnail": "/blog-assets/type-safe-your-api/banner.png" + }, { "title": "Hive Summer Update 2023", - "description": "Learn what is new on GraphQL Hive. Learn what is new on GraphQL Hive. Learn what is new on GraphQL Hive.", + "description": "Learn what is new on GraphQL Hive, we have shipped a lot of new exciting features and improvements.", "tags": ["graphql", "graphql-hive"], "authors": ["laurin", "kamil", "dimitri", "dotan"], "link": "/blog/hive-summer-update-2023", @@ -208,16 +218,6 @@ "date": "2022-12-06", "thumbnail": "/blog-assets/graphql-yoga-nest-v9/thumbnail.png" }, - { - "title": "GraphQL File Uploads with S3", - "description": "Learn how to upload and do File Uploads with GraphQL and S3.", - "tags": ["graphql", "best-practices"], - "authors": ["laurin"], - "link": "/blog/graphql-file-uploads-with-s3", - "image": "/blog-assets/x", - "date": "2022-11-17", - "thumbnail": "/blog-assets/x" - }, { "title": "Announcing GraphQL Yoga v3", "description": "The new version of GraphQL Yoga is out! Learn what has improved and what is new!", diff --git a/website/pages/blog/type-safe-your-api.mdx b/website/pages/blog/type-safe-your-api.mdx new file mode 100644 index 000000000..d4a30c5d0 --- /dev/null +++ b/website/pages/blog/type-safe-your-api.mdx @@ -0,0 +1,290 @@ +--- +title: feTS - Enhancing Type Safety in REST API +authors: tuvalSimha +date: 2023-08-14 +tags: [typescript, openapi, rest, api, feTS] +description: feTS - type safety, efficiency & seamless TypeScript/OpenAPI +thumbnail: /blog-assets/type-safe-your-api/banner.png +image: /blog-assets/type-safe-your-api/banner.png +--- + +## Introduction + +About two years ago, I began my journey to become a developer, driven by a strong passion for +learning and creating. As a junior developer, I was eager to find a project that would help me grow +my skills while being fun and interesting. + +For my project, I needed to use an REST API, to be specific. This API allows me to fetch data from a +server and use it in my application. It supports operations like reading (GET), updating (PUT), +creating (POST), and deleting (DELETE) various types of data. + +Working with REST APIs can be tricky due to issues like the lack of type safety, which can lead to +errors and bugs during development. In this blog post, I'll introduce you to `feTS`, a solution that +simplifies REST API development. `feTS` help with these challenges, making API development more +reliable and straight forward. + +## Introducing `feTS`: Type-Safe REST API Development + +`feTS`, which stands for 'Fetch' and 'TypeScript,' is a modern technology that enhances REST APIs. +By using TypeScript and the OpenAPI specification, feTS ensures that data exchanged between the +client and server is clearly defined and error-free. This proactive approach identifies and prevents +errors during development, making your applications more dependable and robust. + +## A Practical Example: Leveraging `feTS` for Soccer Team Information + +To illustrate the power of `feTS`, let's dive into a real-world example. We'll build a demo app that +interacts with the +[SportStats](https://sportsdata.io/developers/api-documentation/soccer#/sports-data-feeds) V4 API +using `feTS`. Our goal is to fetch soccer team information and display it in an intuitive user +interface. + +### Step 1: Obtain a Free Trial Token for Sportstats + +To get started, create a free trial Token for SportStats API access. The registration process is +straight forward and won't take much time. You can find the registration form +[here](https://sportsdata.io/cart/free-trial). + +By the way, you can choose any other API you want, but for this example, we will use the SportStats +API. You can find openApi at `APIS.GURU` [website](https://apis.guru/about/) + +### Step 2: Incorporate OpenAPI Definitions into TypeScript + +To integrate OpenAPI definitions into a TypeScript file and export them using the `as const` +modifier. This process ensures strong typing and easy integration with `feTS`. A sample code snippet +is provided below: + +```typescript filename="openapi.ts" +export default { openapi: '3.0.0' /* ... */ } as const +``` + +`openapi.ts` GitHub file +[here](https://github.com/TuvalSimha/feTS-example/blob/main/src/fets/openapi.ts). You can choose any +OpenAPI file you want, but for this example, we will use the SportStats OpenAPI file. You can find +the OpenAPI file [here](https://sportsdata.io/developers/sports-data-open-api-swagger-files). + +### Step 3: Install `feTS` and Create a Client + +Now, we are ready to install `feTS` and create a new client. To do this, you can visit the docs +[here](https://the-guild.dev/openapi/fets/client/quick-start#installation). + +Next, create a new `TypeScript` file and import the `createClient` function from the `feTS` package. +Then, use the `createClient` function to instantiate a new client. + +```typescript filename="client.ts" +import { createClient, type NormalizeOAS } from 'feTS' +import type openapi from './openapi' + +export const client = createClient>({ + endpoint: 'https://api.sportsdata.io/v4/soccer/stats' +}) +``` + +`client.ts` GitHub file +[here](https://github.com/TuvalSimha/feTS-example/blob/main/src/fets/client.ts). + +In the code below, we demonstrate how to set up the `feTS` client with a custom error handling +plugin. This plugin is added to enhance the error handling capabilities of the client. This +error-handling plugin is to provide a robust mechanism for handling HTTP errors that may occur +during API requests. When the server responds with an error status code, this plugin ensures that an +informative error message is thrown, including the status code, status text, and the URL that +triggered the error. + +```typescript filename="client.ts" +/* eslint-disable react-hooks/rules-of-hooks */ +import { ClientPlugin, createClient, type NormalizeOAS } from 'feTS' +import type openapi from './openapi' + +function useErrorHandler(): ClientPlugin { + return { + onResponse({ response }) { + if (!response.ok) { + throw new Error( + `Request failed with status ${response.status}, ${response.statusText}, ${response.url}` + ) + } + } + } +} + +export const client = createClient>({ + plugins: [useErrorHandler()], + endpoint: 'https://api.sportsdata.io/v4/soccer/stats' +}) +``` + +By using the custom plugins, you can add your own logic to the client. You can find more information +about plugins [here](https://the-guild.dev/openapi/fets/client/plugins). + +### Step 4: Use the Client to Fetch Data from the API + +Now, let's use our client to get data from the API. We'll use the `feTS` client we made earlier to +fetch information from the SportStats API. The fetchAllClubs function will show how `feTS` makes it +easy to request data from the API and deal with the results. + +```typescript filename="endpoint.ts" +export async function fetchAllClubs() { + const response = await client['/{format}/Teams/{competition}'].get({ + params: { + competition: 'EPL', + format: 'json' + }, + headers: { + 'Ocp-Apim-Subscription-Key': YOUR_TOKEN_HERE + }, + query: { + key: YOUR_TOKEN_HERE + } + }) + + const data = await response.json() + + return data +} +``` + +In this code snippet, we bring in the client object we made using `feTS`. The `fetchAllClubs` +function shows how to use this client to send a `GET` request to the SportStats API's +`/Teams/{competition}` endpoint. We provide the needed details like the competition and format as +well as the required authentication headers. Your IDE should automatically fill in these parameters +for you. + +![Endpoint auto complete](/blog-assets/type-safe-your-api/endpoint.png) + +![Headers auto complete](/blog-assets/type-safe-your-api/autocomplate.png) + +### Step 5: Use the Data from the API + +In this step, we'll demonstrate how to use the data fetched from the SportStats API using the +`fetchAllClubs` function. We'll utilize the `useQuery` hook from the `react-query` library to +efficiently manage data fetching and rendering in our React application. + +The `useQuery` hook from the react-query library provides a convenient way to fetch and manage +asynchronous data in a React component. It abstracts away the complex logic of data fetching, +caching, and error handling. + +Here's an example of how you can use `useQuery` to fetch and display data from the SportStats API in +your `App.tsx` component: + +```typescript filename="App.tsx" +import { useQuery } from 'react-query' +import { fetchAllClubs } from './feTS/endpoint' + +function App() { + const { data, isLoading, error } = useQuery('clubs', fetchAllClubs) + + if (isLoading) { + return
Loading...
+ } + + if (error) { + return
Error: {error.message}
+ } + + return ( +
+ {data?.map(club => ( +
{club.Name}
+ ))} +
+ ) +} + +export default App +``` + +For more advanced and design example, you can check out the +[example](https://github.com/TuvalSimha/feTS-example/blob/main/src/App.tsx) in the GitHub +repository. + +Lets create a design example for our soccer teams data: + +```typescript filename="App.tsx" +import { useState } from 'react' +import { useQuery } from 'react-query' +import { TeamsCards } from './components/teams-crads' +import { fetchAllClubs } from './feTS/endpoint' +import { Club } from './feTS/type-helpers' + +export function App() { + const [selectedClub, setSelectedClub] = useState(undefined) + + const { data, isLoading, error } = useQuery('clubs', fetchAllClubs) + + if (isLoading) { + return
Loading...
+ } + + if (error) { + return
Error: {error.message}
+ } + + return ( +
+ +
+ ) +} +``` + +In this code snippet, i created a custom component called `TeamsCards` (GitHub file +link)[https://github.com/TuvalSimha/feTS-example/blob/main/src/components/teams-crads.tsx] that +renders a list of cards with the club's name and logo. When the user clicks on a card, the +`setSelectedClub` function is called with the selected club as an argument. This allows us to +display the selected club's information in a separate component. + +In your code snippet, you've leveraged the power of `feTS` to define the Club type, which represents +a structured and strongly-typed representation of the data schema for a soccer club. This type is +essential for maintaining type safety and ensuring that the data exchanged between your application +and the API is well-defined and consistent. + +The `Club` type is inferred using `feTS` `OASModel` type.This type inference mechanism allows you to +generate TypeScript types directly from the OpenAPI schema defined in your `openapi` file. Here's +how you've done it: + +```typescript filename="type-helpers.ts" +import { NormalizeOAS, OASModel } from 'feTS' +import openapi from './openapi' + +export type Club = OASModel, 'Team'> +``` + +GitHub file [here](https://github.com/TuvalSimha/feTS-example/blob/main/src/fets/type-helpers.ts). + +![Club type](/blog-assets/type-safe-your-api/type.png) + +And its done! You've successfully created a React application that fetches data from the SportStats +API using `feTS`. + +![Result](/blog-assets/type-safe-your-api/result.gif) The full code for this example is available on +[GitHub](https://github.com/TuvalSimha/feTS-example) + +## Leveraging `feTS` for Enhanced API Development + +In this blog post, we've explored the transformative capabilities of `feTS` for REST API +development. By combining TypeScript's type safety and OpenAPI's structured definitions, `feTS` +empowers developers to build efficient, reliable, and well-structured applications. To recap, `feTS` +offers the following benefits: + +- Enhanced Type Safety: `feTS` provides robust type safety, catching potential issues at compile + time and reducing runtime errors. +- Improved Efficiency: With auto-generated API clients and TypeScript types, `feTS` eliminates + manual response parsing and improves overall development efficiency. +- Unified Interface: `feTS`'s unified and structured endpoint interface simplifies API navigation + and promotes collaboration among developers. +- Seamless Development: The `feTS` workflow seamlessly integrates with development processes, from + API interactions to UI rendering. + +## Embracing a Future of Seamless REST API Development + +As you integrate `feTS` into your projects, consider exploring its full potential. Experiment with +custom plugins, delve into advanced use cases, and stay engaged with the `feTS` community to +contribute to its evolution. + +In closing, `feTS` represents a beacon of innovation in the dynamic world of software development. +By adopting `feTS`, you're not only enhancing your REST API development process; you're embracing a +culture of continuous improvement and excellence. Whether you're building a personal project or a +large-scale application, `feTS` equips you with the tools to create exceptional, type-safe, and +efficient software. + +Thank you for joining us on this journey through [feTS](https://the-guild.dev/openapi/fets). Here's +to a future filled with seamless and elevated REST API development! diff --git a/website/public/blog-assets/type-safe-your-api/autocomplate.png b/website/public/blog-assets/type-safe-your-api/autocomplate.png new file mode 100644 index 000000000..4e4d26312 Binary files /dev/null and b/website/public/blog-assets/type-safe-your-api/autocomplate.png differ diff --git a/website/public/blog-assets/type-safe-your-api/banner.png b/website/public/blog-assets/type-safe-your-api/banner.png new file mode 100644 index 000000000..73852ed06 Binary files /dev/null and b/website/public/blog-assets/type-safe-your-api/banner.png differ diff --git a/website/public/blog-assets/type-safe-your-api/endpoint.png b/website/public/blog-assets/type-safe-your-api/endpoint.png new file mode 100644 index 000000000..63d39e694 Binary files /dev/null and b/website/public/blog-assets/type-safe-your-api/endpoint.png differ diff --git a/website/public/blog-assets/type-safe-your-api/result.gif b/website/public/blog-assets/type-safe-your-api/result.gif new file mode 100644 index 000000000..6f92a3291 Binary files /dev/null and b/website/public/blog-assets/type-safe-your-api/result.gif differ diff --git a/website/public/blog-assets/type-safe-your-api/type.png b/website/public/blog-assets/type-safe-your-api/type.png new file mode 100644 index 000000000..2c9f25dbe Binary files /dev/null and b/website/public/blog-assets/type-safe-your-api/type.png differ