{movie.overview}
-{game.name}
- {game.cover && } -{game.summary}
-{game.console}
-{response.message}
- ) : ( -{response.message}
- )) -} - - - - - -``` - -## Using Xata query - -The user profile page (`src/pages/[profile]/index.astro`) leverages the Xata Client to dynamically fetch and display all the photographs (not paginated) for a specific user profile. To engage users visually as soon as they open the gallery, we use the stored `imageHash` value as the background of the images (to be loaded). To prevent CLS, we use the stored `width` and `height` values to inform the browser of the expected dimensions of the images. - -```astro ---- -// File: src/pages/[profile]/index.astro - -// Import the Xata Client created by Xata CLI in src/xata.ts -import { getXataClient } from '@/xata' - -import Layout from '@/layouts/Layout.astro' - -// Get the profile slug from url path -const { profile } = Astro.params - -// Fetch the Xata instance -const xata = getXataClient() - -// Get all the photographs related to the profile -const profilePhotographs = await xata.db.photographs - // Filter the results to the specific profile - .filter({ 'profile-slug': profile }) - // Get all the photographs - .getAll() ---- - -{error.message}
- Documentation -{error.message}
- Documentation -` and ``), weāre able to selectively pass our own Svelte components containing their own processing logic. In this example, we create our custom `Code` svelte component to touch up the `` elementās visual appearance.
-
-```html
-
-
-
-```
-
-Using `svelte-highlight` package, weāll specifically highlight the code blocks in the chatbot responses to make them more visually appealing. Within just a few lines, weāre able to create HTML classes for each code block specifically based on its language (by default, weāve set it to `typescript`).
-
-```html
-
-
-
-```
-
-To make sure the specific HTML classes have their respective CSS styles present, we use the `GitHub Light Theme` (exported by `svelte-highlight`) in the appās parent [layout file](https://kit.svelte.dev/docs/advanced-routing#advanced-layouts-layout).
-
-```html
-
-
-
- {@html github}
-
-
-
-```
-
-Here's a preview of what we've successfully created with SvelteKit and Xata šš»
-
-![Conversational UI with Xata](/images/rag-sveltekit-xata/rag-sveltekit-xata-10.png)
-
-## Deploy to Vercel
-
-The repository, is now ready to deploy to Vercel. Use the following steps to deploy:
-
-- Start by creating a GitHub repository containing your app's code.
-- Then, navigate to the Vercel Dashboard and create a **New Project**.
-- Link the new project to the GitHub repository you just created.
-- In **Settings**, update the **Environment Variables** to match those in your local `.env` file.
-- Deploy! š
-
-## More Information
-
-For more detailed insights, explore the references cited in this post.
-
-| Resource | Link |
-| --------------------- | -------------------------------------------------------- |
-| GitHub Repo | https://github.com/rishi-raj-jain/xata-rag-chatbot |
-| SvelteKit with Xata | https://xata.io/docs/getting-started/sveltekit |
-| Xata Vector Search | https://xata.io/docs/sdk/vector-search |
-| How to Prompt Llama 2 | https://huggingface.co/blog/llama2#how-to-prompt-llama-2 |
-| SvelteKit AI SDK | https://sdk.vercel.ai/docs/guides/frameworks/sveltekit |
-
-## Whatās next?
-
-We'd love to hear from you if you have any feedback on this tutorial, would like to know more about Xata, or if you'd like to contribute a community blog or tutorial. Reach out to us on [Discord](https://discord.com/invite/kvAcQKh7vm) or join us on [X | Twitter](https://twitter.com/xata). Happy building š¦
diff --git a/searching-for-retro-games.mdx b/searching-for-retro-games.mdx
deleted file mode 100644
index 1c8605c8..00000000
--- a/searching-for-retro-games.mdx
+++ /dev/null
@@ -1,494 +0,0 @@
----
-title: 'Searching for retro games with Xata & Next.js 13'
-description: 'Creating a full-stack web app to search retro games data and prioritize highly-rated games in the results.'
-image:
- src: https://raw.githubusercontent.com/xataio/mdx-blog/main/images/search-retro-games.png
- alt: Data platforms
-author: Anjana Vakil
-date: 11-02-2022
-tags: ['nextjs', 'search']
-published: true
-slug: search-retro-games
----
-
-_Long ago, in the beautiful kingdom of JavaScript surrounded by cascades and DOM trees..._
-_legends told of an omnipotent and omniscient Gaming Power that resided in a hidden app._
-
-_It's hidden because you haven't built it yet._
-_And the time of destiny for Princess ~~Zelda~~ You is drawing near._
-
-## An epic quest
-
-In this tutorial we'll build a full-stack web app that allows us to search a collection of retro games data to find the old-school console games of our dreams. And I hope you like adventure - for this will be a treacherous journey through the bleeding edge of recently released web technologies!
-
-We'll learn how to:
-
-- use [Xata](https://xata.io)'s serverless platform to store & retrieve data without needing a database
-- build an app with React Server & Client Components using [Next.js 13](https://nextjs.org/blog/next-13) and its `app/` directory
-- implement full-text search with boosting via the Xata SDK
-
-Before we begin, make sure you have [Node/npm](https://nodejs.org/en/) installed, and download the [games.csv](https://github.com/vakila/search-xata-next13/raw/main/util/games.csv) data we'll be working with. You can optionally check out the [completed tutorial code](https://github.com/vakila/search-xata-next13) for reference.
-
-The games may be old, but the tech is brand spankin' new - so let's play!
-
-## Backstory
-
-My friend & avid retro gamer [Sara Vieira](https://github.com/saravieira) had compiled a great collection of retro games data for her awesome site [letsplayretro.games](https://letsplayretro.games), a Next [app](https://github.com/SaraVieira/lets-play-retro-games) which already had a ton of great features. But one feature Sara still wished for was full-text search over not just games' names, but all their metadata, with the ability to prioritize (aka "boost") the highest-rated games in the search results.
-
-Since every minute spent coding is one less minute spent playing NES, neither of us wanted to waste a lot of time on making this happen. That's when it dawned on me that I knew of a way to easily implement full-text, boosted search over her tabular data: [Xata](https://xata.io) provides it out of the box!
-
-## It's-a me, Xata!
-
-Xata is a serverless data platform that's new on the Jamstack scene, and aims to make life easier for developers to build full-stack apps need without worrying about the details of a scalable, performant backend database. Let's give it a whirl.
-
-### Set up Xata CLI
-
-Sign up for a free account at [xata.io](https://xata.io) to get started.
-
-Then, from the command line install the [CLI](https://xata.io/docs/getting-started/cli) and authenticate with your Xata account:
-
-```bash
-npm i --location=global @xata.io/cli
-xata auth login
-```
-
-### Import CSV data
-
-Now we need to get our games data into Xata. Sara gave us a file `games.csv` with information about 7.3K games, and conveniently Xata offers a CSV importer that lets us create a database table from that file, with an automatically-inferred schema.
-
-Import the file with the following command. Xata will ask you to set up a new workspace & database (I called my database `retrogames` but you can name yours as you please), then create a new table in that database named `games`:
-
-```bash
-xata import csv games.csv --table=games
-```
-
-Now let's take a look at our data in Xata's web interface.
-
-### Browse the dashboard
-
-Open your newly-created database in your browser with the command:
-
-```bash
-xata browse
-```
-
-This brings us to a spreadsheet-like view of our games data in the Xata dashboard. We can use the dashboard to browse, query, edit, and search our data, view and edit our databases, tables and schemas, and more.
-
-
-
-For our app's purposes, these columns of the data are going to be particularly useful:
-
-- `id`: the game's unique ID in the database
-- `name`: the game's name
-- `totalRating`: the game's average rating on [IGDB](https://igdb.com)
-- `cover`: URL of the game's cover image
-
-### Search all the things!
-
-The "Search" tab lets us try out Xata's built-in full-text search capabilities. Whatever search term we enter will be matched against any of the text-based columns in our database; each result is assigned a relevance score, with most relevant results appearing first.
-
-
-
-Using Xata's "Boost" feature, we can increase the relevance score of certain results by telling Xata which information we care most about. For example, we can add a numeric booster based on `totalRating` to have the highest-rated games appear higher in the search results.
-
-
-
-To deliver this awesome, customized search experience to our users, we'll use Xata's SDK to interact with our database from our app. On the Search page in the dashboard, click "Get code snippet" to reveal the SDK code for performing that search, including any boosters applied:
-
-```tsx
-// Generated with CLI
-import { getXataClient } from './xata';
-const xata = getXataClient();
-
-const { records } = await xata.search.all('demon', {
- tables: [
- {
- table: 'games',
- boosters: [{ numericBooster: { column: 'totalRating', factor: 2 } }]
- }
- ],
- fuzziness: 0,
- prefix: 'phrase'
-});
-
-console.log(records);
-```
-
-We'll use this code later to search our Xata data from our web app. But that app doesn't exist yet, so first we'll need to create it!
-
-## Next.js 13, fresh on the scene
-
-Because life is full of contradictions, we're going to serve up our super vintage games data with the newest, most cutting-edge fullstack web framework we know: [Next.js 13](https://nextjs.org/blog/next-13), which is only about a week old at the time of writing.
-
-Next.js is beloved by React developers for its server-side rendering capabilities, which make apps more performant by letting us pre-load data and render React components on the server when we can, rather sending all that JS over the wire and making the client to do the heavy lifting on each request.
-
-With v13 and its experimental `app/` directory, Next is now changing up the way we think about architecting & fetching data in Next apps, prioritizing server-side rendering as default via React [Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components), while still allowing us to write hook-based Client Components when we need them.
-
-The Next and React features this new architecture is based on are still experimental and may change, but it's worth wrapping our heads around the new direction in which the React/Next community is headed - so let's dive in and try them out!
-
-### Bootstrapping Next with `app/` directory
-
-The handy tool `create-next-app` helps us bootstrap a new Next app, and in the latest version we can try out the experimental new `app/` directory.
-
-Back on the command line, create an app with the following command, walking through the prompts to name the project as you please and use TypeScript (or JavaScript if you prefer):
-
-```bash
-npx create-next-app@latest --experimental-app
-```
-
-Exploring our newly-created directory, we notice the following structure:
-
-```bash
-app/
- layout.tsx (or .jsx if using JS)
- page.tsx
-pages/
- api/
- hello.ts (or .js)
-```
-
-This represents a partial [migration](https://beta.nextjs.org/docs/upgrade-guide#migrating-from-pages-to-app) to the new `app/` directory architecture, in which `app/page.tsx` will be rendered at our app's `/` route, using the default layout `app/layout.tsx`. However, the new `app/` directory doesn't yet support API routes, so we're still using the old structure `pages/api/hello.ts` to create the [API route](https://nextjs.org/docs/api-routes/introduction) `/api/hello`. See the [Next.js 13](https://beta.nextjs.org/docs/routing/fundamentals#) documentation for more details.
-
-To build our search app, we'll need to write all three of these:
-
-- Server Components
-- Client Components
-- API routes
-
-In order to fetch the data we'll need for these, we first need to set up the Xata SDK so that our app can talk to the database we created earlier in Xata.
-
-### Connect to Xata
-
-In the new directory created by `create-next-app`, [initialize a Xata project](https://xata.io/docs/getting-started/cli#project-mode) with the command:
-
-```bash
-xata init
-```
-
-In the prompts that follow, select the database you created earlier, and choose to generate TypeScript code (or JS code with ESM if you chose not to use TS) for the Xata client. This will create a `xata.ts` file at the path you specify; you can use the default path `src/xata.ts` or change it to e.g. `util/xata.ts` as I did.
-
-After running the init script, you'll have a `.env` file with the environment variables the Xata client needs to run, and a `xata.ts` file which exports a `getXataClient()` function we can now use to create a client object that can read our database on Xata.
-
-### Create a Server Component
-
-By default, components in the `app/` directory (like `app/page.jsx`) are [Server Components](https://beta.nextjs.org/docs/rendering/server-and-client-components#server-components). Server components are great for parts of our app that don't require client-side interactivity.
-
-Let's try writing our first server component to load & display data about a certain game. We'll make the route for this page `/games/[gameID]`, where `[gameID]` is a [dynamic route segment](https://beta.nextjs.org/docs/routing/defining-routes#dynamic-segments) matching the ID of a game in our dataset, e.g. `/games/123` will show info for "Clash at Demonhead".
-
-
-
-To make the new route, create a new file at the path `app/games/[gameID]/page.tsx`. Now, we need to flesh out that file to load data from Xata into a Server Component.
-
-In the new `page.tsx` file, import & call `getXataClient()` to instantiate a new Xata client:
-
-```tsx
-// app/games/[gameID]/page.tsx
-
-import { getXataClient } from '../../../util/xata'; // or the path you chose during 'xata init'
-
-const xata = getXataClient();
-```
-
-Now, we can query our data with the Xata SDK by calling methods on `xata.db[tableName]`, in our case `xata.db.games`.
-
-To retrieve a game by its ID, for example, we can use [`.read()`](https://xata.io/docs/sdk/get):
-
-```tsx
-const clashAtDemonhead = await xata.db.games.read('123');
-```
-
-Unlike with previous versions of Next, when fetching data in `app/` Server Components we don't need anything fancy tricks like `getServerSideProps`. We can `await` our data-fetching functions like we would anywhere else!
-
-In `page.tsx`, export a `Page()` function that captures the ID from our dynamic `gameID` route segment from the `params` object passed in by Next, and passes that ID to the call to `.read()`. We can then use properties of the `game` record, e.g. its `name`, `cover` image, `summary` text and `console`, to render a basic page:
-
-```tsx
-// app/games/[gameID]/page.tsx
-
-import { getXataClient } from '../../../util/xata'; // or the path you chose during 'xata init'
-
-const xata = getXataClient();
-
-export default async function Page({ params }: { params: { gameID: string } }) {
- const game = await xata.db.games.read(params.gameID);
-
- if (!game) return Game not found
;
-
- return (
-
- {game.name}
- {game.cover && }
- {game.summary}
- {game.console}
-
- );
-}
-```
-
-To see the new page in action, start up a local Next server with the command:
-
-```bash
-npm run dev
-```
-
-Then navigate to `localhost:3000/games/123` and behold your beautiful new server-rendered component, which automatically uses the default layout in `app/layout.tsx`.
-
-Server Components are great for pages that don't require interactivity, like our game-info page, because they can be more performantly rendered server-side. But what we really wanted in this app was a search page that dynamically loads data based on user input; for that, we're going to need a Client Component.
-
-### Create a Client Component
-
-[Client Components](https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components) are what we might think of as "traditional" React components that allow us to power client-side interactions with hooks like `useState()`. Now that the `app/` directory makes Server Components the default, we have to explicitly designate our Client Components with the `'use client';` directive.
-
-Create a new `Search` Client Component by creating a new file `app/search.tsx` with `'use client';` on the first line and exporting a function called `Search()` that renders a basic search input:
-
-```tsx
-// app/search.tsx
-'use client';
-
-export default function Search() {
- return (
-
-
-
- );
-}
-```
-
-We want this component to show up on our app's home page at the `/` route, so in `app/page.tsx` import the `Search` component and use it to replace the default content in the `Home` component generated by `create-next-app`. While you're at it, why not change the page heading to something more fun?
-
-```tsx
-// app/page.tsx
-import styles from './page.module.css';
-import Search from './search';
-
-export default function Home() {
- return (
-
-
- Search Retro Games!
-
-
-
-
- );
-}
-```
-
-Navigate to `localhost:3000/` and you'll see the `Search` Client Component successfully rendered within the `Home` Server Component and default layout!
-
-
-
-Now let's capture what the user types into the search input as state, so that we can later use it to dynamically search our Xata database. Editing `app/search.tsx`, use the `useState()` React hook to connect the value of the search input to a piece of state called something like `searchTerm`:
-
-```tsx
-// app/search.tsx
-
-'use client';
-
-import { useState } from 'react';
-
-export default function Search() {
- const [searchTerm, setSearchTerm] = useState('');
-
- return (
-
- setSearchTerm(e.target.value)} />
-
- );
-}
-```
-
-Now that we've got a functioning search input connect to our app state, we need to actually send the search terms to Xata to find relevant games!
-
-However, we can't directly call Xata from the client side, because that would potentially expose the secret API key Xata uses to access our database (which we set up during `xata auth login` and added to our app's `.env` file during `xata init`). So we'll need to set up an API route on our server to fetch the data from Xata, and then fetch from that API endpoint from our client component.
-
-### Create an API route to return search results
-
-The new `app/` directory will eventually support API routes, but as of the time of writing it doesn't yet do so. In the meantime, we can [still use](https://beta.nextjs.org/docs/upgrade-guide#migrating-from-pages-to-app) the old `pages/api/` path structure to create API routes that will seamlessly mesh with the routes in our `app/` directory. This is showcased by `create-next-app` with the automatically generated `pages/api/hello.ts`, which demonstrates how to handle an API request and return a response with JSON data:
-
-```tsx
-// pages/api/hello.ts (generated by create-next-app)
-
-// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
-import type { NextApiRequest, NextApiResponse } from 'next';
-
-type Data = {
- name: string;
-};
-
-export default function handler(req: NextApiRequest, res: NextApiResponse) {
- res.status(200).json({ name: 'John Doe' });
-}
-```
-
-Let's repurpose this file for the `/api/search` endpoint we need. Rename the file to `pages/api/search.ts` and navigate to `localhost:3000/api/search` to see it returning the dummy "John Doe" data.
-
-Now, we can finally use the `xata.search.all(...)` code snippet we copied from the Xata dashboard earlier to retrieve data based on a given search term, boosting by the game's rating.
-
-We can use the Next [request helper](https://nextjs.org/docs/api-routes/request-helpers) `req.query` to capture the query parameters of the request, so that we can pass the search term to our endpoint via a `term` parameter like so: `/api/search?term=mario`
-
-Each object in the `records` array returned by `xata.search.all()` has the shape `{ table: "games", record: { id, name, console, ... }}`, but since we don't need the `table` name, extract just the `record` of each object by mapping over the array, and send the resulting array as JSON data in the response.
-
-Your finished `search.ts` file should look something like this:
-
-```tsx
-// pages/api/search.ts
-import type { NextApiRequest, NextApiResponse } from 'next';
-import { getXataClient } from '../../util/xata';
-
-// Instantiate the Xata client
-const xata = getXataClient();
-
-// Make the function async to await the search data
-export default async function handler(req: NextApiRequest, res: NextApiResponse) {
- // Parse the 'term' query parameter from the request
- const term = req.query.term as string;
-
- // Paste the code snippet copied from the Xata search dashboard
- // passing in the search term from the request
- const { records } = await xata.search.all(term, {
- tables: [
- {
- table: 'games', // Search the 'games' data
- // Prioritize games with a higher 'totalRating'
- boosters: [{ numericBooster: { column: 'totalRating', factor: 2 } }]
- }
- ],
- fuzziness: 0,
- prefix: 'phrase'
- });
-
- // Extract the `record` property from the { table, record } objects returned by the search
- res.status(200).json(records.map((r) => r.record));
-}
-```
-
-Navigate to your new & improved search endpoint to try out different search terms and verify it's working! For example `http://localhost:3000/api/search?term=batman`.
-
-Now all that remains is to connect our client side code to the new search API route, so that our users can search games from the UI.
-
-### Fetch data from the client
-
-The React and Next teams are working on some big changes to our current patterns for [fetching data](https://beta.nextjs.org/docs/data-fetching/fundamentals) on the server & client. At the time of writing, these new patterns are quite experimental and not _quite_ at the stage where developers can fully take advantage of them.
-
-As we saw earlier in our `games/[gameID]/page.tsx` component, we can use `await` to fetch data in React Server Components, but it isn't supported in Client Components. Instead, React's new `use()` hook is intended to bring `await`-like functionality to Client Components, so in the future we should be able to asynchronously fetch data with `use()` in a Client Component as described in the [Next docs](https://beta.nextjs.org/docs/data-fetching/fetching#example-fetch-and-use-in-client-components) and [React's promises RFC](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#example-use-in-client-components-and-hooks).
-
-However, at the time of writing, the caching functionality that's a necessary complement to `use()` has [not been fully implemented](https://github.com/vercel/next.js/issues/42180#issuecomment-1296302329) in React/Next yet, and the intended `use()` pattern seems to create an infinite loop of rerenders and network requests in our Client Component.
-
-So while we eagerly await (ha!) full use (ha!!) of `use()`, we still need a way to fetch search results from our API endpoint and use it to render a basic list of games (each linking to its corresponding `/games/[gameID]` page). For now, we can do this by fetching data within a `useEffect()` and capturing the results with `useState()`, like so:
-
-```tsx
-// app/search.tsx
-
-'use client';
-
-import { useEffect, useState } from 'react';
-import Link from 'next/link';
-
-// fetch() is not yet supported inside Client Components
-// so we wrap it in a utility function
-async function getData(term: string) {
- const res = await fetch(`/api/search?term=${term}`);
- return res.json();
-}
-
-export default function Search() {
- const [searchTerm, setSearchTerm] = useState(''); // Track the search term
- const [games, setGames] = useState([]); // Track the search results
-
- useEffect(() => {
- if (searchTerm) {
- // Update the games array once data has loaded
- getData(searchTerm).then((results) => setGames(results));
- } else {
- // Reset games if the search term has been cleared
- setGames([]);
- }
- }, [searchTerm]);
-
- return (
- <>
-
- setSearchTerm(e.target.value)} />
-
- {games.map(({ id, name }) => {
- // Render a basic link to the info page for each game
- return (
-
-
- {name}
-
-
- );
- })}
- >
- );
-}
-```
-
-Now our search app, while basic, is complete! Reload the home page at `localhost:3000/` and enjoy searching through thousands of retro games.
-
-
-
-## Recap & next steps
-
-In the process of building our app to search retro games data, we've covered a lot of ground! We learned how to:
-
-- Import CSV data into Xata
-- Set up the Xata SDK in a Next project
-- Use the new `app/` directory in Next.js 13
-- Build React Server and Client components in `app/`
-- Perform full-text search with boosting via the Xata SDK
-- (Almost) use the new `use()` hook to fetch data from Client Components
-
-There is still a lot more Xata functionality we haven't had time to explore. Check out [search-retro-games.vercel.app](https://search-retro-games.vercel.app) for an enhanced version of this [project](https://github.com/vakila/search-retro-games) featuring:
-
-- [Filtering](https://xata.io/docs/sdk/filtering) search results by console
-- [Aggregating](https://xata.io/docs/sdk/aggregate) to count the total number of games
-- Debounced search input to avoid over-fetching
-- More details in search results & game pages
-
-Now let's take a break from all this coding, and go play some retro games!
-
-_Thanks very much to [Xata](https://xata.io) for sponsoring this work and to retro gaming queen [Sara Vieira](https://github.com/saravieira) for making it possible!_
diff --git a/securely-querying-your-database.mdx b/securely-querying-your-database.mdx
deleted file mode 100644
index d3fe3212..00000000
--- a/securely-querying-your-database.mdx
+++ /dev/null
@@ -1,101 +0,0 @@
----
-title: 'Securely querying your database with Xata'
-description: 'Connect to a Xata database and fetch data from client-side apps without exposing security keys.'
-image:
- src: https://raw.githubusercontent.com/xataio/mdx-blog/main/images/securely-querying-your-database-on-xata.png
- alt: Xata
-author: Atila Fassina
-date: 10-24-2022
-tags: ['security']
-published: true
-slug: securely-querying-your-database-on-xata
----
-
-With a database access key being sent out from the browser, your database is readable and writable by anyone. They may read the key and therefore use the key to create malicious connections to your database. These actions (attacks) are called **exploits**, and the data retrieved from such exploits are called **leaks**. Yes, like the famous ādata leaksā we hear about in the news. Whether your leaked app makes to the news or not will depend on how big and influential your user-base is, but the problem is still the same. Someone with the key can even delete all of your data if they so choose.
-
-When building an app, a common pattern is to build an API (Application Program Interface) that will work as a bridge between your database and the client-side app. Said API will expose a certain number of endpoints depending on how it is designed. For example:
-
-
-
-This architecture is not only common, it's a requirement. It happens because a database requires **access keys**. As anything valuable, user data must be stored behind locked doors. Wherever the query originates from, must add these **access keys** (often referred to as **access tokens** as well) to the request. This is why any communication with a database must happen in a **private connection**.
-
-A browser is a **public** interface. Whatever data and code an app has in the browser is **exposed the public**. Now connect the dots: if everything on the browser is available to the public, and a query (request) goes from the browser straight to the database, what happens to the secure key it must carry to actually retrieve the data? You guessed it: the key is readable, as said in security contexts, it is **exposed**.
-
-## Querying Xata
-
-To make things more ergonomic, Xata has a [Software Development Kit (SDK)](https://xata.io/docs/sdk/typescript/overview) we call [client-ts](https://github.com/xataio/client-ts). Under the hood, there are many best practices we enforce. It also generates TypeScript types for your data request and responses based on your schema - that is the Object Relational Mapping (ORM) side of our SDK.
-
-But arguably, the most important aspect of our SDK is that it will securely query Xata using your environment variables (`process.env`) the `XATA_API_KEY`, which is the key required to query your database. There multiple different ways to expose this environment variable to your running app, for example:
-
-- when calling the script:
-
-```bash
-XATA_API_KEY=my_api_key node ./script/query.js
-```
-
-- more commonly, via the `.env` configuration file:
-
-```bash
-XATA_API_KEY=my_api_key
-```
-
-When deploying your app, you must find out how to define Environment Variables to the deployment script. Each PaaS (Platform as a Service) has a different way of doing that. Have a look for examples at [CircleCI](https://circleci.com/docs/env-vars/), [Cloudflare Workers](https://developers.cloudflare.com/workers/platform/environment-variables/), [Netlify](https://docs.netlify.com/configure-builds/environment-variables/), or [Vercel](https://vercel.com/docs/concepts/projects/environment-variables).
-
-The `process.env` namespace is not available in browser environments so if using the SDK, you will experience _friction_ when attempting to query your Xata database from the browser. **This is intentional and by design.** Essentially, `process.env.XATA_API_KEY` will be `undefined`, and the SDK will not be able to fire your request (thus preventing you from leaking access to your database), and you will see a runtime error like:
-
-```bash
-error Error: Option apiKey is required
- at XataClient.parseOptions_fn (index.mjs?4597:3090:1)
- at new _a (index.mjs?4597:3053:1)
- at XataClient._createSuperInternal (_create_super.mjs?05d7:12:1)
- at new XataClient (xata.codegen.ts?b4b0:32:24)
- at getXataClient (xata.codegen.ts?b4b0:42:14)
- at eval (stepone.js?34b1:101:47)
-```
-
-If on a Node.js environment, this means the SDK cannot read your API key. You can solve this by using one of the methods mentioned above to expose your access key to your runtime. If using the `.env` approach, you may need a package like [dotenv](https://www.npmjs.com/package/dotenv) to read those values.
-
-If on a browser environment, this error \***\*\*\*\*\***\*\*\***\*\*\*\*\***\*\*\***\*\*\*\*\***\*\*\***\*\*\*\*\***should not be āfixedā\***\*\*\*\*\***\*\*\***\*\*\*\*\***\*\*\***\*\*\*\*\***\*\*\***\*\*\*\*\***. As explained in the first section of this article: exposing your Xata API keys to a client-side runtime like a browser is a huge security issue. For more information, read our documentation about [Securely Talking to Xata](https://xata.io/docs/getting-started/installation#working-with-client-sdks).
-
-## Creating apps and connecting to Xata
-
-The path of least resistance to create an app and connect it to Xata is to use a full-stack framework such as [Next.js](https://nextjs.org/docs), [Remix](https://remix.run/docs/en/v1), [Nuxt](https://nuxtjs.org), [Gatsby](https://www.gatsbyjs.com/docs/), and others. Such apps offer a way of creating **serverless functions** within them. In Next.js for example, any files in `./pages/api` contain these functions. But what are they? In a nutshell serverless functions are pieces of code that will run only on the server, allowing developers to query their database there and thus remaining close to the app logic and more importantly, **_out of the unsafe browser environment._**
-
-When creating a pure client-side app ([Astro](http://astro.build), [Eleventy](https://www.11ty.dev/), [Qwik](https://qwik.builder.io/), [React](https://beta.reactjs.org/), [Solid](https://www.solidjs.com/), [Svelte](https://svelte.dev/), [Vue](https://vuejs.org/), etc.), the database request will not be able to originate from the app. Instead, refer to the initial architecture we outlined in this post: first hit an API, then the API connects to the Database. This keeps your API keys secret.
-
-### What are serverless functions?
-
-Different than a long-standing server that will continue to run even when there are no requests coming in or out, a serverless function will cease activity and go into āsleepā whenever its execution is finished. This makes serverless functions a perfect use-case to bridge connections between your app and your database.
-
-Additionally, serverless providers like [AWS Amplify](https://aws.amazon.com/amplify/), [Cloudflare Workers](http://workers.cloudflare.com), [Netlify](http://netlify.com), and [Vercel](http://vercel.com) will provide developers with a no-obstacle deployment. You define your function handler (most receive 2 parameters: `request` and `response`).
-
-## Connecting a Serverless Function to Xata
-
-Creating a serverless function is basically as straightforward as creating a JavaScript file.
-
-```jsx
-// In Next.js, this would be ./pages/api/get-data.ts
-
-export default function handler (request, response) {
- // fetch stuff from Xata
- const data = await xata.db.myDb.getMany();
- response.end(data.records);
-}
-```
-
-To understand how to create one, and how to design your API, we have a [Vercel Serverless Function Sample](https://github.com/xataio/examples/tree/main/apps/starter-vercel-serverless-functions) in our example repository. Do not forget to create and add your API Keys to the deployment routines within your dashboard, otherwise the Xata Client will not have a key to fire requests with.
-
-For a more involved example, refer to the [working with client SDKs](https://xata.io/docs/getting-started/installation#working-with-client-sdks).
-
-## Conclusion
-
-We hope this has made it clearer how to interact with your Xata database from within and outside your apps. Please feel free to create an issue in our [examples repository](https://github.com/xataio/examples) if there is a specific use-case that you would like us to prioritize in showing how to address.
-
-Additionally, we cannot stress it enough that **we love open-source** and there is some swag waiting for everyone who ships a community sample app, or creates a PR to contribute to any of our projects (examples included!).
diff --git a/semantic-search-openai-typescript-deno.mdx b/semantic-search-openai-typescript-deno.mdx
deleted file mode 100644
index f296d9e2..00000000
--- a/semantic-search-openai-typescript-deno.mdx
+++ /dev/null
@@ -1,323 +0,0 @@
----
-title: 'Semantic search with Xata, OpenAI, TypeScript, and Deno'
-description: 'Reduce complex data and perform similarity search with Xata.'
-image:
- src: https://raw.githubusercontent.com/xataio/mdx-blog/main/images/semantic-search-openai-typescript-deno.png
- alt: Xata and ChatGPT logos
-author: Tudor Golubenco
-date: 03-02-2023
-tags: ['search', 'ai']
-published: true
-slug: semantic-search-openai-typescript-deno
----
-
-At the same time we launched our [ChatGPT integration](https://xata.io/chatgpt), we also added a new vector type to Xata, making it easy to store embeddings. Additionally, we have added a new `vectorSearch` endpoint, which performs similarity search on embeddings.
-
-Letās take a quick tour to see how you can use these new capabilities to implement semantic search. Weāre going to use the OpenAI embeddings API, TypeScript and Deno. This tutorial assumes prior knowledge of TypeScript, but no prior knowledge of Xata, Deno, or OpenAI.
-
-## What is semantic search?
-
-Instead of just matching keywords, as traditional search engines do, semantic search attempts to understand the context and intent of the query and the relationships between the words used in it.
-
-For example, letās say you have the following sample sentences:
-
-- "The quick brown fox jumps over the lazy dog story"
-- āThe vehicle collided with the treeā
-- āThe cat growled towards the dogā
-- "Lorem ipsum dolor sit amet, consectetur adipiscing elitā
-- āThe sunset painted the sky with vibrant colorsā
-
-If you search for āsample text in latinā, traditional keyword search wonāt match the ālorem ipsumā text, but semantic search will (and weāre going to demo it in this article).
-
-Similarly, if you search for āthe kitty hissed at the puppyā, semantic search will see that the phrase has the same meaning as āthe cat growled towards the dogā, even if they use none of the same words. Or, for another example, āvanilla skyā should bring up the āThe sunset painted the sky with vibrant colorsā sentence. Pretty cool, right? This is now quite possible thanks large-language models and vector search.
-
-## A quick intro to embeddings
-
-From a data point of view, embeddings are simply arrays of floats. They are the output of ML models, where the input can be a word, a sentence, a paragraph of text, an image, an audio file, a video file, and so on.
-
-
-
-Each number in the array of floats represents the input text on a particular dimension, which depends on the model. This means that the more similar the embeddings are, the more āsimilarā the input objects are. Iāve put āsimilarā in quotes because it depends on the model what kind of similar it means. When it comes to text, itās usually about āsimilar in meaningā, even if different words, expressions, or languages are used.
-
-Reducing complex data to an array of numbers representing its qualities turns out to be very useful for a number of use cases. Think of reverse image search, recommendations algorithms for video and music, product recommendations, categorizing products, and so on.
-
-## Vector type in Xata
-
-If you want to follow along, start with steps:
-
-- Sign up or sign into Xata [here](https://app.xata.io/signin) (the usage from this tutorial fits well within the free tier, so you donāt need to set up billing)
-- Create a database named `vectors`
-- Create a table named `Sentences`
-- Add two columns:
- - `sentence` of type string
- - `embedding` of type vector. Use 1536 as the dimension
-
-
-
-When you are done, the schema should look like this:
-
-
-
-## Initialize the Xata project
-
-To get ready for running the typescript code, install the Xata CLI:
-
-```sh
-npm install -g @xata.io/cli@latest
-```
-
-Run `xata auth login` to authenticate the CLI. This will open a browser window and prompt you to generate a new API key. Give it any name youād like.
-
-```sh
-xata auth login
-```
-
-Create a folder for the code:
-
-```sh
-mkdir sentences
-cd sentences
-```
-
-And run `xata init` to connect it to the Xata DB:
-
-```sh
-xata init
-```
-
-The Xata CLI will ask you to select the database and then ask you how to generate the types. Select **Generate TypeScript code with Deno imports**. Use default settings for the rest of the questions.
-
-
-
-## Prepare OpenAI and Deno
-
-Create an [OpenAI account](https://platform.openai.com/signup) and generate a key. Note that you need to set up billing for OpenAI in order for to run these examples, but the cost will be tiny (under $1).
-
-Add the OpenAI key to the `.env` file which was created by the `xata init` command above. Your `.env` should look something like this:
-
-```bash
-# API key used by the CLI and the SDK
-# Make sure your framework/tooling loads this file on startup to have it available for the SDK
-XATA_API_KEY=xau_
-OPENAI_API_KEY=sk-
-```
-
-Install the Deno CLI. See [this page](https://deno.land/manual@v1.31.3/getting_started/installation) for the various install options, on macOs with Homebrew it is:
-
-```sh
-brew install deno
-```
-
-## Load data
-
-Itās now the time to write a bit of TypeScript code. Create a `loadWithEmbeddings.ts` file top level in your project with the following contents:
-
-```tsx
-import { Configuration, OpenAIApi } from 'npm:openai';
-import { getXataClient } from './src/xata.ts';
-import { config as dotenvConfig } from '';
-
-dotenvConfig({ export: true });
-
-const openAIConfig = new Configuration({
- apiKey: Deno.env.get('OPENAI_API_KEY')
-});
-const openAI = new OpenAIApi(openAIConfig);
-const xata = getXataClient();
-
-const sentences: string[] = [
- 'The quick brown fox jumps over the lazy dog story',
- 'The vehicle collided with the tree',
- 'The cat growled towards the dog',
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit',
- 'The sunset painted the sky with vibrant colors'
-];
-
-for (const sentence of sentences) {
- const resp = await openAI.createEmbedding({
- input: sentence,
- model: 'text-embedding-ada-002'
- });
- const [{ embedding }] = resp.data.data;
-
- xata.db.Sentences.create({
- sentence,
- embedding
- });
-}
-```
-
-Step by step, this is what the script is doing:
-
-- Uses `dotenv` to load the `.env` file into the current environment. This makes sure the `XATA_API_KEY` and the `OPENAI_API_KEY` are available to the rest of the script.
-- Initializes the OpenAI and Xata client libraries.
-- Defines the test data in the `sentences` array.
-- For each `sentence` , calls the [OpenAI embeddings API](https://platform.openai.com/docs/guides/embeddings) to get the embedding for it.
-- Inserts a record containing the sentence and the embedding into Xata.
-
-Execute the script like this:
-
-```sh
-deno run --allow-net --allow-env --allow-read --allow-run ./loadWithEmbeddings.ts
-```
-
-If you visit the Xata UI now, you should see the data loaded, together with the embeddings.
-
-
-
-## Run search queries
-
-Letās write another simple script that performs a search based on an input query. Name this script `search.ts`.
-
-```tsx
-import { Configuration, OpenAIApi } from 'npm:openai';
-import { getXataClient } from './src/xata.ts';
-import { config as dotenvConfig } from '';
-
-dotenvConfig({ export: true });
-
-const openAIConfig = new Configuration({
- apiKey: Deno.env.get('OPENAI_API_KEY')
-});
-const openAI = new OpenAIApi(openAIConfig);
-const xata = getXataClient();
-
-if (Deno.args.length !== 1) {
- console.log('Please provide a search query');
- console.log("Example: deno run --allow-net --allow-env search.ts 'the quick brown fox'");
- Deno.exit(1);
-}
-
-const query = Deno.args[0];
-const resp = await openAI.createEmbedding({
- input: query,
- model: 'text-embedding-ada-002'
-});
-const [{ embedding }] = resp.data.data;
-
-const results = await xata.db.Sentences.vectorSearch('embedding', embedding);
-
-for (const result of results) {
- console.log(result.getMetadata().score, '|', result.sentence);
-}
-```
-
-Here is whatās going on in the script:
-
-- The beginning is similar as for the previous script, using `dotenv` to load the `.env` file and then initializing the client libraries for OpenAI and Xata
-- Read the query as the first argument passed to the script
-- Use the [OpenAI embeddings API](https://platform.openai.com/docs/guides/embeddings) to create embeddings for the query
-- Run a vector search using the Xata [Vector Search API.](/docs/sdk/vector-search) This finds vectors that are similar to the provided embedding
-- Print the results, together with the similarity score
-
-To run the script, execute it like this:
-
-```sh
-$ deno run --allow-net --allow-env --allow-read --allow-run \\
- ./search.ts 'sample text in latin'
-
-1.8154079 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
-1.7424928 | The quick brown fox jumps over the lazy dog story
-1.7360129 | The cat growled towards the dog
-1.7311659 | The sunset painted the sky with vibrant colors
-1.7038174 | The vehicle collided with the tree
-```
-
-As you can see, searching for āsample text in latinā results in the āLorem ipsumā text as we hoped. You can also try some variations, for example, āsample sentenceā still brings the āLorem ipsumā one as the top result:
-
-```sh
-$ deno run --allow-net --allow-env --allow-read --allow-run \\
- ./search.ts 'sample sentence'
-
-1.805396 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
-1.7715557 | The quick brown fox jumps over the lazy dog story
-1.7608802 | The sunset painted the sky with vibrant colors
-1.7573793 | The cat growled towards the dog
-1.7493906 | The vehicle collided with the tree
-```
-
-The scores on the left are numbers between 0 and 2 which indicate how close each sentence is to the provided query. If you run with a sentence that exits in the data, youāll get a score very close to 2:
-
-```sh
-$ deno run --allow-net --allow-env --allow-read --allow-run \\
- ./search.ts 'The quick brown fox jumps over the lazy dog story'
-
-1.9999993 | The quick brown fox jumps over the lazy dog story
-1.8063612 | The cat growled towards the dog
-1.769694 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
-1.7673006 | The vehicle collided with the tree
-1.7605586 | The sunset painted the sky with vibrant colors
-
-```
-
-Now letās try the āvanilla skyā query:
-
-```sh
-$ deno run --allow-net --allow-env --allow-read --allow-run \\
- ./search.ts 'vanilla sky'
-
-1.8137007 | The sunset painted the sky with vibrant colors
-1.7584264 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
-1.7579571 | The quick brown fox jumps over the lazy dog story
-1.7505519 | The vehicle collided with the tree
-1.738231 | The cat growled towards the dog
-```
-
-Bingo, top result matches what we expected.
-
-Another one to try:
-
-```sh
-$ deno run --allow-net --allow-env --allow-read --allow-run \\
- ./search.ts 'The car crashed into the oak.'
-
-1.907266 | The vehicle collided with the tree
-1.7763984 | The cat growled towards the dog
-1.7755028 | The quick brown fox jumps over the lazy dog story
-1.7745116 | The sunset painted the sky with vibrant colors
-1.7570261 | Lorem ipsum dolor sit amet, consectetur adipiscing elit
-
-```
-
-Again, the sentence with the same meaning shows up at the top with a score close to 2.
-
-## Conclusion
-
-The large-language-models are powerful tools that open up new use cases. Semantic search is one of these use cases, and weāve seen how the Xata vector search can be used to implement it. You can also use it to build recommendation engines, or finding similar entries in a knowledge-base, or questions in a Q\&A website.
-
-If youāre running this tutorial, please join us on the Xata [Discord](https://xata.io/discord) and let us know what you are building!
diff --git a/server-actions-databases-data-handling.mdx b/server-actions-databases-data-handling.mdx
deleted file mode 100644
index fe7e53b6..00000000
--- a/server-actions-databases-data-handling.mdx
+++ /dev/null
@@ -1,180 +0,0 @@
----
-title: 'Server Actions, databases, and the future of data handling'
-description: 'Check out how Xata is improving data handling in modern apps using React advancements'
-image:
- src: https://raw.githubusercontent.com/xataio/mdx-blog/main/images/server-actions-data-handling.png
- alt: 'Server Actions, databases, and the future of data handling'
-author: Atila Fassina
-date: 06-21-2023
-tags: ['serverless', 'server']
-published: true
-slug: server-actions-data-handling
----
-
-At Xata we're focused on improving how modern applications handle data. So naturally, we're excited about the future of React. As a result of Concurrency in React gaining momentum and React Server Components becoming more widely understood, the data fetching story has made significant strides in terms of improvement.
-
-As developers, we're experiencing an exciting shift in how data integrates into our applications. Using the "Fetching then Rendering" pattern in our components provides a more enjoyable experience for developers and makes it easier to bring data into an application, resulting in a smoother overall experience.
-
-Notice we're querying the database straight from the component rendering function. That's amazing! On top of that, we can even wrap our components into a [suspense boundary](https://react.dev/reference/react/Suspense#usage) and let data stream as necessary.
-
-```tsx
-export async function Movie({ slug }) {
- const movie = await xata.db.movies.filter({ id: slug }).getFirst();
-
- return (
- <>
-
-
- {movie.title}
-
-
- {movie.overview}
-
- >
- );
-}
-```
-
-Check out the above code snippet in our [XMDB template](https://xata.io/docs/examples/xmdb)
-
-```tsx
-// app/page.tsx
-async function SERP() {
- searchParams
-}) => {
- return (
-
- }>
-
-
-
- )
-}
-```
-
-While inside `SearchResult`, we can fetch, await, and render. Additionally, we can even pass the pending promise as a `prop`. The [data-fetching story](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#static-data-fetching) is extremely versatile and can adapt to business logic instead of writing logic to contort framework specific rules, abstractions, and conventions. We write our logic and we can make it perform in the best way possible without much of a workaround.
-
-## Acting on server
-
-One could argue that apart from the granularity of performing data-fetching on a per-component basis, the story was well defined before. We had similar capabilities previously (albeit with the integration of additional libraries or tools), but we could achieve the same results. The missing piece, however, was having this functionality within the framework, which unlocks the mutation story. This aspect has varied greatly between different tech stacks and poses an important decision that teams must make, with plenty of room for mistakes.
-
-Remix tackles the mutation story on a per-view basis, using action functions that allow for the co-location of fetching and posting data with the render logic like never seen before. The framework astutely determines user intentions based on the HTTP method (usually `GET` or `POST`, but it also fully supports others). All we need to do is export the appropriate method in our page and set the correct method within our `` to the page to insert all the records to the trips table in the database when submitted.
-
-![Adding a form](/images/travel-app-guide/add-form.jpeg)
-
-## Display the trips
-
-To query and list all the trips, start by creating a file within the folder utils called `get-trips.js` that retrieves all the stored trip data from Xata with the following content:
-
-```js
-// File: utils/get-trips.js
-'use server';
-
-import { getXataClient } from '@/src/xata';
-
-export async function getTrips() {
- const xata = getXataClient();
- const trips = await xata.db.trips.getMany();
- const serializedTrips = JSON.stringify(trips);
- return serializedTrips;
-}
-```
-
-First, import the `getXataClient` function. Then, use the Xata client instance and the `getMany` function to get the resulting stored trips in the database. The return trips are serialized as a JSON string as `serializedTrips`.
-
-Next, create two separate files `utils/date.js` and `utils/capitalizeWords.js`.
-
-```js
-// File: utils/date.js
-export function formatDate(dateString) {
- const date = new Date(dateString);
- const options = {
- month: 'long',
- day: 'numeric'
- };
- const day = date.getDate();
- const suffix = (day) => {
- if (day >= 11 && day <= 13) {
- return 'th';
- } else {
- switch (day % 10) {
- case 1:
- return 'st';
- case 2:
- return 'nd';
- case 3:
- return 'rd';
- default:
- return 'th';
- }
- }
- };
- return `${date.toLocaleDateString('en-US', options)}${suffix(day)}`;
-}
-```
-
-The sole responsibility of the code above is to format the date for the stored start and end date field, which displays the month and day with a suffix attached to it.
-
-```js
-// File: utils/capitalizeWords.js;
-export function capitalizeWords(str) {
- if (!str) return '';
-
- return str
- .toLowerCase()
- .split(/\s+|-/)
- .map((word, index) =>
- index === 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word.charAt(0).toUpperCase() + word.slice(1)
- )
- .join(' ');
-}
-```
-
-The function `capitalizeWord` accepts a parameter to capitalize the first letter of every word.
-
-Now, letās create an `app/(custom)/dashboard/page.js` file to load the data and render the list of trips.
-
-```js
-// File: app/(custom)/dashboard/page.js
-'use client';
-
-import Link from 'next/link';
-import Header from '@/components/Heading';
-import { useState, useEffect } from 'react';
-import { getTrips } from '@/utils/get-trips';
-import { capitalizeWords } from '@/utils/capitalizeWords';
-import { formatDate } from '@/utils/date';
-
-export const revalidate = 0;
-
-export default function Dashboard() {
- const [trips, setTrips] = useState([]);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- const fetchTrips = async () => {
- try {
- const tripsData = await getTrips();
- setTrips(JSON.parse(tripsData));
- setLoading(false);
- } catch (error) {
- console.error('Error fetching trips:', error);
- setLoading(false);
- }
- };
-
- fetchTrips();
- }, []);
-
- return (
- <>
-
- {loading ? (
-
- Loading...
-
- ) : (
- trips.length === 0 && No travel memories found
- )}
-
- {!loading &&
- trips?.map((trip) => (
-
-
-
- {trip?.country ? trip?.country.toUpperCase() : ''} | {trip?.city ? capitalizeWords(trip?.city) : ''}
-
-
- {trip?.start ? formatDate(trip?.start) : ''} to{' '}
- {trip?.end ? formatDate(trip?.end) : ''}
-
-
-
- ))}
- >
- );
-}
-```
-
-Hereās a brief explanation of the code above:
-
-First, import all the necessary components like `getTrips`, `useEffect`, `useState`, and so on.
-
-The [revalidate](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating) option (`export const revalidate = 0`) helps cache data and immediately revalidate it to display the stored data upon page refresh.
-
-Before looping through the mapped trips state array, within the `fetchTrips` function, the tripsData is parsed with the `JSON.parse` method and updates the state with `setTrips`.
-
-Next, the UI is rendered. If there are no records present using `trips.length === 0`, the page shows a message saying, āNo travel memories foundā. In the other scenario where the data is being fetched, the loading state displays the loading message as the page loads. Now, loop through the trips data to get the column properties of each trip record.
-
-The Link component references the individual trip record, which will be used later.
-
-Your dashboard page should look like the following:
-
-![Dashboard view](/images/travel-app-guide/dashboard-view.jpeg)
-
-## Query and show a single trip
-
-To get a record by its ID from the database, letās begin with creating this file, `filterTrip.js`, within the `utils` folder:
-
-```js
-// File: utils/filterTrip.js
-'use server';
-
-import { getXataClient } from '@/src/xata';
-
-const xata = getXataClient();
-
-export async function filterTrip(params) {
- const data = await xata.db.trips.read({ id: params.id });
-
- return { data: JSON.parse(JSON.stringify(data)) };
-}
-```
-
-The params object within the function `filterTrip` is used in dynamic routes to retrieve and read the query of a single record from the database in the code block above, passing the return data object from the function.
-
-Next, create a page to retrieve the trip data and render the contents of the single trip to the UI.
-
-```js
-// File: app/(custom)/dashboard/trip/[id]/page.js
-import Header from '@/components/Heading';
-import Link from 'next/link';
-import Image from 'next/image';
-import { capitalizeWords } from '@/utils/capitalizeWords';
-import { filterTrip } from '@/utils/filterTrip';
-
-export default async function Trip({ params }) {
- const { data } = await filterTrip(params);
-
- return (
- <>
-
-
-
- {data?.country.toUpperCase()} | {capitalizeWords(data?.city)}
-
-
-
- Edit
-
-
- Delete
-
-
- Notes:
-
-
-
-
-
-
- {capitalizeWords('Vigo')}
- Rating: 4/5
-
-
-
-
-
-
-
-
- New Note +
-
-
-
-
-
- >
- );
-}
-```
-
-To get the value for the trip view, destructure the `const { data }` object with the imported `filterTrip` function and the parameter, params, in an asynchronous execution. The result will produce the resulting single Trip information.
-
-For the Edit button in the Link component, with `href={/dashboard/trip/${data.id}/update}`, it will route to another page when clicked. Later on, we will create and update the content for the Edit page view and the Delete component.
-
-The rest of the page with the content under the Notes section will be static. When you click on the New Note, it routes to the form page for creating notes.
-
-If you refresh the page, and it shows this error message, kindly [configure the next.config.mjs](https://nextjs.org/docs/app/api-reference/next-config-js) file in the root of your project directory.
-
-![Possible error message](/images/travel-app-guide/error-message.jpeg)
-
-```js
-// File: next.config.mjs;
-/** @type {import('next').NextConfig} */
-
-const nextConfig = {
- images: {
- deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
- remotePatterns: [
- {
- protocol: 'https',
- hostname: 'images.pexels.com'
- },
- {
- protocol: 'https',
- hostname: 'eu-west-1.storage.xata.sh'
- }
- ]
- }
-};
-
-export default nextConfig;
-```
-
-Your trip detail view page should now look like this:
-
-![Single trip detail view](/images/travel-app-guide/single-trip-view.jpeg)
-
-## Updating a single trip
-
-Updating the single trip data is similar to how the `dashboard/trip/create` page looks, but it will only populate the input fields with the existing data from the database.
-
-Before creating the file for updating the UI record for a trip data, letās create a file in the utils folder called `edit.js` that will do a partial update using the update method on the object of the trip:
-
-```js
-// File: utils/edit.js
-'use server';
-
-import { getXataClient } from '@/src/xata';
-
-const xata = getXataClient();
-
-export async function Edit(id, city, start, end, country) {
- await xata.db.trips.update(id, {
- city: city,
- country: country,
- start: start,
- end: end
- });
-}
-```
-
-Next, create a new file, `update/page.js` under the `trip/[id]` directory:
-
-```js
-// File: app/(custom)/dashboard/trip/[id]/update/page.js
-'use client';
-
-import Header from '@/components/Heading';
-import { Edit } from '@/utils/edit';
-import { filterTrip } from '@/utils/filterTrip';
-import { useState, useEffect } from 'react';
-import { useRouter } from 'next/navigation';
-import Link from 'next/link';
-import { ToastContainer, toast } from 'react-toastify';
-import 'react-toastify/dist/ReactToastify.css';
-
-export const revalidate = 0;
-
-export default function Update({ params }) {
- const router = useRouter();
-
- const [city, setCity] = useState('');
- const [country, setCountry] = useState('');
- const [start, setStart] = useState('');
- const [end, setEnd] = useState('');
-
- useEffect(() => {
- const fetchData = async () => {
- try {
- const { data } = await filterTrip(JSON.parse(JSON.stringify(params)));
- setCity(data.city);
- setCountry(data.country);
- setStart(data.start);
- setEnd(data.end);
- } catch (error) {
- console.error('Error fetching trip data:', error);
- }
- };
-
- fetchData();
- }, [params]);
-
- const handleSubmit = async (event) => {
- event.preventDefault();
- try {
- if (new Date(end) <= new Date(start)) {
- toast.error('End date must be after the start date.');
- return;
- }
- await Edit(params.id, city, start, end, country);
- router.push('/dashboard');
- } catch (error) {
- console.error('Error updating trip data:', error);
- }
- };
-
- const handleStartChange = (event) => {
- setStart(event.target.value);
- };
-
- const handleEndChange = (event) => {
- setEnd(event.target.value);
- };
-
- const today = new Date().toISOString().split('T')[0];
-
- const tomorrow = new Date();
- tomorrow.setDate(tomorrow.getDate() + 1);
- const tomorrowFormatted = tomorrow.toISOString().split('T')[0];
-
- return (
-
-
-
-
-
- setCity(e.target.value)}
- required
- className="w-full px-4 py-2 border rounded-lg mb-5 mt-3 text-gray-700 bg-white border-gray-300 appearance-none block leading-normal focus:outline-none"
- />
-
-
-
- setCountry(e.target.value)}
- required
- className="w-full px-4 py-2 border rounded-lg mb-5 mt-3 text-gray-700 bg-white border-gray-300 appearance-none block leading-normal focus:outline-none"
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Cancel
-
-
-
-
-
- );
-}
-```
-
-In the code block, the `useEffect` hook runs the function fetchData when the component mounts, and every time the value of the params changes. Also, the `useRouter` component passed as a variable `const router = useRouter()` will help navigate the window to the `/dashboard` page on successful changes of the input fields in the `handleSubmit` function.
-
-If you donāt intend to change any field, clicking the Cancel button returns you to the `/dashboard` page.
-
-## Handling the notes form submissions
-
-Letās begin by creating the connection to [insert the record](https://xata.io/docs/sdk/insert) for the notes data using the Xata client SDK.
-
-Create the `notes-data.js` file in the `utils` directory:
-
-```js
-// File: utils/notes-data.js
-'use server';
-
-import { getXataClient } from '@/src/xata';
-
-const xata = getXataClient();
-
-export async function notesData(name, description, type, rating, mediaType, tripId) {
- const record = await xata.db.notes.create(
- {
- name: name,
- description: description,
- type: type,
- trip: tripId,
- rating: parseInt(rating),
- img: { mediaType, base64Content: '', name: name }
- },
- ['*', 'img.uploadUrl']
- );
- return { uploadUrl: record.img?.uploadUrl };
-}
-```
-
-Using [Xata file attachments](https://xata.io/docs/sdk/file-attachments#upload-files-using-upload-urls), image files can be uploaded using the upload URL which will be possible from the client-side.
-
-With the Xata client connection in place, create a new file `app/(custom)/dashboard/note/create/page.js` that will collect all the user input and send its data from the client-side to Xata serverless database.
-
-```js
-// File: app/(custom)/dashboard/note/create/page.js
-'use client';
-
-import { useState, useEffect } from 'react';
-import Header from '@/components/Heading';
-import { ToastContainer, toast } from 'react-toastify';
-import 'react-toastify/dist/ReactToastify.css';
-import Image from 'next/image';
-import { useRouter } from 'next/navigation';
-import { notesData } from '@/utils/notes-data';
-import { getTrips } from '@/utils/get-trips';
-import { capitalizeWords } from '@/utils/capitalizeWords';
-
-const defaultFormFields = {
- name: '',
- description: '',
- type: '',
- trip: '',
- rating: 1,
- img: null
-};
-
-export default function NoteForm() {
- const [formFields, setFormFields] = useState(defaultFormFields);
- const { name, description, type, rating, img, trip } = formFields;
-
- const [trips, setTrips] = useState([]);
-
- const router = useRouter();
-
- useEffect(() => {
- const fetchTrips = async () => {
- try {
- const tripsData = await getTrips();
- setTrips(JSON.parse(tripsData));
- } catch (error) {
- console.error('Error fetching trips', error);
- }
- };
- fetchTrips();
- }, []);
-
- const getTripIdByName = (tripName) => {
- const selectedTrip = trips.find((trip) => trip.city === tripName);
- return selectedTrip ? selectedTrip.id : null;
- };
-
- const validateForm = () => {
- return name.trim() !== '' && description.trim() !== '' && type.trim() !== '' && trip.trim() !== '' && img !== null;
- };
-
- const submit = async () => {
- const tripId = getTripIdByName(trip);
- const { uploadUrl } = await notesData(name, description, type, rating, img.type, tripId);
- await fetch(uploadUrl, { method: 'PUT', body: img });
- router.push('/dashboard/note');
- };
-
- const handleFormSubmit = async (event) => {
- event.preventDefault();
- try {
- if (validateForm()) {
- await submit();
- console.log('Notes data stored successfully');
- }
- } catch (error) {
- toast.error('Please fill out all fields');
- }
- };
-
- const handleInputChange = (event) => {
- const { name, value, files } = event.target;
- if (name === 'img') {
- setFormFields({ ...formFields, [name]: files[0] });
- } else {
- setFormFields({ ...formFields, [name]: value });
- }
- };
-
- const types = [
- { label: 'Event', value: 'Event' },
- { label: 'Dining', value: 'Dining' },
- { label: 'Experience', value: 'Experience' },
- { label: 'General', value: 'General' }
- ];
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {img && (
-
- Selected Image:
-
-
- )}
-
-
-
-
-
-
-
- >
- );
-}
-```
-
-Letās break down the code above:
-
-- The states in the code are defined accordingly.
-- In the `useEffect` hook, `fetchTrips` runs once when the component mounts with an empty dependency array, `[]` and the trips state array is updated from the imported getTrips function.
-- `getTripIdByName` function uses the `.find()` method to return the first element in the trips array that matches the city name from the trips table.
-- `validateForm` adds the logic to check if the trimmed strings are not empty while the `img !== null` checks that the `img` is not null and an image has been selected.
-- The submit function handles the form submission in an asynchronous manner where the `notesData` is called by the client and it retrieves the `uploadUrl` from it and does a fetch to upload the file using the PUT request.
-- To display the image file's output in the browser, we use `URL.createObjectURL(img)` to generate a temporary URL.
-
-![Adding a form](/images/travel-app-guide/adding-a-form.jpeg)
-
-## Query and display notes
-
-Create a file to get all the data from the notes table, including selecting the fields in the link to the table trip via a new file, `utils/api-note-detail.js`:
-
-```js
-// File: utils/api-note-detail.js
-'use server';
-
-import { getXataClient } from '@/src/xata';
-
-export async function apiNoteDetail() {
- const xata = getXataClient();
- const notes = await xata.db.notes.select(['*', 'trip']).getMany();
- const serializedNotes = JSON.stringify(notes);
- return serializedNotes;
-}
-```
-
-The `.select()` method uses the Query API for column selection. The asterisk `(\*)` symbol in the square bracket `[]` represents all the columns from the notes table. In addition, the trip column is expanded to retrieve more information from the linked trip record.
-Next, integrate the `apiNoteDetail()` function to the dashboard note to display all the notes from the database in this new file, `app/(custom)/dashboard/note/page.js`:
-
-```js
-// File: app/(custom)/dashboard/note/page.js
-'use client';
-
-import { apiNoteDetail } from '@/utils/api-note-detail';
-import Link from 'next/link';
-import { useState, useEffect } from 'react';
-import Image from 'next/image';
-import { capitalizeWords } from '@/utils/capitalizeWords';
-
-export const revalidate = 0;
-
-export default function Note() {
- const [notes, setNotes] = useState([]);
- const [loading, setLoading] = useState(true);
-
- useEffect(() => {
- const fetchNotes = async () => {
- try {
- const notesData = await apiNoteDetail();
- setNotes(JSON.parse(notesData));
- setLoading(false);
- } catch (error) {
- console.log('Error fetching notes', error);
- setLoading(false);
- }
- };
- fetchNotes();
- }, []);
-
- return (
- <>
- Notes:
- {loading ? (
-
- Loading...
-
- ) : (
- notes.length === 0 && No notes found
- )}
-
- {!loading && (
-
- {notes.map((note) => (
-
-
-
-
- {capitalizeWords(note.trip?.city)}
- {note.name}
- Rating: {note.rating}/5
-
-
-
- ))}
-
-
- New Note +
-
-
-
- )}
- >
- );
-}
-```
-
-In the code snippets above, the same principles apply to how the [Display the trips](#display-the-trips) is carried out.
-
-![Notes view](/images/travel-app-guide/notes-view.jpeg)
-
-## Query and show a single note
-
-The display of a single note is possible by using the ID of each record information in the notes table. Create a new file called `filter.js` in the utils directory:
-
-```js
-// File: utils/filter.js
-'use server';
-
-import { getXataClient } from '@/src/xata';
-
-const xata = getXataClient();
-
-export async function filter(params) {
- const record = await xata.db.notes.read({ id: params.id });
-
- return { record };
-}
-```
-
-In the code above, the `.read()` method is used to query and destructure the id from the params object to retrieve a single record in the database.
-
-Next, create a page display and render the UI content for a single note record in `app/(custom)/dashboard/note/[id]/page.js`:
-
-```js
-// File: app/(custom)/dashboard/note/[id]/page.js
-import Header from '@/components/Heading';
-import Image from 'next/image';
-import { capitalizeWords } from '@/utils/capitalizeWords';
-import { filter } from '@/utils/filter';
-import BtnDel from '@/components/BtnDel';
-
-export default async function NoteDetail({ params }) {
- const { record } = await filter(params);
-
- const noteInfo = (
-
-
- {capitalizeWords(record.name)}
-
-
- Description:
- {record.description.split('\n').map((paragraph, index) => (
-
- {paragraph}
-
- ))}
-
-
- Type: {record.type}
-
-
- Rating: {record.rating}/5
-
-
-
-
-
-
- );
-
- return (
- <>
-
-
-
-
- {noteInfo}
- >
- );
-}
-```
-
-`import` the `filter()` function and set it to the record object, `const { record } = await filter(params)` which will read the value for each dynamic note route.
-
-![Note detail page](/images/travel-app-guide/note-detail.jpeg)
-
-For the BtnDel component, the id properties identify each unique note record. Create this file in the components directory and include the code:
-
-```js
-// File: components/BtnDel.js
-'use client';
-
-import { deleteNote } from '@/utils/delete';
-import { useRouter } from 'next/navigation';
-import ConfirmationDialog from './ConfirmationDialog';
-
-export default function BtnDel({ id }) {
- const router = useRouter();
-
- const handleDelete = async () => {
- try {
- await deleteNote(id);
- router.push('/dashboard/note/');
- } catch (error) {
- console.error('Error deleting note:', error);
- }
- };
-
- return (
- <>
-
- >
- );
-}
-```
-
-The code above shows a confirmation dialog that displays a message to the user if you want to delete a note. Also, the imported `deleteNote` function uses the Xata client SDK with the id of the specified record on the `.delete()` method.
-
-Create these files `components/ConfirmationDialog.js` and `utils/delete.js`. Copy-paste the [confirmation dialog](https://github.com/Terieyenike/track-trip-dashboard-with-xata-next/blob/main/components/ConfirmationDialog.js) and [deleteNote](https://github.com/Terieyenike/track-trip-dashboard-with-xata-next/blob/main/utils/delete.js) code in their respective files.
-
-![Delete note view](/images/travel-app-guide/delete-note-view.jpeg)
-
-## Deleting a single trip
-
-Letās return to the query and show a single trip code. Update the code so that we get the result of each note based on the trip detail and delete the trip when the delete button is clicked.
-
-```js
-// File: app/(custom)/dashboard/trip/[id]/page.js
-
-import BtnDelTrip from '@/components/BtnDelTrip';
-import { getXataClient } from '@/src/xata';
-
-export default async function Trip({ params }) {
- const { data } = await filterTrip(params);
-
- const xata = getXataClient();
-
- const notes = await xata.db.notes.filter('trip.id', data?.id).getMany();
-
- return (
- <>
-
-
- ā¦
-
-
- Edit
-
-
-
- Notes:
-
- {notes.map((note) => (
-
-
-
-
-
- {capitalizeWords(note.name)}
- Rating: {note.rating}/5
-
-
-
-
- ))}
- ā¦
-
-
- >
- );
-}
-```
-
-For this code block, import the Xata client and the component handling, deleting both the trip and note detail at the same time where the id is passed as a property, ` ` to identify the ID of the linked table trip ID and the note ID.
-
-In addition, we filter the records of the notes column from the database using only the ID with the `.filter()` method, `const notes = await xata.db.notes.filter("trip.id", data?.id).getMany()`. Loop through the notes with the `.map()` method and access the columns of each Note record using their properties attached to the Link component to open the note on a different route.
-
-![Trip and note detail](/images/travel-app-guide/trip-note-detail.jpeg)
-
-Create these files `components/BtnDelTrip.js` and `utils/delete-trip.js`. Copy-paste the file content with the code in the [BtnDelTrip](https://github.com/Terieyenike/track-trip-dashboard-with-xata-next/blob/main/components/BtnDelTrip.js) and [delete-trip](https://github.com/Terieyenike/track-trip-dashboard-with-xata-next/blob/main/utils/delete-trip.js) files, respectively.
-
-![Delete trip detail](/images/travel-app-guide/delete-trip-detail.jpeg)
-
-## Deploy to Vercel
-
-Vercel can access a GitHub repository with these steps and deploy the project.
-
-- Push all the application code to GitHub locally
-- On your Vercel dashboard, create a New Project
-- Link the new project with the GitHub repository using the Import button
-- In Settings, update the Environment Variables to match with your local `.env` file
-- Deploy
-
-## Conclusion
-
-This guide walked through the seamless integration of Xata and Next.js to build a travel-tracking application. With these technologies, you can now relive and document all the key moments from your trips. Integrating Tailwind and React Toastify helped to create a better user interface and experience.
-
-The union between Xata and Next.js helps developers build dynamic and serverless applications, whether you are an experienced developer or a novice. It is an all-encompassing way to deliver a 360Āŗ approach to full-stack CRUD applications.
-
-Happy coding!
-
-More information
-For more detailed insights, explore the references in this post below:
-
-- [Demo track trips](https://track-trip-dashboard-with-xata-next.vercel.app/)
-- [GitHub repo](https://github.com/Terieyenike/track-trip-dashboard-with-xata-next)
-- [Xata file attachments](https://xata.io/docs/sdk/file-attachments)
-- [Route groups in Next.js](https://nextjs.org/docs/app/building-your-application/routing/route-groups)
-- [Next.js with Xata](https://xata.io/docs/getting-started/nextjs)
-- [Add notifications to apps](https://github.com/fkhadra/react-toastify#readme)
-- [Fetching, caching, revalidating](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating)
diff --git a/xata-trpc-clerk.mdx b/xata-trpc-clerk.mdx
deleted file mode 100644
index 35ce3a82..00000000
--- a/xata-trpc-clerk.mdx
+++ /dev/null
@@ -1,180 +0,0 @@
----
-title: 'Xata, tRPC, and Clerk: the killer combo'
-description: 'Build a fully typed application with Clerk for authentication and tRPC for a robust stack.'
-image:
- src: https://raw.githubusercontent.com/xataio/mdx-blog/main/images/nextjs-trpc-clerk.png
- alt: Data platforms
-author: Fabien Bernard
-date: 01-13-2023
-tags: ['nextjs', 'trpc', 'clerk']
-published: true
-slug: nextjs-trpc-clerk
----
-
-Building an application is not easy, or to be more accurate, this usually starts easy and becomes complicated. Letās see why. What are the usual challenges we need to deal with?
-
-## User Management
-
-Every application has users, and this comes with a lot of specific problems:
-
-- How do my users log in?
-- How do I store their password? This is quite sensitive.
-- What if I want to connect through my Google or GitHub account?
-- But, if a user did connect with an email and after with GitHub. Should I link the accounts?
-- How can I ban a user?
-- ā¦
-
-Luckily, [Clerk](https://clerk.dev/) solves all those issues for us (with a nice free tier). The only thing we need to care about is storing the `userId` and making sure a user can only see his own data.
-
-## Features Management
-
-One of the main challenges in building an application is to add or remove features. Indeed, without the correct tooling, itās easy to forget something. Leading to a bug and unhappy customers at the end (or even worse if you are discovering a critical bug on a Sunday).
-
-Again, we can help ourselves using the correct tool or, more precisely, the combination of tools!
-
-Using TypeScript as a base, in combination with Xata and [tRPC](http://trpc.io), we can have full type safety from the database schema to our React components.
-
-For example, if you decide to remove a field from your database, with this setup in place, you can run `xata codegen` and use TypeScript to track **every** usage of this field in your **entire** application, backend side & frontend side.
-
-Of course, this is not 100% bullet-proof guarantee. But trust us: this will keep you on the safe side for most cases.
-
-## Getting Started!
-
-To help you in this journey, Xata has a starter with already everything set up for you.
-
-The starter has a simple multi-user Todo application, this should provide you with enough guidance to build a solid stack for your next amazing project!
-
-To get started, letās start a new project together and see how everything works.
-
-```bash
-$ npx degit xataio/examples/apps/starter-nextjs-trpc-clerk my-todo-clerk-app
-$ cd my-todo-clerk-app
-$ npm install # yarn install or pnpm install
-```
-
-So far so good, now letās connect our application to Clerk and Xata.
-
-Go [Xata](https://xata.io) and create an account. Login and connect the project:
-
-```bash
-$ npm run xata -- auth login # needed only once$ npm run xata:init
-```
-
-Now create an account and a project at [Clerk](https://clerk.dev).
-
-You can then go to [Clerk + Next.js integration documentation](https://clerk.dev/docs/nextjs/set-environment-keys) and copy the credentials to your `.env` file. Thatās it! Just run `npm run dev` and you have a working application.
-
-## Code Walkthrough
-
-```tsx
-// server/trpc.ts
-import { initTRPC, TRPCError } from '@trpc/server';
-import { Context } from './context';
-
-const t = initTRPC.context().create();
-
-const isAuthed = t.middleware(({ next, ctx }) => {
- if (ctx.user === null) {
- throw new TRPCError({ code: 'UNAUTHORIZED' });
- }
-
- return next({
- ctx: {
- user: ctx.user
- }
- });
-});
-
-export const router = t.router;
-export const publicProcedure = t.procedure;
-export const protectedProcedure = t.procedure.use(isAuthed);
-```
-
-This is where everything starts, we are exposing two procedures, one for public routes and one with auth. All the clerk logic lives in `server/context.ts`, and the context is injected `pages/api/trpc/[trpc].ts`.
-
-The idea is quite simple, if Clerk has some information about the user, itās injected in the `context`, checking if the user is here, we canāt filter authenticate users. Letās take `todo/list` as an example:
-
-```tsx
-// server/routers/todo.ts
-import { protectedProcedure, router } from '../trpc';
-import { z } from 'zod';
-import { getXataClient } from '../../utils/xata';
-import { TRPCError } from '@trpc/server';
-
-export const todoRouter = router({
- /**
- * List user todo items.
- */
- list: protectedProcedure.query(({ ctx }) => {
- const userId = ctx.user.id; // here `ctx.user` is typed as `User`
- const xata = getXataClient();
-
- return xata.db.items.filter({ userId }).getAll();
- })
- /* ... */
-});
-```
-
-Here are using `protectedProcedure` since we need the user information for our database query. The entire function is type-safe, I have access to all userās data in this function and we are simply using the `user.id` as a filter. Please note that we donāt have any types annotation as so ever and just returning raw Xata response.
-
-Looking at `pages/index.ts`, we can look at how to use this route in our application.
-
-
-
-Here, everything is type-safe, serialized through the tRPC layer, and ready to be consumed.
-
-On the React part, tRPC provides you a typed version of react-query, this means you will have all the benefits of React-Query like the query cache but without having to define your own fetcher.
-
-```tsx
-// Function block of `Hello` inside `pages/index.tsx`
-
-const utils = trpc.useContext();
-
-const { mutate: addTodo } = trpc.todo.add.useMutation({
- onMutate: async (newTodo) => {
- await utils.todo.list.cancel();
-
- const previousTodos = utils.todo.list.getData();
-
- const optimisticTodo: RouterOutputs['todo']['list'][-1] = {
- title: newTodo.title,
- createdAt: new Date().toISOString(),
- userId: 'me',
- id: 'optimistic_' + self.crypto.randomUUID(),
- isCompleted: false
- };
- utils.todo.list.setData(undefined, (old) => (old ? [...old, optimisticTodo] : [optimisticTodo]));
-
- return { previousTodos };
- },
- onError: (_err, _newTodo, context) => {
- utils.todo.list.setData(undefined, context?.previousTodos);
- },
- onSettled: () => {
- utils.todo.list.invalidate();
- }
-});
-```
-
-A lot of interesting patterns in this `addTodo` mutation! First, we are using `trpc.useContext()`, this is the way to access react-query client with all the types backed! Here, we are implementing a simple optimistic UI: we donāt wait for the backend response to update the UI state!
-
-To do so, we need to create a fake entry in our cache, with the same type as our `todo/list` return type. We can simply use this `RouterOutputs["todo"]["list"][-1]` that will infer the type from our router.
-
-Doing so, if tomorrow somebody adds a property to this todo list, this part of the code will not compile until we provide a value for the introduced property, one less bug to fix later!
-
-You also have `RouterInputs` and `ReactQueryOptions` as inferred types exported inside `utils/trpc.ts`. Those helpers are useful to avoid duplicating types. As code, we should always try to avoid duplicate types when we can, so just changing the return type or the parameter of one of our routes will propagate through the entire codebase.
-
-## Final Considerations
-
-And this is it for the big lines, the easiest way to learn more about the project is to try to destroy it! Add or remove some code and see how everything plays together.
-
-Fork this [example](https://github.com/xataio/examples/tree/main/apps/starter-nextjs-trpc-clerk) and sign up with [Xata](https://xata.io) today to get started!
-
-Have fun! If you have any suggestions on how to improve this starter or encounter any friction, we are available on [our Discord channel](https://xata.io/discord)
diff --git a/xata-workers-client-side-database.mdx b/xata-workers-client-side-database.mdx
deleted file mode 100644
index 9b77fe68..00000000
--- a/xata-workers-client-side-database.mdx
+++ /dev/null
@@ -1,143 +0,0 @@
----
-title: 'Xata Workers: client-side database access without client-side secrets'
-description: 'Xata Workers act as middleware to simplify database access and deploy front-end functions on databases.'
-image:
- src: https://raw.githubusercontent.com/xataio/mdx-blog/main/images/xata-workers.png
- alt: Winners
-author: Alexis Rico
-authorEmail: alexis@xata.io
-date: 01-01-2023
-tags: ['serverless']
-published: true
-slug: xata-workers
----
-
-
- The Workers experiment was a great learning experience for us, but we've decided to deprecate it in favor of
- strengthening Xata's integrations with full-stack frameworks and their server-side data fetching capabilities. You can
- check out our public [Roadmap](https://xata.io/roadmap) and get in touch via [Discord](https://xata.io/discord), or
- [Contact Support](https://support.xata.io/hc/en-us/requests/new) if you have any questions.
-
-
-In the last few years, there's been a rise of Jamstack, and new ways of thinking about the cloud that some people call serverless or edge computing. Instead of maintaining dedicated servers to run a single service, these architectures split applications in smaller services or functions.
-
-By simplifying the state and context of our applications, we can benefit from external providers deploying these functions in dozens of servers across the globe. This architecture benefits the developer and user experience alike. Developers don't have to manage servers, and users don't have to experience latency. Your application simply scales, even if you receive hundreds of thousands of unexpected visitors.
-
-When it comes to databases though, we still struggle with the complexity of orchestrating replication, failover and high availability. Traditional databases are difficult to scale horizontally and usually require a lot of infrastructure maintenance or learning complex database optimization concepts.
-
-At Xata, we are building a modern data platform designed for scalable applications. It allows you to focus on your application logic, instead of having to worry about how the data is stored.
-
-## Making databases accessible to everyone
-
-We started Xata with the mission of helping developers build their applications and forget about maintaining the underlying infrastructure.
-
-With that mission in mind, we asked ourselves: how can we make databases accessible to everyone? How can we provide a delightful experience for a frontend engineer or designer using a database?
-
-To begin with, we built an appealing web dashboard, that any developer --- no matter their experience --- can be comfortable using to work with their data.
-
-Whether they're defining or refining their schema, viewing or adding records, fine-tuning search results with query boosters, or getting code snippets to speed up development with our SDK. We believe that only the best user experience can provide the best development experience.
-
-We identified a major challenge amongst our user base early on. Many front-end developers want to access their database from client-side code.
-
-Allowing access to a database from client-side code comes with several security risks if not done properly. For example, someone could inspect the code, find the credentials, and if they weren't scoped to a single operation, potentially query or modify other parts of the database. Unfortunately, this is a common reason for data leaks and security breaches.
-
-It was a hard problem to solve, and after plenty of brainstorming, we agreed on two potential ways forward: implementing row-level access rules or writing API routes that talked to the database from server code.
-
-Row-level access rules are a good way to solve this, but they would have required us to define our own opinionated language. For our users, it would have been hard to migrate away when they outgrow this solution. Instead, we preferred to focus on making serverless functions easier for our users.
-
-Typically, serverless functions require you to either choose a full-stack framework or manually write, compile, deploy and use them. This generally adds a lot of cognitive overhead even to choose the right solution. We wanted to simplify accessing the database from the frontend without sacrificing flexibility for developers. This is why we decided to build Xata Workers.
-
-## Xata Workers
-
-A Xata Worker is a function that a developer can write in JavaScript or TypeScript as client-side code. Rather than being executed client-side, it will actually be executed on Cloudflare's global network.
-
-You can think of a Xata Worker as a `getServerSideProps` function in Next.js or a `loader` function in Remix. You write your server logic in a function and our tooling takes care of deploying and running it server-side for you (yes, it's that easy).
-
-The main difference with other alternatives is that Xata Workers are, by design, agnostic to a framework or hosting provider. You can use them to build any kind of application or website, and even upload it as static HTML in a service like GitHub Pages or S3. You don't need a full-stack web framework to use Xata Workers.
-
-With our command line tool, we handle the build and deployment process. When the function is invoked, the Xata Worker actually makes a request to a serverless function over the network.
-
-```tsx
-import { useQuery } from '@tanstack/react-query';
-import { xataWorker } from '~/xata';
-
-const listProducts = xataWorker('listProducts', async ({ xata }) => {
- return await xata.db.products.sort('popularity', 'desc').getMany();
-});
-
-export const Home = () => {
- const { data = [] } = useQuery(['products'], listProducts);
-
- return (
-
- {data.map((product) => (
-
- ))}
-
- );
-};
-```
-
-In the code snippet above, you can see a React component that connects to an e-commerce database with products on sale. Inside the UI component, with a popular client-side data fetching library, data is retrieved from the serverless function and for each product it renders another component in a grid.
-
-As you can see a Xata Worker is a function that wraps any user-defined code and receives an instance of our SDK as a parameter. This instance has access to the database and, given that the code doesn't run on the browser anymore, your secrets are not exposed for malicious usage.
-
-When using a Xata Worker in TypeScript, our command line tool also generates custom types based on your schema. These types offer type-safety for your queries or mutations, and improve your developer experience by adding extra intellisense to your IDE.
-
-
-
-A Xata Worker, like any other function, can receive additional parameters that pass application state, context or user tokens. The code you write in the function can either return an object that will be serialized over the network with a superset of JSON to support dates and other non-primitive data types, or a full response with a custom status code and headers.
-
-Developers can write all their logic, including their own authentication and authorization. Unlike complex row level access control rules, you can easily express your logic without constraints and even unit test it with the rest of your code.
-
-## How we use Cloudflare
-
-We are happy to join the [Supercloud](https://blog.cloudflare.com/welcome-to-the-supercloud-and-developer-week-2022/) movement, Cloudflare has an excellent track record, and we are using[Cloudflare Workers for Platforms](https://developers.cloudflare.com/cloudflare-for-platforms/workers-for-platforms/) to host our serverless functions. By using the Workers isolated execution contexts we reduce security risks of running untrusted code on our own while being close to our users, resulting in super low latency.
-
-All of it, without having to deploy extra infrastructure to handle our user's application load or ask them to configure how the serverless functions should be deployed. It really feels like magic! Now, let's dive into the internals of how we use Cloudflare to run our Xata Workers.
-
-For every workspace in Xata we create a Worker Namespace, a Workers for Platform concept to organize Workers and restrict the routing between them. We used Namespaces to group and encapsulate the different functions coming from all the databases built by a client or team.
-
-When a user deploys a Xata Worker, we create a new Worker Script, and we upload the compiled code to its Namespace. Each script has a unique name with a compilation identifier so that the user can have multiple versions of the same function running at the same time.
-
-During the compilation, we inject the database connection details and the database name. This way, the function can connect to the database without leaking secrets and restricting the scope of access to the database, all of it transparently for the developer.
-
-When the client-side application runs a Xata Worker, it calls a Dispatcher function that processes the request and calls the correct Worker Script. The dispatcher function is also responsible for configuring the CORS headers and the log drain that can be used by the developer to debug their functions.
-
-
-
-By using Cloudflare, we are also able to benefit from other products in the Workers ecosystem. For example, we can provide an easy way to cache the results of a query in Cloudflare's global network. That way, we can serve the results for read-only queries directly from locations closer to the end users, without having to call the database again and again for every Worker invocation. For the developer, it's only a matter of adding a "cache" parameter to their query with the number of milliseconds they want to cache the results in a KV Namespace.
-
-```tsx
-import { xataWorker } from '~/xata';
-
-const listProducts = xataWorker('listProducts', async ({ xata }) => {
- return await xata.db.products.sort('popularity', 'desc').getMany({
- cache: 15 * 60 * 1000 // TTL
- });
-});
-```
-
-In development mode, we provide a command to run the functions locally and test them before deploying them to production. This enables rapid development workflows with real-time filesystem change monitoring and hot reloading of the workers code. Internally, we use the latest version of miniflare to emulate the Cloudflare Workers runtime, mimicking the real production environment.
-
-## Conclusion
-
-Xata is no longer in Beta and is now in General Availability for all to use! We offer a generous free tier that allows you to build and deploy your applications and only pay to scale them when you actually need it.
-
-You can [sign up for free](https://app.xata.io/), create a database in seconds, and enjoy features such as branching with zero downtime migrations, search and analytics, transactions, and many others. Check out our [website](https://xata.io/) to learn more!
-
-We are very proud of our collaboration with Cloudflare for this new feature. Processing data closer to where it's being requested is the future of computing and we are excited to be part of this movement. We look forward to seeing what you build with Xata Workers.
diff --git a/xataform-is-here.mdx b/xataform-is-here.mdx
deleted file mode 100644
index 97c99720..00000000
--- a/xataform-is-here.mdx
+++ /dev/null
@@ -1,154 +0,0 @@
----
-title: 'XataForm is here!'
-description: 'New open-source project to deal with your forms, survey and quizzes!'
-image:
- src: https://raw.githubusercontent.com/xataio/mdx-blog/main/images/bulletproof-docs-links.png
- alt: Xata
-author: Fabien Bernard
-date: 05-04-2023
-tags: ['announcements', 'opensource']
-published: true
-slug: xataform-is-here
----
-
-## Introduction
-
-This is it! Finally! After few months of crafting this new product/example/playground, XataForm made it and is now open-source and ready to be used by you!
-
-Waitā¦ What is XataForm? š¤ Iām glad you asked!
-
-You probably know the amazing [Typeform](https://www.typeform.com/), if you donāt, please have a look, itās product to create forms. Why do we need them?
-
-- Creating survey to understand your customer
-- Organizing your next crĆŖpes party
-- Ask for feedbacks
-- ā¦
-
-And XataForm in this story? XataForm is a light clone of Typeform, build on top of Xata, with our favorite stack: Next.js, trpc, clerk and vercel. We are doing this for multiple reasons. First, we wanted to stress test Xata in a production-ready application, so we can discover all the friction points, see whatās easy, whatās not, and learn about our own product! #dogFooding
-
-Second, we are using a lot of forms internally, and we are quite sure that you also do. As far as we know, we didnāt find any open-source solution to handle this need, so you can own your data and customize the product to your specific needs.
-
-Third and last, this is fun! You canāt imagine how many hidden challenges you have building this kind of product! As example, we tried to make everything accessibleā¦ and oh boy, this was challenging! I will probably talk more about this in future blog posts!
-
-## Tour of the product
-
-Letās be honest, if you are familiar with Typeform, you should feel at home. But just in case, letās have a quick tour together.
-
-### Dashboard
-
-
-
-With all the simplicity, everything starts in your dashboard, here you can create a new form and manage existing ones.
-
-Letās create our first form:
-
-
-
-And just like this, we are ready to prepare our next crĆŖpes party! (yes, Iām french and love clichĆ©s š
)
-
-Clicking on the `+` button I can create new questions
-
-
-
-I can edit the order of my questions by drag\&dropping them on the left sidebar
-
-
-
-Now that Iām happy with the content, I can click on the top right `Preview` button to visualize the form in its published shape.
-
-Everything looks nice (I hope), this is time to publish our first form!!!!
-
-
-
-Tadaaaaaaa š„³š„³š„³
-
-Now we can click on `Live` to copy the public form URL, and share it to our friends.
-
-_Few moments later_
-
-
-
-Oh oh, we do have some responses! š Letās see them in our `Results` tab
-
-
-
-Letās take an instant to point out a little subtility here, on the right side, you can see that we are on `Version 1`.
-
-Indeed, the version will increase every time I push the `Publish` button! Every version has its own Xata table with the proper schema under the hood.
-
-This is to ensure we are never losing any data and everything is in sync. Here is how this looks in Xata UI.
-
-
-
-We are using a separate database called `xataform-answers` with generated table following this pattern: `{formId}-v{version}`
-
-The published form is cached for better performance, and if somebody is submitting an old form, their answer will go to the proper version, as simple as that.
-
-With this in mind, you should try to publish as little as possible, because doing it often will make your life harder to analyze the results. Changing a survey in the middle of its lifetime is a bad practice as this will spoil your results! Not a big deal for our crĆŖpes party, but if Iām asking one more question, the previous version will not have this question!
-
-I guess thatās it for the tour! No illustration yet, neither logic between questions, but those is coming! I canāt wait to see what you will do with this new tool! Time for you to play!
-
-## How do I play?
-
-You have two options:
-
-- Use the [hosted version](https://xataform.com/), but just so you know, we will have access to all the answers, questions, and so on. This is for demo purposes only.
-- Clone and host the tool yourself. It's easy to deploy on Vercel/Netlify + Xata.
-
-All the instructions are inside the Readme of the [GitHub repository](https://github.com/xataio/xataform). If you encounter any problems, or have any questions or features requests, just open an issue or go to our [discord](https://xata.io/discord)