Introduction
Baeta is a schema-first, type-safe GraphQL framework for building modular APIs. The runtime is small and environment-agnostic; the type generation does the heavy lifting so resolvers stay focused on data, not plumbing.
Why Baeta
- Schema-first. Describe your API in GraphQL SDL, then implement resolvers against generated types.
- Modular. Split a large schema across modules and let Baeta stitch them together. Each module owns its types, resolvers, and tests.
- Opt-in features. Add what you need — authorization, complexity limits, caching, federation, pagination — each as a separate package.
How it Works
1. Define your schema
type User {
id: ID!
name: String!
email: String!
age: Int
}
input UserWhereUnique {
id: ID
email: String
}
type Query {
user(where: UserWhereUnique!): User!
users: [User!]!
}
2. Implement your resolvers
import { UserModule } from "./typedef.ts";
const { Query } = UserModule;
const userQuery = Query.user.resolve(({ args }) => {
return dataSource.user.find(args.where);
});
const usersQuery = Query.users.resolve(() => {
return dataSource.user.findMany();
});
export default Query.$fields({
user: userQuery,
users: usersQuery,
});
3. Add authorization
import { auth, rule, scope } from "./lib/auth.ts";
import { UserModule } from "./typedef.ts";
const { Query } = UserModule;
const userQuery = Query.user
.$use(auth(rule.or(scope.isPublic, scope.isLoggedIn)))
.resolve(async ({ args }) => {
// ...
});
4. Add caching
import { createCache, defineQuery } from "@baeta/cache";
import { redisClient } from "./lib/redis.ts";
import { UserModule } from "./typedef.ts";
const { Query, Mutation } = UserModule;
export const userCache = createCache(redisClient, {
name: "UserCache",
parse: JSON.parse,
serialize: JSON.stringify,
})
.withQueries({
findUser: defineQuery({
resolve: async (args: { id: string }) => {
return dataSource.user.findUnique({ where: args });
},
}),
})
.build();
const userQuery = Query.user.map(({ args }) =>
userCache.queries.findUser({ id: args.where.id }),
);
const updateUserMutation = Mutation.updateUser
.$use(async (next) => {
const user = await next();
if (user) await userCache.update(user);
return user;
})
.resolve(async ({ args }) => {
// ...
});
Compatibility
Baeta produces a standard GraphQLSchema, so it drops into any GraphQL server — Yoga, Apollo, Mercurius — and pairs with the data layer of your choice (Prisma, Drizzle, Kysely, raw drivers).
The build tooling needs Node.js, but the runtime is environment-agnostic. Anywhere a GraphQLSchema runs, Baeta runs:
- Node.js, Bun, Deno
- Cloudflare Workers
- AWS Lambda, Vercel Edge Functions
- Any other JavaScript runtime