babel-blade docs

babel-blade docs

  • Docs
  • API
  • Help
  • GitHub
  • Roadmap
  • Blog

›Usage/API

Introduction

  • What is Babel-Blade?
  • The Double Declaration Problem

Usage/API

  • GraphQL Spec By Example

Getting Started

  • As a babel plugin
  • As a babel macro

GraphQL Spec By Example

obligatory note: babel-blade is not yet production ready! Please proceed only if you are an early adopter. Feel free to chat with @swyx or check our issues!

On this page we show by example how to do every thing in the GraphQL query spec with babel-blade. These are directly tested for in our snapshot tests.

After you have tagged a data object with your query created with createQuery, it becomes a blade:

import { Connect, query } from 'urql';
import { createQuery } from 'blade.macro'; // if you are using as a babel macro

const movieQuery = createQuery(); // create the query
const Movie = () => (
  <div>
    <Connect
      query={query(movieQuery)}
      children={({ data }) => {
        const DATA = movieQuery(data); // `DATA` is a blade
        const { schedule } = DATA; // `schedule` is also a blade
        return (
          <div>
            <h2>{schedule.movie}</h2>
          </div>
        );
      }}
    />
  </div>
);

API Note: Exporting queries

As of v0.1.7 you can now export queries.

Before:

export const pageQuery = createQuery();

const App = data => {
  const DATA = pageQuery(data);
  const movie = DATA.movie;
};

After:

export const pageQuery = `
query pageQuery{
  movie
}`;

const App = data => {
  const DATA = data;
  const movie = DATA.movie;
};

So you can run your queries (or fragments!) elsewhere!

Thanks to Jonas for the suggestion!

API Note: Array methods

Special note on using Array prototype methods

Only applies if your GraphQL field names coincide with array prototype method names.

Blades will propagate through the following array methods:

  • map
  • every
  • filter
  • find
  • findIndex
  • forEach
  • reduce
  • reduceRight
  • some

so this will work:

import { Connect, query } from 'urql';

const movieQuery = createQuery()
const App = () => <Connect query={query(movieQuery)} children={({ data }) => {
  let result = movieQuery(data);
  let {actors} = result.movie;
  return <div>
          {actors.map(actor => (
            <Actor data={actor.supporting} />
            <Actor data={actor.leading} />
          ))}
        </div>;
}} />;

For the rest of the Array prototype methods, babel-blade simply "stops tracking" so you will need to stub out the rest of the dependencies with fragments on in a no-op assignment somewhere.

If you do actually have a field called "map" for example, destructure it:

// do this, will be in the GraphQL
const { map } = blade;
// don't do this, won't be captured in the generated graphql
const temp = blade.map; // we won't know if this is an array or an object property

Fields

After you have tagged a data object with your query created with createQuery, any property you access (including with destructuring) will be included in the generated GraphQL query.

Code Example

Before:

import { Connect, query } from 'urql';
import { createQuery } from 'blade.macro'; // if you are using as a babel macro

const movieQuery = createQuery();
const Movie = () => (
  <div>
    <Connect
      query={query(movieQuery)}
      children={({ data }) => {
        const DATA = movieQuery(data); // key step
        return (
          <div>
            <h2>{DATA.movie.gorilla}</h2>
            <p>{DATA.movie.monkey}</p>
            <p>{DATA.chimp}</p>
          </div>
        );
      }}
    />
  </div>
);

After:

import { Connect, query } from 'urql';

const Movie = () => (
  <div>
    <Connect
      query={query(`
query movieQuery{
  movie {
    gorilla
    monkey
  }
  chimp
}`)}
      children={({ data }) => {
        const DATA = data;
        return (
          <div>
            <h2>{DATA.movie.gorilla}</h2>
            <p>{DATA.movie.monkey}</p>
            <p>{DATA.chimp}</p>
          </div>
        );
      }}
    />
  </div>
);

Arguments

Every blade property can take arguments as though it were a function call - this gets moved to the generated GraphQL.

Code Example

Before

import { Connect, query } from 'urql';
import { createQuery } from 'blade.macro'; // if you are using as a babel macro

const movieQuery = createQuery();
const Movie = () => (
  <div>
    <Connect
      query={query(movieQuery)}
      children={({ data }) => {
        const DATA = movieQuery(data);
        const film = DATA.movie('limit: 5'); // like this
        const nestedQuery = film.schedule('schedule: true'); // or this
        return (
          <div>
            <Films data={film.titles} />
            <Schedule data={nestedQuery.data} />
          </div>
        );
      }}
    />
  </div>
);

After:

import { Connect, query } from 'urql';

const Movie = () => (
  <div>
    <Connect
      query={query(`
query movieQuery{
  movie_19e8: movie(limit: 5) {
    schedule_7d17: schedule(schedule: true) {
      data
    }
    titles
  }
}`)}
      children={({ data }) => {
        const DATA = data;
        const film = DATA.movie_19e8;
        const nestedQuery = film.schedule_7d17;
        return (
          <div>
            <Films data={film.titles} />
            <Schedule data={nestedQuery.data} />
          </div>
        );
      }}
    />
  </div>
);

Aliases

Done for you!

Each arguments call gets an autogenerated 4 character hex alias to help distinguish between them. This way you don't have to manually assign aliases for multiple queries on the same fields but with different arguments.

Fragments

Use the createFragment pseudofunction to create the fragment, and then attach it as an argument to any blade property.

Code Example

Before:

import { Connect, query } from 'urql';
import { createQuery, createFragment } from 'blade.macro'; // if you are using as a babel macro

// MovieComponent.js
const movieFragment = createFragment('Movie');
const Movie = ({ data }) => {
  let result = movieFragment(data);
  let movie = result.movie;
  return (
    <div className="movie">
      {loaded === false ? (
        <p>Loading</p>
      ) : (
        <div>
          <h2>{movie.title}</h2>
          <p>{movie.actors.supporting}</p>
          <p>{movie.actors.leading}</p>
          <button onClick={onClose}>Close</button>
        </div>
      )}
    </div>
  );
};

Movie.fragment = movieFragment; // like this

// MoviePage.js
const pageQuery = createQuery(); // create a top-level query
const App = () => (
  <Connect
    query={query(pageQuery)}
    children={({ loaded, data }) => {
      let result = pageQuery(data);
      // rendering Movie while adding
      // `Movie.fragment` into the query.
      // (could be automatic in future)
      return (
        <ul>
          <Movie data={result.movie(Movie.fragment)} />
        </ul>
      );
    }}
  />
);

This transpiles to:

import { Connect, query } from 'urql';
const Movie = ({ data }) => {
  let result = data;
  let movie = result.movie;
  return (
    <div className="movie">
      {loaded === false ? (
        <p>Loading</p>
      ) : (
        <div>
          <h2>{movie.title}</h2>
          <p>{movie.actors.supporting}</p>
          <p>{movie.actors.leading}</p>
          <button onClick={onClose}>Close</button>
        </div>
      )}
    </div>
  );
};

Movie.fragment = movieFragment => `
fragment ${movieFragment} on Movie{
  movie {
    title
    actors {
      supporting
      leading
    }
  }
}`;

const App = () => (
  <Connect
    query={query(`
query pageQuery{
  movie {
    ...Moviefragment
  }
}

${Movie.fragment('Moviefragment')}`)}
    children={({ loaded, data }) => {
      let result = data;
      // rendering Movie while adding
      // `Movie.fragment` into the query.
      // (could be automatic in future)
      return (
        <ul>
          <Movie data={result.movie} />
        </ul>
      );
    }}
  />
);

Operation Name

All queries are named by whatever variable identifier you assign.

Code Example

Before

import { Connect, query } from 'urql';
import { createQuery } from 'blade.macro'; // if you are using as a babel macro

const movieQuery = createQuery(); // movieQuery becomes the operation name
const Movie = () => (
  <div>
    <Connect
      query={query(movieQuery)}
      children={({ data }) => {
        const DATA = movieQuery(data);
        return (
          <div>
            <h2>{DATA.movie.gorilla}</h2>
            <p>{DATA.movie.monkey}</p>
            <p>{DATA.chimp}</p>
          </div>
        );
      }}
    />
  </div>
);

After:

import { Connect, query } from 'urql';

const Movie = () => (
  <div>
    <Connect
      query={query(`
query movieQuery{
  movie {
    gorilla
    monkey
  }
  chimp
}`)}
      children={({ data }) => {
        const DATA = data;
        return (
          <div>
            <h2>{DATA.movie.gorilla}</h2>
            <p>{DATA.movie.monkey}</p>
            <p>{DATA.chimp}</p>
          </div>
        );
      }}
    />
  </div>
);

Variables

Supply variables as a string or template string to your createQuery call.

Code Example

Before:

import { Connect, query } from 'urql';
import { createQuery } from 'blade.macro'; // if you are using as a babel macro

const App = ({ movieID }) => {
  const pageQuery = createQuery(`$movieID: ${movieID}`);
  return (
    <Connect
      query={query(pageQuery)}
      children={({ data }) => {
        let result = pageQuery(data);
        const stuff = result.movie('id: $movieID');
        return (
          <ul>
            <div>{stuff.title}</div>
          </ul>
        );
      }}
    />
  );
};

After:

import { Connect, query } from 'urql';

const App = ({ movieID }) => {
  return (
    <Connect
      query={query(`
query pageQuery(${`$movieID: ${movieID}`}){
  movie_d076: movie(id: $movieID) {
    title
  }
}`)}
      children={({ data }) => {
        let result = data;
        const stuff = result.movie_d076;
        return (
          <ul>
            <div>{stuff.title}</div>
          </ul>
        );
      }}
    />
  );
};

Directives

You can add directives just like any other argument. You just have to make sure to use '@' as the first character in a template string or string literal.

Code Example

Before:

import { Connect, query } from 'urql';
import { createQuery } from 'blade.macro'; // if you are using as a babel macro

const movieQuery = createQuery();
const Movie = () => (
  <div>
    <Connect
      query={query(movieQuery)}
      children={({ data }) => {
        const DATA = movieQuery(data);
        const film = DATA.movie('limit: 5');
        const nestedQuery = film.schedule('@sort', 'id: 23', '@ping'); // like this
        return (
          <div>
            <Films data={film.titles} />
            <Schedule data={nestedQuery.data} />
          </div>
        );
      }}
    />
  </div>
);

After:

import { Connect, query } from 'urql';

const Movie = () => (
  <div>
    <Connect
      query={query(`
query movieQuery{
  movie_27f6: movie(limit: 5) {
    schedule_1c35: schedule(id: 23) @sort @ping {
      data
    }
    titles
  }
}`)}
      children={({ data }) => {
        const DATA = data;
        const film = DATA.movie_27f6;
        const nestedQuery = film.schedule_1c35;
        return (
          <div>
            <Films data={film.titles} />
            <Schedule data={nestedQuery.data} />
          </div>
        );
      }}
    />
  </div>
);

Mutations

Sorry.. this is not implemented yet. Contact @swyx or file an issue!

Inline Fragments and Union Types

Sorry.. this is not implemented yet. Contact @swyx or file an issue!

← The Double Declaration ProblemAs a babel plugin →
  • API Note: Exporting queries
  • API Note: Array methods
  • Fields
  • Arguments
  • Aliases
  • Fragments
  • Operation Name
  • Variables
  • Directives
  • Mutations
  • Inline Fragments and Union Types
babel-blade docs
Docs
Getting StartedAPI Reference
Community
Twitter
More
BlogGitHubStar
MIT License. Maintained by @swyx