fauna-typed v0.2.11
Typesafe Client with built-in State Management for Fauna
!Only React supported at the moment!
Motivation
Fauna provides with FQL 10 a typescript-like language, and offers built-in type-safety with Schema enforcement (TODO: Link). This client streamlines the typesafe experience between Fauna and your React project by
- syncing the types from Fauna to your Typescript project
- transform and compose your typesafe functions on the fly to FQL.X post Requests
- Typesafe
- State Management built-in for Optimistic Response
- TODO Isomorphic (Code can run on Fauna or in Typescript runtime)
- TODO Svelte Support
Getting Started
Install
pnpm install fauna-typed
pnpm install ts-node
Create schema
- Create
fauna.schema.json
file on the root level (same level aspackage.json
) - And add your schema into the file:
Example schema
{
"Car": {
"fields": {
"plate": "string",
"brand": "string",
"owner": "Customer"
},
"constraints": {
"required": []
}
},
"Customer": {
"fields": {
"name": "string",
"cars": "Cars[]"
},
"constraints": {
"required": ["name"]
}
}
}
Extend package.json
"scripts": {
...,
"fauna:generate": "ts-node node_modules/fauna-typed/src/converter.ts"
},
Generate TS interfaces
pnpm fauna:generate
You should see the following folder structure as the output
|- fqlx-generated
| |- collectionsWithFields.ts
| |- typedefs.ts
Configure Client
Wrap your App with the <FaunaProvider>
Nextjs
import { FaunaProvider } from 'fauna-typed';
import './globals.css';
import dynamic from 'next/dynamic';
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang='en'>
<body>
<FaunaProvider config={{ faunaSecret: <userFaunaAccessToken> }} >
{children}
</FaunaProvider>
</body>
</html>
);
}
FaunaProvider
Property | Mandatory? | Description | Example |
---|---|---|---|
faunaSecret | ✔️ | The secret to authenticate with Fauna. Recommended to use an accessToken from your users, that you received after user login | faunaSecret: useAuth() |
loader | 🗙 | You can provide a skeleton loader component that displays automatically that component during suspense. | loader: {<Skeleton />} |
endpoint | 🗙 | Specify the Fauna endpoint. The default is Fauna cloud https://db.fauna.com . If you're not a Fauna Beta tester or using the Fauna Docker version, you probably don't need this | endpoint: new URL('https://db.fauna-preview.com') |
Use Client
import { useQuery as query } from 'fauna-typed';
const OwnedCars = (customerId) => {
return ({
query.Customer.firstWhere((customer) => {customer.id == customerId}).cars.exec().map((car) => {
return (
<div>
{car.plate}
</div>
)
});
});
}
export default OwnedCars;
Execute query | .exec()
FQL 10 follows the same syntax as Typescript. e.g., .map()
can be executed on Fauna
or on Browser/Edge/Node
side. That means we need to tell the app which part of the code should run on Fauna
side and which code should run on Browser/Edge/Node
side.
For that, we use the
.exec()
function. Put it at the end of your query, where you want to have a handover from Fauna
to Browser/Edge/Node
side.
Everything before
.exec()
will run onFauna
side. Everything after.exec()
will run onBrowser/Edge/Node
side.
Projection | .project()
With projection, you can define what data you want to return. With this, you can avoid over- and under-fetching. Also, you can fetch child fields.
query.Customer.first().cars.project({
plate = true,
brand = true,
owner = true,
})
.exec()
Fetch child fields
query.Customer.first().cars.project({
plate = true,
brand = true,
owner = {
name = true,
},
})
.exec()
Unfortunately, we have not yet found a way to eliminate the
= true
appendix as part of the projection. If you know how to achieve this, we're happy to get your input (Contributions are also more as welcome). We're looking for something like that: .project({ plate, brand, owner = { name, }, }