Skip to main content

Baeta

Schema first without the hassle

Schema First

Define your API contract upfront with a clear, schema-first approach that ensures consistency and maintainability.

Modular By Design

Build your API piece by piece. Baeta's modular architecture lets you organize code into small, maintainable modules.

Type Safe

Focus on your logic while Baeta handles type safety with automatic code generation and TypeScript integration.

Flexible & Extensible

Use only what you need. Add powerful features through official extensions when your API grows.

Define Your API Contract

Start with a clear, readable schema that serves as your API contract. Baeta's schema-first approach lets you focus on designing the perfect API before diving into implementation details.

modules/user/user.gql
type User {
id: ID!
name: String!
email: String!
age: Int
}

input UserWhereUnique {
id: ID
email: String
}

type Query {
user(where: UserWhereUnique!): User!
users: [User!]!
}

Write Clean, Type-Safe Resolvers

Focus purely on your business logic while Baeta handles all type definitions and safety. No more type gymnastics or complex and nested resolver patterns - just clean, straightforward code.

modules/user/resolvers.ts
import { getUserModule } from "./typedef";

const { Query } = getUserModule();

Query.user(({ args }) => {
return dataSource.user.find(args.where);
});

Query.users(() => {
return dataSource.user.findMany();
});

Compose and Extend

Build your API like building blocks. Baeta's modular architecture lets you split your schema into small, focused pieces that are easy to maintain. Extend existing types seamlessly as your API grows.

modules/user-photos/user-photos.gql
type Photo {
id: ID!
url: String!
description: String!
postedBy: User!
}

input PhotoCreateData {
url: String! @trim
description: String!
userId: ID!
}

extend type User {
photos: [Photo!]!
}

Scope-Based Authorization

Secure your API with granular, scope-based authorization. Define permissions directly in your schema and let Baeta handle the rest.

const { Query, Mutation } = getUserModule();

Query.users.$auth({
$or: {
isPublic: true,
isLoggedIn: true,
},
});

Simple, Effective Caching

Add automatic caching to your queries with minimal setup. Update cached data easily and predictably when mutations occur.

import { getUserModule } from './typedef.ts';

const { User, Query } = getUserModule();

export const userCache = User.$createCache();

Query.user.$useCache(userCache);
Query.users.$useCache(userCache);

Mutation.updateUser.$use(async (params, next) => {
const user = await next();
await userCache.save(user);
return user;
});

Powerful Custom Directives

Add custom behavior exactly where you need it. Create your own directives for validation, transformation, or any custom logic. Baeta makes it simple to apply complex behaviors declaratively in your schema.

const trimDirective = createInputDirective({
name: "trim",
target: "scalar",
resolve: ({ getValue, setValue }) => {
const value = getValue();
if (typeof value === "string") {
setValue(value.trim());
}
},
});