🚀 Getting Started
Want a ready‑to‑run template? Start with a starter: /from-examples
Prefer to understand how Axolotl works under the hood? Read on.
Installation
Install dependencies and the Yoga adapter:
npm i @aexol/axolotl-core @aexol/axolotl-graphql-yoga graphql-yoga graphql
Create a schema.graphql
file (or point to a URL). Axolotl will generate
type‑safe models from your schema.
Take a look at the following schema
type Beer implements Node{
name: String!
price: Int!
_id: String!
}
type Query{
beers: [Beer!]
testAuth: String! @auth
}
type Mutation{
addBeer(
beer: CreateBeer!
): String
deleteBeer(
_id: String!
): Boolean
updateBeer(
beer: UpdateBeer!
_id: String!
): Boolean
}
input CreateBeer{
name: String!
price: Int!
}
interface Node{
_id: String!
createdAt: String!
}
input UpdateBeer{
name: String
price: Int
}
schema{
query: Query
mutation: Mutation
}
directive @auth on FIELD_DEFINITION
This simple schema will produce models.ts
at the path you choose.
Generate models:
npx @aexol/axolotl build
This generates type‑safe models for your Axolotl engine. Example output:
export interface CreateBeer {
name: string;
price: number;
}
export interface UpdateBeer {
name?: string | undefined;
price?: number | undefined;
}
export type Models = {
['Beer']: {
name: {
args: Record<string, never>;
};
price: {
args: Record<string, never>;
};
_id: {
args: Record<string, never>;
};
};
['Query']: {
beers: {
args: Record<string, never>;
};
testAuth: {
args: Record<string, never>;
};
};
['Mutation']: {
addBeer: {
args: {
beer: CreateBeer;
};
};
deleteBeer: {
args: {
_id: string;
};
};
updateBeer: {
args: {
beer: UpdateBeer;
_id: string;
};
};
};
};
export type Directives = {
auth: {
args: Record<string, never>;
};
};
export interface Beer {
name: string;
price: number;
_id: string;
}
export interface Query {
beers?: Array<Beer> | undefined;
testAuth: string;
}
export interface Mutation {
addBeer?: string | undefined;
deleteBeer?: boolean | undefined;
updateBeer?: boolean | undefined;
}
Models
and Directives
types will be consumed by Axolotl engine. Below there
types that you can use to check if the response is valid using TypeScript
satisfies
keyword.
Then create the axolotl.ts
adapter entry:
import { Directives, Models } from '@/src/models.js';
import { Axolotl } from '@aexol/axolotl-core';
import { graphqlYogaAdapter } from '@aexol/axolotl-graphql-yoga';
// if you want to use apollo server, import apollo server adapter
export const { applyMiddleware, createResolvers, createDirectives, adapter } = Axolotl(graphqlYogaAdapter)<
Models,
Directives
>();
Now implement resolvers in resolvers.ts
:
import { createResolvers } from '@/src/axolotl.js';
import { Beer } from '@/src/models.js';
const Beers: Beer[] = [
{
_id: '0',
createdAt: '2023-10-10T15:21:43.038Z',
name: 'Kumple',
price: 10,
info: 'Dobre piwko',
},
{
_id: '1',
createdAt: '2023-10-11T14:51:09.029Z',
name: 'Zubr',
price: 100,
},
];
export default createResolvers({
Query: {
beers: () => Beers,
testAuth: () => 'TOP SECRET',
},
Mutation: {
addBeer: (input, args) => {
return Beers.push({ ...args.beer, _id: Beers.length + 1 + '', createdAt: new Date().toISOString() });
},
deleteBeer: (input, args) => {
return Beers.splice(Beers.findIndex((b) => b._id === args._id));
},
updateBeer: (input, args) => {
const oldElement = Beers.find((b) => b._id);
if (!oldElement) return false;
return Beers.splice(
Beers.findIndex((b) => b._id === args._id),
1,
{
...oldElement,
...args.beer,
},
);
},
},
});
Here you can have every type safe resolver implementation.
Now we need to run the resolvers using prepared adapter. Write your index.ts
file.
import { adapter } from '@/src/axolotl.js';
import resolvers from '@/src/resolvers.js';
adapter({ resolvers }).server.listen(parseInt(process.env.PORT || '4000'), () => {
console.log('LISTENING to ' + process.env.PORT || '4000');
});
Now if we build it and run the index.js using node
command - we have the
running server.
Your file tree should look like this:
- axolotl.ts
- index.ts
- models.ts
- resolvers.ts