Getting Started

🚀 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

schema.graphql
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:

src/models.ts
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:

axolotl.ts
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:

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.

index.ts
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
  • package.json
  • schema.graphql
  • tsconfig.json