Лекция 19
type Query {
me: User
}
type User {
id: ID
name: String
}
function Query_me(request) {
return request.auth.user;
}
function User_name(user) {
return user.getName();
}
{
me {
name
}
}
{
"me": {
"name": "Luke Skywalker"
}
}
{
hero {
name
}
}
"hero": {
"name": "R2-D2"
}
{
hero {
# Queries can have comments!
friends {
name
}
}
}
"hero": {
"friends": [
{ "name": "Luke Skywalker" },
...
]
}
{
human(id: "1000") {
name
height
}
}
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
{
human(id: "1000") {
name
height(unit: FOOT)
}
}
"human": {
"name": "Luke Skywalker",
"height": 5.6430448
}
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
{
empireHero: ...,
jediHero: ...
}
{
empireHero: hero(episode: EMPIRE) {
...heroFields
}
jediHero: hero(episode: JEDI) {
...heroFields
}
}
fragment heroFields on Character {
name
appearsIn
friends {
name
}
}
# type - query, mutation, subscription
# name - для логирования и дебага
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
# @include(if: Boolean)
# @skip(if: Boolean)
query Hero($episode: Episode, $withFriends: Boolean!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
# несколько операций в одной мутации
# будут выполнены последовательно
mutation CreateReviewForEpisode(
$ep: Episode!,
$review: ReviewInput!
) {
createReview(episode: $ep, review: $review) {
# возвращаемые значения
stars
commentary
}
}
query HeroForEpisode($ep: Episode!) {
hero(episode: $ep) {
name
... on Droid {
primaryFunction
}
... on Human {
height
}
}
}
{
search(text: "an") {
__typename
... on Human {
name
}
... on Droid {
name
}
... on Starship {
name
}
}
}
{
"data": {
"search": [
{
"__typename": "Human",
"name": "Han Solo"
},
{
"__typename": "Human",
"name": "Leia Organa"
},
{
"__typename": "Starship",
"name": "TIE Advanced x1"
}
]
}
}
type Character {
# nullable string
name: String
# non-nullable array of Episode
appearsIn: [Episode]!
}
Int
Float
String
Boolean
ID
enum Episode {
NEWHOPE
EMPIRE
JEDI
}
interface Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
}
type Human implements Character {
id: ID!
name: String!
friends: [Character]
appearsIn: [Episode]!
starships: [Starship]
totalCredits: Int
}
union SearchResult = Human | Droid | Starship
type Starship {
id: ID!
name: String!
# не просто поле - функция
length(unit: LengthUnit = METER): Float
}
Query: {
human(obj, args, context) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
# obj - предыдущий разрезолвленный объект
# args - аргументы запроса к полю
# context - в зависимости от реализации
# может хранить текущего юзера, подключение к БД, ...
Query: {
human(obj, args, context) {
return context.db.loadHumanByID(args.id).then(
userData => new Human(userData)
)
}
}
Human: {
name(obj, args, context) {
return obj.name
}
}
npm i express
npm i sequelize
npm i graphql
npm i express-graphql
// models/turtle.js
module.exports = (Sequelize, sequelize) => {
return sequelize.define('turtles', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
color: Sequelize.STRING,
});
};
// models/pizza.js
module.exports = (Sequelize, sequelize) => {
return sequelize.define('pizzas', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
colories: Sequelize.DOUBLE,
});
};
// models/weapon.js
module.exports = (Sequelize, sequelize) => {
return sequelize.define('weapons', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: Sequelize.STRING,
dps: Sequelize.INTEGER,
});
};
// models/index.js
module.exports = (Sequelize, config) => {
const options = {
host: config.db.host,
dialect: 'mysql',
logging: false,
define: {
timestamps: true,
}
};
const sequelize = new Sequelize(...);
...
};
// models/index.js
module.exports = (Sequelize, config) => {
...
const Turtle = require('../models/turtle')(Sequelize, sequelize);
const Pizza = require('../models/pizza')(Sequelize, sequelize);
const Weapon = require('../models/weapon')(Sequelize, sequelize);
Weapon.hasOne(Turtle);
Pizza.hasOne(Turtle, {as: 'favoritePizza'});
Pizza.hasOne(Turtle, {as: 'secondFavoritePizza'});
...
};
// models/index.js
module.exports = (Sequelize, config) => {
...
return {
turtles: Turtle,
pizzas: Pizza,
weapons: Weapon,
sequelize: sequelize,
Sequelize: Sequelize,
Op: Sequelize.Op,
};
};
// models/test-data.js
module.exports = async function (db) {
await db.sequelize.sync({force: true});
await db.pizzas.bulkCreate([
{name: "Neapolitan", colories: 1000},
{name: "Sicilian", colories: 1400},
{name: "Pepperoni", colories: 2200},
{name: "Mozzarella", colories: 1700},
]);
...
};
// models/test-data.js
module.exports = async function (db) {
...
await db.weapons.bulkCreate([
{name: "Katana", dps: 79},
{name: "Nunchucks", dps: 55},
{name: "Staff", dps: 60},
{name: "Sai", dps: 70},
]);
...
};
// models/test-data.js
module.exports = async function (db) {
...
await db.turtles.bulkCreate([
{ name: "Leo", color: "Blue",
favoritePizzaId: 1,
weaponId: 1 },
{ name: "Mikey", color: "Orange",
favoritePizzaId: 1,
secondFavoritePizzaId: 2,
weaponId: 2 },
...
]);
};
// models/test-data.js
module.exports = async function (db) {
...
await db.turtles.bulkCreate([
...
{ name: "Donnie", color: "Purple",
favoritePizzaId: 2,
weaponId: 3 },
{ name: "Raph", color: "Red",
favoritePizzaId: 3,
secondFavoritePizzaId: 1,
weaponId: 4 },
]);
};
// schema.js
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLInt,
GraphQLString,
GraphQLFloat,
GraphQLList,
} = require('graphql');
module.exports = db => {
...
return new GraphQLSchema({ query: Query });
};
// schema.js
const Pizza = new GraphQLObjectType({
name: 'Pizza',
fields: {
id: { type: GraphQLInt },
name: { type: GraphQLString },
colories: { type: GraphQLInt },
},
});
// schema.js
const Weapon = new GraphQLObjectType({
name: 'Weapon',
fields: {
id: {type: GraphQLInt},
name: {type: GraphQLString},
dps: {type: GraphQLFloat},
},
});
// schema.js
const Turtle = new GraphQLObjectType({
name: 'Turtle',
fields: {
id: { type: GraphQLInt },
name: { type: GraphQLString },
color: { type: GraphQLString },
weapon: {
type: Weapon,
resolve: turtle =>
db.weapons.findById(
turtle.weaponId,
{ raw: true }
)
},
...
},
});
// schema.js
const Turtle = new GraphQLObjectType({
name: 'Turtle',
fields: {
...
favoritePizza: {
type: Pizza,
resolve: turtle =>
db.pizzas.findById(
turtle.favoritePizzaId,
{ raw: true }
)
},
...
},
});
// schema.js
const Turtle = new GraphQLObjectType({
name: 'Turtle',
fields: {
...
secondFavoritePizza: {
type: Pizza,
resolve: turtle =>
db.pizzas.findById(
turtle.secondFavoritePizzaId,
{ raw: true }
)
},
},
});
// schema.js
const Query = new GraphQLObjectType({
name: 'TMNT',
fields: {
turtles: {
type: new GraphQLList(Turtle),
resolve: () =>
db.turtles.findAll({ raw: true })
},
...
}
});
// schema.js
const Query = new GraphQLObjectType({
name: 'TMNT',
fields: {
...
weapons: {
type: new GraphQLList(Weapon),
resolve: () =>
db.weapons.findAll({ raw: true })
},
...
}
});
// schema.js
const Query = new GraphQLObjectType({
name: 'TMNT',
fields: {
...
pizzas: {
type: new GraphQLList(Pizza),
resolve: () =>
db.pizzas.findAll({ raw: true })
},
...
}
});
// schema.js
const Query = new GraphQLObjectType({
name: 'TMNT',
fields: {
...
turtle: {
args: { id: { type: GraphQLInt } },
type: Turtle,
resolve: (obj, args) =>
db.turtles.findById(
args.id,
{ raw: true }
)
},
}
});
// index.js
const Sequelize = require('sequelize');
const Promise = require('bluebird');
const express = require('express');
const graphqlHttp = require('express-graphql');
const config = require('./config');
const db = require('./models')(Sequelize, config);
const schema = require('./schema')(db);
const fillWithTestData = require('./models/test-data');
...
// index.js
...
const app = express();
app.listen = Promise
.promisify(app.listen)
.bind(app);
app.use(graphqlHttp({
schema,
pretty: true,
graphiql: true
}));
...
// index.js
...
(async function () {
await fillWithTestData(db);
await app.listen(config.port);
console.log(`Server running at http://localhost:${config.port}`);
})();