🚀 Getting Started
If you want to create a new project - just go ahead and start from starter 🚀
However it is good to know how axolotl
work under the hood and read this:
Installation
Install dependencies. Install yoga adapter.
npm i @aexol/axolotl-core @aexol/axolotl-graphql-yoga graphql-yoga graphql
Now you need a schema.graphql
file or a URL with settings to download the
schema from upstream. Out of it Axolotl can generate simple type definitions
needed for the library out of your GraphQL 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
}
s
directive @auth on FIELD_DEFINITION
This is a simple schema and we will generate models.ts
for it.
models.ts
are located where you specify when initiating Axolotl To use in existing project run
npx @aexol/axolotl build
This will generate type safe models for your Axolotl engine. For the following schema:
For the following schema the models command will generate the following models:
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 you need to create the axolotl.ts
file with the following content:
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
>();
When you have this file you can create actual resolvers implementation inside
resolvers.ts
file:
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