Skip to content

Instantly share code, notes, and snippets.

@Aryan-Jagadale
Last active June 27, 2023 11:58
Show Gist options
  • Select an option

  • Save Aryan-Jagadale/9a5e47b976f630f0990df7f954a841ba to your computer and use it in GitHub Desktop.

Select an option

Save Aryan-Jagadale/9a5e47b976f630f0990df7f954a841ba to your computer and use it in GitHub Desktop.
Full Stack Open GraphQL solution
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const { GraphQLError } = require("graphql");
const { v1: uuid } = require("uuid");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
mongoose.set("strictQuery", false);
const Author = require("./models/author");
const Book = require("./models/book");
const User = require("./models/user");
require("dotenv").config();
mongoose
.connect(process.env.MONGODB_URI)
.then(() => {
console.log("connected to MongoDB");
})
.catch((error) => {
console.log("error connection to MongoDB:", error.message);
});
const typeDefs = `
type User {
username: String!
favoriteGenre: String!
id: ID!
}
type Token {
value: String!
}
type Book {
title: String!
published: String!
author: Author!
genres: [String!]!
id: ID!
}
type Author {
name: String!
born: Int!
id: ID!
}
input AuthorInput {
name:String!
born:String
}
type Query {
bookCount:Int!
authorCount:Int!
allBooks(author: String,genre: String):[Book!]!
allAuthors:[Author!]!
me:User
}
type Mutation {
addBook(
title : String!
published : String!
author : AuthorInput!
genres: [String!]!
) :Book
editAuthor(
name: String!
setBornTo: Int!
):Author
createUser(
username: String!
favoriteGenre: String!
): User
login(
username: String!
password: String!
): Token
}
`;
const resolvers = {
Query: {
authorCount: () => Author.collection.countDocuments(),
bookCount: () => Book.collection.countDocuments(),
allBooks: async (root, args) => {
if (args.author) {
const foundAuthor = await Author.findOne({ name: args.author });
if (foundAuthor) {
if (args.genre) {
return await Book.find({
author: foundAuthor._id,
genres: { $in: [args.genre] },
}).populate("author");
}
return await Book.find({ author: foundAuthor.id }).populate("author");
}
return null;
}
if (args.genre) {
return Book.find({ genres: { $in: [args.genre] } }).populate("author");
}
return Book.find({}).populate("author");
},
allAuthors: async () => await Author.find({}),
me: (root, args, context) => {
return context.currentUser;
},
},
Mutation: {
addBook: async (root, args, context) => {
const foundBook = await Book.findOne({ title: args.title });
const foundAuthor = await Author.findOne({ name: args.author.name });
const currentUser = context.currentUser;
if (!currentUser) {
throw new GraphQLError("You are not logged in");
}
if (foundBook) {
throw new GraphQLError("Book already exists", {
invalidArgs: args.title,
});
}
if (!foundAuthor) {
const author = await Author({ ...args.author });
try {
await author.save();
} catch (error) {
console.log("Error in found Author", error);
throw new GraphQLError(error.message, {
invalidArgs: args,
});
}
}
const foundAuthor2 = await Author.findOne({ name: args.author.name });
const book = await Book({ ...args, author: foundAuthor2 });
try {
await book.save();
} catch (error) {
throw new GraphQLError(error.message, {
invalidArgs: args,
});
}
return book;
},
editAuthor: async (root, args,context) => {
const author = await Author.findOne({ name: args.name });
const currentUser = context.currentUser
if (!currentUser) {
throw new AuthenticationError("not authenticated")
}
if (!author) {
return null;
}
const setBornYear = await Author.findByIdAndUpdate(
{ _id: author._id },
{ ...args, born: args.setBornTo }
);
setBornYear.save();
return await Author.findOne({ name: args.name });
},
createUser: async (root, args) => {
const user = await User({
username: args.username,
favoriteGenre: args.favoriteGenre,
});
try {
await user.save();
} catch (error) {
throw new GraphQLError("Creating the user failed", {
extensions: {
code: "BAD_USER_INPUT",
invalidArgs: args.name,
error,
},
});
}
},
login: async (root, args) => {
const user = await User.findOne({ username: args.username });
if (!user || args.password !== "secret") {
throw new GraphQLError("wrong credentials", {
extensions: {
code: "BAD_USER_INPUT",
},
});
}
const userForToken = {
username: user.username,
id: user._id,
};
return { value: jwt.sign(userForToken, process.env.JWT_SECRET) };
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req, res }) => {
const auth = req ? req.headers.authorization : null;
if (auth && auth.startsWith("Bearer ")) {
const decodedToken = jwt.verify(
auth.substring(7),
process.env.JWT_SECRET
);
const currentUser = await User.findById(decodedToken.id)
return { currentUser };
}
},
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const { GraphQLError } = require("graphql");
const { v1: uuid } = require("uuid");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
mongoose.set("strictQuery", false);
const Author = require("./models/author");
const Book = require("./models/book");
const User = require("./models/user");
require("dotenv").config();
mongoose
.connect(process.env.MONGODB_URI)
.then(() => {
console.log("connected to MongoDB");
})
.catch((error) => {
console.log("error connection to MongoDB:", error.message);
});
/*const typeDefs = `
type User {
username: String!
favoriteGenre: String!
id: ID!
}
type Token {
value: String!
}
type Book {
title: String!
published: String!
author: Author!
genres: [String!]!
id: ID!
}
type Author {
name: String!
born: Int!
id: ID!
}
input AuthorInput {
name:String!
born:String
}
type Query {
bookCount:Int!
authorCount:Int!
allBooks(author: String,genre: String):[Book!]!
allAuthors:[Author!]!
me:User
}
type Mutation {
addBook(
title : String!
published : String!
author : AuthorInput!
genres: [String!]!
) :Book
editAuthor(
name: String!
setBornTo: Int!
):Author
createUser(
username: String!
favoriteGenre: String!
): User
login(
username: String!
password: String!
): Token
}
`;*/
const typeDefs =`
type Author {
name: String!
born: Int
bookCount: Int!
id: ID!
}
input AuthorInput {
name: String!
born: Int
}
type Book {
title: String!
published: Int!
author: Author!
genres: [String!]!
id: ID!
}
type User {
username: String!
favoriteGenre: String!
id: ID!
}
type Token {
value: String!
}
type Query {
authorCount: Int!
bookCount: Int!
allBooks(author: String, genre: String): [Book!]!
allAuthors: [Author!]!
me: User
}
type Mutation {
addBook(
title: String!
published: Int!
author: AuthorInput!
genres: [String!]!
): Book
editAuthor(name: String!, setBornTo: Int!): Author
createUser(username: String!, favoriteGenre: String!): User
login(username: String!, password: String!): Token
}
`;
const resolvers = {
Query: {
authorCount: () => Author.collection.countDocuments(),
bookCount: () => Book.collection.countDocuments(),
allBooks: async (root, args) => {
if (args.author) {
const foundAuthor = await Author.findOne({ name: args.author });
if (foundAuthor) {
if (args.genre) {
return await Book.find({
author: foundAuthor.id,
genres: { $in: [args.genre] },
}).populate("author");
}
return await Book.find({ author: foundAuthor.id }).populate("author");
}
return null;
}
if (args.genre) {
return Book.find({ genres: { $in: [args.genre] } }).populate("author");
}
return Book.find({}).populate("author");
},
allAuthors: async () => await Author.find({}),
me: (root, args, context) => {
return context.currentUser;
},
},
Author: {
bookCount: async (root) => {
const foundAuthor = await Author.findOne({ name: root.name })
const foundBooks = await Book.find({ author: foundAuthor.id })
return foundBooks.length
}
},
Mutation: {
addBook: async (root, args, context) => {
const foundBook = await Book.findOne({ title: args.title });
const foundAuthor = await Author.findOne({ name: args.author.name });
const currentUser = context.currentUser;
if (!currentUser) {
throw new GraphQLError("You are not logged in");
}
if (foundBook) {
throw new GraphQLError("Book already exists", {
invalidArgs: args.title,
});
}
if (!foundAuthor) {
const author = await Author({ ...args.author });
try {
await author.save();
} catch (error) {
console.log("Error in found Author", error);
throw new GraphQLError(error.message, {
invalidArgs: args,
});
}
}
const foundAuthor2 = await Author.findOne({ name: args.author.name });
const book = await Book({ ...args, author: foundAuthor2 });
try {
await book.save();
} catch (error) {
throw new GraphQLError(error.message, {
invalidArgs: args,
});
}
return book;
},
editAuthor: async (root, args, context) => {
const author = await Author.findOne({ name: args.name });
const currentUser = context.currentUser;
if (!currentUser) {
throw new AuthenticationError("not authenticated");
}
if (!author) {
return null;
}
const setBornYear = await Author.findByIdAndUpdate(
{ _id: author._id },
{ ...args, born: args.setBornTo }
);
setBornYear.save();
return await Author.findOne({ name: args.name });
},
createUser: async (root, args) => {
const user = await User({
username: args.username,
favoriteGenre: args.favoriteGenre,
});
try {
await user.save();
} catch (error) {
throw new GraphQLError("Creating the user failed", {
extensions: {
code: "BAD_USER_INPUT",
invalidArgs: args.name,
error,
},
});
}
},
login: async (root, args) => {
const user = await User.findOne({ username: args.username });
//mluukkai--secret
if (!user || args.password !== "secret") {
throw new GraphQLError("wrong credentials", {
extensions: {
code: "BAD_USER_INPUT",
},
});
}
const userForToken = {
username: user.username,
id: user._id,
};
return { value: jwt.sign(userForToken, process.env.JWT_SECRET) };
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
context: async ({ req, res }) => {
const auth = req ? req.headers.authorization : null;
if (auth && auth.startsWith("Bearer ")) {
const decodedToken = jwt.verify(
auth.substring(7),
process.env.JWT_SECRET
);
const currentUser = await User.findById(decodedToken.id);
return { currentUser };
}
},
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
//Frontend
//Author.js
import { useQuery } from "@apollo/client";
import { ALL_AUTHORS } from "../queries";
import YearForm from "./YearForm.jsx";
const Authors = (props) => {
const result = useQuery(ALL_AUTHORS);
if (!props.show) {
return null;
}
if (result.loading) {
return <div>loading....</div>;
}
const authors = result.data.allAuthors;
return (
<div>
<h2>authors</h2>
<table>
<tbody>
<tr>
<th></th>
<th>born</th>
<th>Book Count</th>
</tr>
{authors?.map((a) => (
<tr key={a.name}>
<td>{a.name}</td>
<td>{a.born}</td>
<td>{a.bookCount}</td>
</tr>
))}
</tbody>
</table>
<br />
<YearForm allAuthors={authors} />
</div>
);
};
export default Authors;
//Books.js
import { useLazyQuery, useQuery } from "@apollo/client";
import { ALL_BOOKS, ALL_BOOKS_WITH_GENRE } from "../queries";
import { useState } from "react";
import { useEffect } from "react";
const Books = (props) => {
//const [str, setStr] = useState("");
const result = useQuery(ALL_BOOKS);
const [books, setBooks] = useState([]);
const [filteredBooks, setFilteredBooks] = useState([]);
const [genres, setGenres] = useState([]);
const [selectedGenre, setSelectedGenre] = useState("");
/*{
/*function genreFilter(e) {
console.log("Clicked");
let string = e.target.textContent;
string = string.toLowerCase();
setStr(string);
}*/
useEffect(() => {
if (result && result.data) {
const allBooks = result.data.allBooks;
setBooks(result.data.allBooks);
//console.log(result);
let genres = ["All genres"];
allBooks.forEach((element) => {
element.genres.forEach((g) => {
if (genres.indexOf(g) === -1) {
//console.log(g);
//console.log(genres.indexOf(g));
genres.push(g);
}
});
});
setGenres(genres);
setSelectedGenre("All genres");
}
}, [result.data]);
useEffect(() => {
if (selectedGenre === "All genres") {
setFilteredBooks(books);
} else {
setFilteredBooks(
books.filter((b) => b.genres.indexOf(selectedGenre) !== -1)
);
}
}, [books, selectedGenre]);
// console.log(books);
if (!props.show) {
return null;
}
if (result.loading) {
return <div>loading....</div>;
}
return (
<div>
<h2>books</h2>
{/*WIth react 8.19 */}
{/*<button onClick={(e) => genreFilter(e)}>Comedy</button>
<button onClick={(e) => genreFilter(e)}>MC</button>
<button onClick={(e) => genreFilter(e)}>Agile</button>
<button onClick={(e) => genreFilter(e)}>All</button>*/}
{genres.length > 0 &&
genres.map((g) => (
<button onClick={() => setSelectedGenre(g)} key={g}>
{g}
</button>
))}
<br />
<br />
<table>
<tbody>
<tr>
<th></th>
<th>author</th>
<th>published</th>
<th>genres</th>
</tr>
{/*{books
?.filter((item) =>
str !== "all" ? item.genres.includes(str) : item
)
.map((item, index) => (
<tr key={index}>
<th>{item.title}</th>
<th>{item.author.name}</th>
<th>{item.published}</th>
<th>{item.genres}</th>
</tr>
))}*/}
{filteredBooks?.map((item, index) => (
<tr key={index}>
<th>{item.title}</th>
<th>{item.author.name}</th>
<th>{item.published}</th>
<th>{item.genres}</th>
</tr>
))}
</tbody>
</table>
<br />
</div>
);
};
export default Books;
//Login Form.js
import React, { useEffect, useState } from 'react'
import { LOGIN } from '../queries'
import { useMutation } from '@apollo/client'
const LoginForm = ({setToken}) => {
const [username, setUsername] = useState('mluukkai')
const [password, setPassword] = useState('secret')
const [login,result] = useMutation(LOGIN,{
onError:(error)=>{
console.log("Something went wrong in LoginForm");
}
})
useEffect(() => {
if (result.data) {
const token = result.data.login.value
localStorage.setItem("library-token",token)
setToken(token)
}
}, [result.data])
const submit = async (event) => {
event.preventDefault()
if (!username|| !password) {
return alert("Please fill all cred")
}
login({variables:{username,password}})
}
return (
<div>
<form onSubmit={submit}>
<div>
username <input
value={username}
onChange={({ target }) => setUsername(target.value)}
/>
</div>
<div>
password <input
type='text'
value={password}
onChange={({ target }) => setPassword(target.value)}
/>
</div>
<button type='submit'>login</button>
</form>
</div>
)
}
export default LoginForm
//NewBook.js
import React, { useState } from "react";
import { useMutation } from "@apollo/client";
import { ALL_AUTHORS, ALL_BOOKS, CREATE_BOOK } from "../queries";
const BookForm = ({show}) => {
const [title, setTitle] = useState("");
const [author, setAuhtor] = useState("");
const [published, setPublished] = useState(undefined);
const [genre, setGenre] = useState("");
const [genres, setGenres] = useState([]);
const [createBook] = useMutation(CREATE_BOOK, {
refetchQueries: [{ query: ALL_BOOKS }, { query: ALL_AUTHORS }],
onError: (error) => {
console.log("Wrong !", error);
},
});
if (!show) {
return null
}
const submit = async (event) => {
event.preventDefault();
console.log("add book...");
createBook({
variables: {
title,
author: { name: author, born: 1200 },
published,
genres,
},
});
/*setTitle("");
setPublished("");
setAuhtor("");
setGenres([]);
setGenre("");*/
};
const addGenre = () => {
setGenres(genres.concat(genre));
setGenre("");
};
return (
<div>
<form onSubmit={submit}>
<div>
title
<input
value={title}
onChange={({ target }) => setTitle(target.value)}
/>
</div>
<div>
author
<input
value={author}
onChange={({ target }) => setAuhtor(target.value)}
/>
</div>
<div>
published
<input
type="number"
value={published}
onChange={({ target }) => setPublished(parseInt(target.value))}
/>
</div>
<div>
<input
value={genre}
onChange={({ target }) => setGenre(target.value)}
/>
<button onClick={addGenre} type="button">
add genre
</button>
</div>
<div>genres: {genres.join(" ")}</div>
<button type="submit">create book</button>
</form>
</div>
);
};
export default BookForm;
//Recommend.js
import React, { useState, useEffect } from 'react'
import { useLazyQuery, useQuery } from '@apollo/client'
import { ME, ALL_BOOKS_WITH_GENRE } from '../queries'
const Recommended = ({ show }) => {
const user = useQuery(ME)
const [getFavoriteBooks, result] = useLazyQuery(ALL_BOOKS_WITH_GENRE)
const [favoriteBooks, setFavoriteBooks] = useState([])
useEffect(() => {
if (result.data) {
setFavoriteBooks(result.data.allBooks)
}
}, [setFavoriteBooks, result])
useEffect(() => {
if (user.data) {
getFavoriteBooks({ variables: { genre: user.data.me.favoriteGenre } })
}
}, [getFavoriteBooks, user])
if (!show) {
return null
}
return (
<div>
<p>
books in your favorite genre <b>{user.data.me.favoriteGenre}</b>
</p>
<table>
<tbody>
<tr>
<th></th>
<th>author</th>
<th>published</th>
</tr>
{favoriteBooks.map((a) => (
<tr key={a.title}>
<td>{a.title}</td>
<td>{a.author.name}</td>
<td>{a.published}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
export default Recommended
//YearForm.js
import { useMutation } from "@apollo/client";
import React, { useState, useEffect } from "react";
import { ALL_AUTHORS, EDIT_BORN_YEAR } from "../queries";
import Select from "react-select";
const YearForm = ({ allAuthors }) => {
const [nameOptions, setNameOptions] = useState(null);
const [setBornTo, setBornYear] = useState(undefined);
const [changeBornYear, result] = useMutation(EDIT_BORN_YEAR, {
refetchQueries: [{ query: ALL_AUTHORS }],
});
useEffect(() => {
if (result.data && result.data.editAuthor === null) {
alert("Author not found!,Yearform.js");
}
}, [result.data]);
const options = [];
allAuthors.forEach((author) =>
options.push({
value: author.name,
label: author.name,
})
);
const submit = async (e) => {
e.preventDefault();
const name = nameOptions.value;
changeBornYear({ variables: { name, setBornTo } });
setNameOptions("");
setBornYear("");
console.log("Submitedd");
};
return (
<div>
<h2>Set BirthYear</h2>
<form onSubmit={submit}>
<div>
{/*8.12 Solution */}
<Select
value={nameOptions}
onChange={setNameOptions}
options={options}
/>
</div>
<div>
born{" "}
<input
value={setBornTo}
onChange={({ target }) => setBornYear(parseInt(target.value))}
/>
</div>
<button type="submit">Update Author</button>
</form>
</div>
);
};
export default YearForm;
//App.js
import { useState } from "react";
import Authors from "./components/Authors";
import Books from "./components/Books";
import NewBook from "./components/NewBook";
import LoginForm from "./components/LoginForm";
import { useApolloClient } from "@apollo/client";
import Recommend from "./components/Recommend";
const App = () => {
const client = useApolloClient();
const [token, setToken] = useState(null);
const [page, setPage] = useState("authors");
if (!token) {
return (
<>
<LoginForm setToken={setToken} />
</>
);
}
const logout = () => {
setToken(null);
localStorage.clear();
client.resetStore();
};
return (
<div>
<button onClick={logout}>Logout</button>
<div>
<button onClick={() => setPage("authors")}>authors</button>
<button onClick={() => setPage("books")}>books</button>
<button onClick={() => setPage("recommend")}>recommend</button>
<button onClick={() => setPage("add")}>add book</button>
</div>
<br/>
<NewBook show={page === "add"} />
<Authors show={page === "authors"} />
<Books show={page === "books"} />
<Recommend show={page === "recommend"} />
</div>
);
};
export default App;
//quiers.js
import { gql } from "@apollo/client";
export const ALL_AUTHORS = gql`
query {
allAuthors {
name
born
bookCount
}
}
`;
export const ALL_BOOKS = gql`
query {
allBooks {
title
published
genres
author {
name
}
}
}
`;
export const ALL_BOOKS_WITH_GENRE = gql`
query getallBooks($genre: String!) {
allBooks(genre: $genre) {
title
published
genres
author {
name
}
}
}
`;
export const CREATE_BOOK = gql`
mutation createBook(
$title: String!
$author: String!
$published: Int!
$genres: [String!]!
) {
addBook(
title: $title
author: $author
published: $published
genres: $genres
) {
title
author
}
}
`;
export const EDIT_BORN_YEAR = gql`
mutation changeBornYear($name: String!, $setBornTo: Int!) {
editAuthor(name: $name, setBornTo: $setBornTo) {
name
born
}
}
`;
export const LOGIN = gql`
mutation loginUser($username: String!, $password: String!) {
login(username: $username, password: $password) {
value
}
}
`;
export const ME = gql`
query {
me {
username
favoriteGenre
}
}
`;
const { ApolloServer } = require('@apollo/server')
const { startStandaloneServer } = require('@apollo/server/standalone')
let authors = [
{
name: 'Robert Martin',
id: "afa51ab0-344d-11e9-a414-719c6709cf3e",
born: 1952,
},
{
name: 'Martin Fowler',
id: "afa5b6f0-344d-11e9-a414-719c6709cf3e",
born: 1963
},
{
name: 'Fyodor Dostoevsky',
id: "afa5b6f1-344d-11e9-a414-719c6709cf3e",
born: 1821
},
{
name: 'Joshua Kerievsky', // birthyear not known
id: "afa5b6f2-344d-11e9-a414-719c6709cf3e",
},
{
name: 'Sandi Metz', // birthyear not known
id: "afa5b6f3-344d-11e9-a414-719c6709cf3e",
},
]
/*
* Suomi:
* Saattaisi olla järkevämpää assosioida kirja ja sen tekijä tallettamalla kirjan yhteyteen tekijän nimen sijaan tekijän id
* Yksinkertaisuuden vuoksi tallennamme kuitenkin kirjan yhteyteen tekijän nimen
*
* English:
* It might make more sense to associate a book with its author by storing the author's id in the context of the book instead of the author's name
* However, for simplicity, we will store the author's name in connection with the book
*
* Spanish:
* Podría tener más sentido asociar un libro con su autor almacenando la id del autor en el contexto del libro en lugar del nombre del autor
* Sin embargo, por simplicidad, almacenaremos el nombre del autor en conección con el libro
*/
let books = [
{
title: 'Clean Code',
published: 2008,
author: 'Robert Martin',
id: "afa5b6f4-344d-11e9-a414-719c6709cf3e",
genres: ['refactoring']
},
{
title: 'Agile software development',
published: 2002,
author: 'Robert Martin',
id: "afa5b6f5-344d-11e9-a414-719c6709cf3e",
genres: ['agile', 'patterns', 'design']
},
{
title: 'Refactoring, edition 2',
published: 2018,
author: 'Martin Fowler',
id: "afa5de00-344d-11e9-a414-719c6709cf3e",
genres: ['refactoring']
},
{
title: 'Refactoring to patterns',
published: 2008,
author: 'Joshua Kerievsky',
id: "afa5de01-344d-11e9-a414-719c6709cf3e",
genres: ['refactoring', 'patterns']
},
{
title: 'Practical Object-Oriented Design, An Agile Primer Using Ruby',
published: 2012,
author: 'Sandi Metz',
id: "afa5de02-344d-11e9-a414-719c6709cf3e",
genres: ['refactoring', 'design']
},
{
title: 'Crime and punishment',
published: 1866,
author: 'Fyodor Dostoevsky',
id: "afa5de03-344d-11e9-a414-719c6709cf3e",
genres: ['classic', 'crime']
},
{
title: 'The Demon ',
published: 1872,
author: 'Fyodor Dostoevsky',
id: "afa5de04-344d-11e9-a414-719c6709cf3e",
genres: ['classic', 'revolution']
},
]
/*
you can remove the placeholder query once your first own has been implemented
*/
const typeDefs = `
type Query {
bookCount:Int!
authorCount:Int!
}
`
const resolvers = {
Query: {
bookCount: () => books.length,
authorCount : () => authors.length
}
}
const server = new ApolloServer({
typeDefs,
resolvers,
})
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`Server ready at ${url}`)
})
//Copy all schema from questions
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const { GraphQLError } = require("graphql");
const { v1: uuid } = require("uuid");
const mongoose = require("mongoose");
mongoose.set("strictQuery", false);
const Author = require("./models/author");
const Book = require("./models/book");
require("dotenv").config();
mongoose
.connect(process.env.MONGODB_URI)
.then(() => {
console.log("connected to MongoDB");
})
.catch((error) => {
console.log("error connection to MongoDB:", error.message);
});
const typeDefs = `
type Book {
title: String!
published: String!
author: Author!
genres: [String!]!
id: ID!
}
type Author {
name: String!
born: Int!
id: ID!
}
input AuthorInput {
name:String!
born:String
}
type Query {
bookCount:Int!
authorCount:Int!
allBooks(author: String,genre: String):[Book!]!
allAuthors:[Author!]!
}
type Mutation {
addBook(
title : String!
published : String!
author : AuthorInput!
genres: [String!]!
) :Book
editAuthor(
name: String!
setBornTo: Int!
):Author
}
`;
const resolvers = {
Query: {
authorCount: () => Author.collection.countDocuments(),
bookCount: () => Book.collection.countDocuments(),
allBooks: async (root, args) => {
if (args.author) {
const foundAuthor = await Author.findOne({ name: args.author });
if (foundAuthor) {
if (args.genre) {
return await Book.find({
author: foundAuthor._id,
genres: { $in: [args.genre] },
}).populate("author");
}
return await Book.find({ author: foundAuthor.id }).populate("author");
}
return null;
}
if (args.genre) {
return Book.find({ genres: { $in: [args.genre] } }).populate("author");
}
return Book.find({}).populate("author");
},
allAuthors: async () => await Author.find({}),
},
Mutation: {
addBook: async (root, args) => {
const foundBook = await Book.findOne({ title: args.title });
const foundAuthor = await Author.findOne({ name: args.author.name });
if (foundBook) {
throw new GraphQLError("Book already exists", {
invalidArgs: args.title,
});
}
if (!foundAuthor) {
const author = await Author({ ...args.author });
try {
await author.save();
} catch (error) {
console.log("Error in found Author", error);
throw new GraphQLError(error.message, {
invalidArgs: args,
});
}
}
const foundAuthor2 = await Author.findOne({ name: args.author.name });
const book = await Book({ ...args, author: foundAuthor2 });
try {
await book.save();
} catch (error) {
throw new GraphQLError(error.message, {
invalidArgs: args,
});
}
return book;
},
editAuthor: async (root, args) => {
const author = await Author.findOne({ name: args.name });
if (!author) {
return null;
}
const setBornYear = await Author.findByIdAndUpdate({_id:author._id},{...args,born:args.setBornTo})
setBornYear.save();
return await Author.findOne({ name: args.name })
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
let authors = [
{
name: "Robert Martin",
id: "afa51ab0-344d-11e9-a414-719c6709cf3e",
born: 1952,
},
{
name: "Martin Fowler",
id: "afa5b6f0-344d-11e9-a414-719c6709cf3e",
born: 1963,
},
{
name: "Fyodor Dostoevsky",
id: "afa5b6f1-344d-11e9-a414-719c6709cf3e",
born: 1821,
},
{
name: "Joshua Kerievsky", // birthyear not known
id: "afa5b6f2-344d-11e9-a414-719c6709cf3e",
},
{
name: "Sandi Metz", // birthyear not known
id: "afa5b6f3-344d-11e9-a414-719c6709cf3e",
},
];
let books = [
{
title: "Clean Code",
published: 2008,
author: "Robert Martin",
id: "afa5b6f4-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Agile software development",
published: 2002,
author: "Robert Martin",
id: "afa5b6f5-344d-11e9-a414-719c6709cf3e",
genres: ["agile", "patterns", "design"],
},
{
title: "Refactoring, edition 2",
published: 2018,
author: "Martin Fowler",
id: "afa5de00-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Refactoring to patterns",
published: 2008,
author: "Joshua Kerievsky",
id: "afa5de01-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "patterns"],
},
{
title: "Practical Object-Oriented Design, An Agile Primer Using Ruby",
published: 2012,
author: "Sandi Metz",
id: "afa5de02-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "design"],
},
{
title: "Crime and punishment",
published: 1866,
author: "Fyodor Dostoevsky",
id: "afa5de03-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "crime"],
},
{
title: "The Demon ",
published: 1872,
author: "Fyodor Dostoevsky",
id: "afa5de04-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "revolution"],
},
];
const typeDefs = `
type Book {
title : String!
author : String!
published : String!
genres: [String!]!
}
type Author {
name: String!
born: Int
bookCount: Int!
id: ID!
}
type Query {
bookCount:Int!
authorCount:Int!
allBooks(author: String!):[Book!]!
allAuthors : [Author!]!
}
`;
const resolvers = {
Query: {
bookCount: () => books.length,
authorCount: () => authors.length,
allBooks: (root,args) => books.filter((book) => book.author === args.author),
allAuthors:() => authors,
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
let authors = [
{
name: "Robert Martin",
id: "afa51ab0-344d-11e9-a414-719c6709cf3e",
born: 1952,
},
{
name: "Martin Fowler",
id: "afa5b6f0-344d-11e9-a414-719c6709cf3e",
born: 1963,
},
{
name: "Fyodor Dostoevsky",
id: "afa5b6f1-344d-11e9-a414-719c6709cf3e",
born: 1821,
},
{
name: "Joshua Kerievsky", // birthyear not known
id: "afa5b6f2-344d-11e9-a414-719c6709cf3e",
},
{
name: "Sandi Metz", // birthyear not known
id: "afa5b6f3-344d-11e9-a414-719c6709cf3e",
},
];
let books = [
{
title: "Clean Code",
published: 2008,
author: "Robert Martin",
id: "afa5b6f4-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Agile software development",
published: 2002,
author: "Robert Martin",
id: "afa5b6f5-344d-11e9-a414-719c6709cf3e",
genres: ["agile", "patterns", "design"],
},
{
title: "Refactoring, edition 2",
published: 2018,
author: "Martin Fowler",
id: "afa5de00-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Refactoring to patterns",
published: 2008,
author: "Joshua Kerievsky",
id: "afa5de01-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "patterns"],
},
{
title: "Practical Object-Oriented Design, An Agile Primer Using Ruby",
published: 2012,
author: "Sandi Metz",
id: "afa5de02-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "design"],
},
{
title: "Crime and punishment",
published: 1866,
author: "Fyodor Dostoevsky",
id: "afa5de03-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "crime"],
},
{
title: "The Demon ",
published: 1872,
author: "Fyodor Dostoevsky",
id: "afa5de04-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "revolution"],
},
];
const typeDefs = `
type Book {
title : String!
author : String!
published : String!
genres: [String!]!
}
type Author {
name: String!
born: Int
bookCount: Int!
id: ID!
}
type Query {
bookCount:Int!
authorCount:Int!
allBooks(author: String!,genre: String!):[Book!]!
}
`;
const resolvers = {
Query: {
bookCount: () => books.length,
authorCount: () => authors.length,
allBooks: (root, args) =>
books.filter((book) => book.genres.includes(args.genre) && book.author === args.author),
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const { GraphQLError } = require("graphql");
const { v1: uuid } = require("uuid");
let authors = [
{
name: "Robert Martin",
id: "afa51ab0-344d-11e9-a414-719c6709cf3e",
born: 1952,
},
{
name: "Martin Fowler",
id: "afa5b6f0-344d-11e9-a414-719c6709cf3e",
born: 1963,
},
{
name: "Fyodor Dostoevsky",
id: "afa5b6f1-344d-11e9-a414-719c6709cf3e",
born: 1821,
},
{
name: "Joshua Kerievsky", // birthyear not known
id: "afa5b6f2-344d-11e9-a414-719c6709cf3e",
},
{
name: "Sandi Metz", // birthyear not known
id: "afa5b6f3-344d-11e9-a414-719c6709cf3e",
},
];
let books = [
{
title: "Clean Code",
published: 2008,
author: "Robert Martin",
id: "afa5b6f4-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Agile software development",
published: 2002,
author: "Robert Martin",
id: "afa5b6f5-344d-11e9-a414-719c6709cf3e",
genres: ["agile", "patterns", "design"],
},
{
title: "Refactoring, edition 2",
published: 2018,
author: "Martin Fowler",
id: "afa5de00-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Refactoring to patterns",
published: 2008,
author: "Joshua Kerievsky",
id: "afa5de01-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "patterns"],
},
{
title: "Practical Object-Oriented Design, An Agile Primer Using Ruby",
published: 2012,
author: "Sandi Metz",
id: "afa5de02-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "design"],
},
{
title: "Crime and punishment",
published: 1866,
author: "Fyodor Dostoevsky",
id: "afa5de03-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "crime"],
},
{
title: "The Demon ",
published: 1872,
author: "Fyodor Dostoevsky",
id: "afa5de04-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "revolution"],
},
];
const typeDefs = `
type Book {
title : String!
author : String!
published : Int!
genres: [String!]!
}
type Author {
name: String!
born: Int
bookCount: Int!
id: ID!
}
type Query {
bookCount:Int!
authorCount:Int!
allBooks(author: String!,genre: String!):[Book!]!
}
type Mutation {
addBook(
title : String!
author : String!
published : Int!
genres: [String!]!
) :Book
}
`;
const resolvers = {
Query: {
bookCount: () => books.length,
authorCount: () => authors.length,
allBooks: (root, args) =>
books.filter(
(book) =>
book.genres.includes(args.genre) && book.author === args.author
)
},
Mutation: {
addBook: (root,args) => {
if (books.find((b)=>b.title === args.title)) {
throw new GraphQLError("Book Titlee must be unique", {
extensions: {
code: "BAD_USER_INPUT",
invalidArgs: args.name,
},
});
}
const book = {...args,id:uuid()};
books = books.concat(book);
return book
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
const { ApolloServer } = require("@apollo/server");
const { startStandaloneServer } = require("@apollo/server/standalone");
const { GraphQLError } = require("graphql");
const { v1: uuid } = require("uuid");
let authors = [
{
name: "Robert Martin",
id: "afa51ab0-344d-11e9-a414-719c6709cf3e",
born: 1952,
},
{
name: "Martin Fowler",
id: "afa5b6f0-344d-11e9-a414-719c6709cf3e",
born: 1963,
},
{
name: "Fyodor Dostoevsky",
id: "afa5b6f1-344d-11e9-a414-719c6709cf3e",
born: 1821,
},
{
name: "Joshua Kerievsky", // birthyear not known
id: "afa5b6f2-344d-11e9-a414-719c6709cf3e",
},
{
name: "Sandi Metz", // birthyear not known
id: "afa5b6f3-344d-11e9-a414-719c6709cf3e",
},
];
let books = [
{
title: "Clean Code",
published: 2008,
author: "Robert Martin",
id: "afa5b6f4-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Agile software development",
published: 2002,
author: "Robert Martin",
id: "afa5b6f5-344d-11e9-a414-719c6709cf3e",
genres: ["agile", "patterns", "design"],
},
{
title: "Refactoring, edition 2",
published: 2018,
author: "Martin Fowler",
id: "afa5de00-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring"],
},
{
title: "Refactoring to patterns",
published: 2008,
author: "Joshua Kerievsky",
id: "afa5de01-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "patterns"],
},
{
title: "Practical Object-Oriented Design, An Agile Primer Using Ruby",
published: 2012,
author: "Sandi Metz",
id: "afa5de02-344d-11e9-a414-719c6709cf3e",
genres: ["refactoring", "design"],
},
{
title: "Crime and punishment",
published: 1866,
author: "Fyodor Dostoevsky",
id: "afa5de03-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "crime"],
},
{
title: "The Demon ",
published: 1872,
author: "Fyodor Dostoevsky",
id: "afa5de04-344d-11e9-a414-719c6709cf3e",
genres: ["classic", "revolution"],
},
];
const typeDefs = `
type Book {
title : String!
author : String!
published : Int!
genres: [String!]!
}
type Author {
name: String!
born: Int
bookCount: Int!
id: ID!
}
type Query {
bookCount:Int!
authorCount:Int!
allBooks(author: String!,genre: String!):[Book!]!
}
type Mutation {
addBook(
title : String!
author : String!
published : Int!
genres: [String!]!
) :Book
editAuthor(
name: String!
setBornTo: Int
bookCount: Int
id: ID
):Author
}
`;
const resolvers = {
Query: {
bookCount: () => books.length,
authorCount: () => authors.length,
allBooks: (root, args) =>
books.filter(
(book) =>
book.genres.includes(args.genre) && book.author === args.author
)
},
Mutation: {
addBook: (root,args) => {
if (books.find((b)=>b.title === args.title)) {
throw new GraphQLError("Book Titlee must be unique", {
extensions: {
code: "BAD_USER_INPUT",
invalidArgs: args.name,
},
});
}
const book = {...args,id:uuid()};
books = books.concat(book);
return book
},
editAuthor: (root,args)=>{
const author = authors.find(author => author.name === args.name)
if (!author) {
return null;
}
const updatedAuthor = {...author,born:args.setBornTo}
authors = authors.map((a)=>a.name === updatedAuthor.name ? updatedAuthor:a)
return updatedAuthor
}
}
};
const server = new ApolloServer({
typeDefs,
resolvers,
});
startStandaloneServer(server, {
listen: { port: 4000 },
}).then(({ url }) => {
console.log(`Server ready at ${url}`);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment