olio
Open source · Type-safe GraphQL

Many modules.
One schema.

olio builds GraphQL servers out of small, self-contained modules. Each module owns its SDL, resolvers and middleware. olio composes them into one schema, and codegen checks every resolver at compile time.

  • Modules own their fields. No one else can resolve them.
  • Typed from your SDL. Resolvers, args and middleware keys.
  • Runs anywhere. app.schema works with any GraphQL server.
Get started
npm i @nxara/olio graphql
The core idea

A module is a slice of your schema

It holds the SDL it declares, the resolvers that answer it and the middleware around them. It can extend other modules' types, so each part of the API stays small and readable.

01typeDefs: the part of the schema this module owns
02resolvers: typed from the SDL, for owned fields only
03middleware: layers for its fields, under the app's
Read about modules →
modules/users/index.ts
// src/modules/users/index.ts
import { module, mw } from "@nxara/olio";
import { loadTypeDefs } from "@nxara/olio-tools";

export const usersModule = module.users({
  typeDefs: await loadTypeDefs(new URL("typedefs/*.gql", import.meta.url)),
  resolvers: {
    Query: { user: (root, { id }) => findUser(id) }, // id: string
    User: { name: (user) => user.name } // user: what User maps to
  },
  middleware: mw.users({ Query: { user: [cache()] } })
});
Module "posts": "User.name" is declared by module "users"; only that module can resolve it
Modulesusersinventorysales · new
olioolio({ modules })
One schema
type User { … } type Product { … } type SalesOrder { … } type Query { user, products, orders }
From SDL to a running API

Four steps, each one line that matters

  1. 1

    Write SDL

    Each module keeps its schema next to its code.

    extend type Query { user(id: ID!): User }
  2. 2

    Generate types

    One script, one generated.ts for the whole app.

    codegen({ modules: "src/modules" })
  3. 3

    Compose

    olio merges the modules and checks every key.

    olio({ modules, middleware })
  4. 4

    Serve

    Fetch or Node, on the framework you already use.

    fetchHandler(app, { context })
Follow the quick start
The olio family

Add only what you need

Start with the core. Every other package is optional, and they all share one version.

Start with one module.

Add the next one when you need it.