src/resolvers.js
```typescript=
const { paginateResults } = require("./utils");
module.exports = {
Query: {
launches: async (_, { pageSize = 20, after }, { dataSources }) => {
const allLaunches = await dataSources.launchAPI.getAllLaunches();
// we want these in reverse chronological order
allLaunches.reverse();
const launches = paginateResults({
after,
pageSize,
results: allLaunches,
});
return {
launches,
cursor: launches.length ? launches[launches.length - 1].cursor : null,
// if the cursor of the end of the paginated results is the same as the
// last item in _all_ results, then there are no more results after this
hasMore: launches.length
? launches[launches.length - 1].cursor !==
allLaunches[allLaunches.length - 1].cursor
: false,
};
},
launch: (_, { id }, { dataSources }) =>
dataSources.launchAPI.getLaunchById({ launchId: id }),
me: async (_, __, { dataSources }) =>
dataSources.userAPI.findOrCreateUser(),
},
Mutation: {
login: async (_, { email }, { dataSources }) => {
const user = await dataSources.userAPI.findOrCreateUser({ email });
if (user) return Buffer.from(email).toString("base64");
},
bookTrips: async (_, { launchIds }, { dataSources }) => {
const results = await dataSources.userAPI.bookTrips({ launchIds });
const launches = await dataSources.launchAPI.getLaunchesByIds({
launchIds,
});
return {
success: results && results.length === launchIds.length,
message:
results.length === launchIds.length
? "trips booked successfully"
: `the following launches couldn't be booked: ${launchIds.filter(
(id) => !results.includes(id)
)}`,
launches,
};
},
cancelTrip: async (_, { launchId }, { dataSources }) => {
const result = await dataSources.userAPI.cancelTrip({ launchId });
if (!result)
return {
success: false,
message: "failed to cancel trip",
};
const launch = await dataSources.launchAPI.getLaunchById({ launchId });
return {
success: true,
message: "trip cancelled",
launches: [launch],
};
},
},
Mission: {
// make sure the default size is 'large' in case user doesn't specify
missionPatch: (mission, { size } = { size: "LARGE" }) => {
return size === "SMALL"
? mission.missionPatchSmall
: mission.missionPatchLarge;
},
},
Launch: {
isBooked: async (launch, _, { dataSources }) =>
dataSources.userAPI.isBookedOnLaunch({ launchId: launch.id }),
},
User: {
trips: async (_, __, { dataSources }) => {
// get ids of launches by user
const launchIds = await dataSources.userAPI.getLaunchIdsByUser();
if (!launchIds.length) return [];
// look up those launches by their ids
return (
dataSources.launchAPI.getLaunchesByIds({
launchIds,
}) || []
);
},
},
};
```
src/index.js
```typescript=
const { ApolloServer } = require("apollo-server");
const typeDefs = require("./schema");
const { createStore } = require("./utils");
const resolvers = require("./resolvers");
const LaunchAPI = require("./datasources/launch");
const UserAPI = require("./datasources/user");
// データベースの作成
const store = createStore();
const isEmail = require("isemail");
const server = new ApolloServer({
context: async ({ req }) => {
// simple auth check on every request
const auth = (req.headers && req.headers.authorization) || "";
const email = Buffer.from(auth, "base64").toString("ascii");
if (!isEmail.validate(email)) return { user: null };
// find a user by their email
const users = await store.users.findOrCreate({ where: { email } });
const user = (users && users[0]) || null;
return { user: { ...user.dataValues } };
},
typeDefs,
resolvers,
dataSources: () => ({
launchAPI: new LaunchAPI(),
userAPI: new UserAPI({ store }),
}),
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
```
src/schema.js
```javascript=
const { gql } = require('apollo-server');
const typeDefs = gql`
type Query {
launches( # replace the current launches query with this one.
"""
The number of results to show. Must be >= 1. Default = 20
"""
pageSize: Int
"""
If you add a cursor here, it will only return results _after_ this cursor
"""
after: String
): LaunchConnection!
launch(id: ID!): Launch
me: User
}
type Launch {
id: ID!
site: String
mission: Mission
rocket: Rocket
isBooked: Boolean!
}
type Rocket {
id: ID!
name: String
type: String
}
type User {
id: ID!
email: String!
trips: [Launch]!
}
type Mission {
name: String
missionPatch(size: PatchSize): String
}
enum PatchSize {
SMALL
LARGE
}
type Mutation {
# if false, booking trips failed -- check errors
bookTrips(launchIds: [ID]!): TripUpdateResponse!
# if false, cancellation failed -- check errors
cancelTrip(launchId: ID!): TripUpdateResponse!
login(email: String): String # login token
}
type TripUpdateResponse {
success: Boolean!
message: String
launches: [Launch]
}
"""
Simple wrapper around our list of launches that contains a cursor to the
last item in the list. Pass this cursor to the launches query to fetch results
after these.
"""
type LaunchConnection { # add this below the Query type as an additional type.
cursor: String!
hasMore: Boolean!
launches: [Launch]!
}
type Mission {
# ... with rest of schema
missionPatch(mission: String, size: PatchSize): String
}
`;
module.exports = typeDefs;
```