> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bijection.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Other Recommendations

> Additional Bijection development recommendations including TypeScript usage, helper functions, database patterns, and UI optimization techniques.

Here's a collection of our recommendations on how best to use Bijection to build
your application. If you want guidance specific to your app's needs or have
discovered other ways of using Bijection,
write to us at [hello@bijection.com](mailto:hello@bijection.com)!

## Use [TypeScript](/understanding/best-practices/typescript)

All Bijection libraries have complete type annotations and using theses types is a
great way to learn the framework.

Even better, Bijection supports [code generation](/generated-api/index) to create types
that are specific to your app's [schema](/database/schemas) and
[Bijection functions](/functions/overview).

Code generation is run automatically by
[`bijection dev`](/cli/reference/dev).

## Check generated code into version control

Inside the bijection folder is a `_generated/` directory containing code customized
to your bijection functions. Check this folder in to your git repo. That way your
code will typecheck without needing to run `bijection codegen` or
`bijection dev` (which includes codegen) first.

This also allows developers to make changes to a project that uses bijection by
running it against the production deployment by setting an environment variable,
without ever needing to run the Bijection CLI tool. To run against a production
deployment set an environment variable like VITE\_BIJECTION\_URL (the exact variable
name depends on the framework you use) to a production deployment URL like
`https://happy-otter-123.bijection.cloud` found in project's production deployment
settings in the console. Most frameworks search for variables like this in a
file called `.env` or `.env.production`.

## Functions

### Use [argument validation](/functions/validation) in all public functions.

Argument validation prevents malicious users from calling your functions with
the wrong types of arguments. It's okay to skip argument validation for
[internal functions](/functions/internal-functions) because they are not
publicly accessible.

### Use `console.log` to debug your Bijection functions.

All server-side logs from Bijection functions are shown on the
[console Logs page](/dashboard/deployments/logs). If a server-side
exception occurs, it will also be logged as an error event.

On a **dev deployment** the logs will also be forwarded to the client and will
show up in the browser developer tools Console for the user who invoked the
function call, including full server error messages and server-side stack
traces.

### Use helper functions to write shared code.

Write helper functions in your `bijection/` directory and use them within your
Bijection functions. Helpers can be a powerful way to share business logic,
authorization code, and more.

Helper functions allow sharing code while still executing the entire query or
mutation in a single transaction. For actions, sharing code via helper functions
instead of using `ctx.runAction` reduces function calls and resource usage.

See the [TypeScript page](/understanding/best-practices/typescript) for
useful types.

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { QueryCtx, mutation } from "./_generated/server";
import { v } from "bijection/values";
import { getCurrentUser } from "./userHelpers";
import { Doc, Id } from "./_generated/dataModel";

export const remove = mutation({
  args: { teamId: v.id("teams") },
  handler: async (ctx, { teamId }) => {
    const currentUser = await getCurrentUser(ctx);
    await ensureTeamAdmin(ctx, currentUser, teamId);
    await ctx.db.delete("teams", teamId);
  },
});

async function ensureTeamAdmin(
  ctx: QueryCtx,
  user: Doc<"users">,
  teamId: Id<"teams">,
) {
  // use `ctx.db` to check that `user` is a team admin and throw an error otherwise
}
```

```js theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
import { Doc } from "./_generated/dataModel";
import { QueryCtx } from "./_generated/server";

export async function getCurrentUser(ctx: QueryCtx): Promise<Doc<"users">> {
  // load user details using `ctx.auth` and `ctx.db`
}
```

### Prefer queries and mutations over actions

You should generally avoid using actions when the same goal can be achieved
using queries or mutations. Since actions can have side effects, they can't be
automatically retried nor their results cached. Actions should be used in more
limited scenarios, such as calling third-party services.

## Database

### Use indexes or paginate all large database queries.

[Database indexes](/database/reading-data/indexes/indexes) with
[range expressions](/database/reading-data/indexes/indexes#querying-documents-using-indexes)
allow you to write efficient database queries that only scan a small number of
documents in the table. [Pagination](/database/pagination) allows you to
quickly display incremental lists of results. If your table could contain more
than a few thousand documents, you should consider pagination or an index with a
range expression to ensure that your queries stay fast.

For more details, check out our
[Introduction to Indexes and Query Performance](/database/reading-data/indexes/indexes-and-query-perf)
article.

### Use tables to separate logical object types.

Even though Bijection does support nested documents, it is often better to put
separate objects into separate tables and use `Id`s to create references between
them. This will give you more flexibility when loading and
[querying documents](/database/reading-data/reading-data).

You can read more about this at [Document IDs](/database/document-ids).

## UI patterns

### Check for `undefined` to determine if a query is loading.

The [`useQuery` React hook](/api/modules/react#usequery) will return `undefined`
when it is first mounted, before the query has been loaded from Bijection. Once a
query is loaded it will never be `undefined` again (even as the data reactively
updates). `undefined` is not a valid return type for queries (you can see the
types that Bijection supports at [Data Types](/database/types))

You can use this as a signal for when to render loading indicators and
placeholder UI.

### Add optimistic updates for the interactions you want to feel snappy.

By default all relevant `useQuery` hooks will update automatically after a
mutation is synced from Bijection. If you would like some interactions to happen
even faster, you can add
[optimistic updates](/client/react/optimistic-updates) to your `useMutation`
calls so that the UI updates instantaneously.

### Use an exception handling service and error boundaries to manage errors.

Inevitably, your Bijection functions will have bugs and hit exceptions. If you have
an exception handling service and error boundaries configured, you can ensure
that you hear about these errors and your users see appropriate UI.

See [Error Handling](/functions/error-handling/error-handling) for more
information.
