├── .gitignore
├── public
├── favicon.ico
└── vercel.svg
├── pages
├── _app.js
├── api
│ └── hello.js
└── index.js
├── Pipfile
├── codegen.yml
├── next.config.js
├── styles
├── globals.css
└── Home.module.css
├── requirements.txt
├── test.py
├── package.json
├── graphql
├── operations.graphql
└── graphql.ts
├── README.md
├── LICENSE
├── models.py
└── app.py
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | backup
4 | database.db
5 | .next
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yasoob/sqlalchemy-strawberry-fastapi-nextjs/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/pages/_app.js:
--------------------------------------------------------------------------------
1 | import '../styles/globals.css'
2 |
3 | function MyApp({ Component, pageProps }) {
4 | return
5 | }
6 |
7 | export default MyApp
8 |
--------------------------------------------------------------------------------
/Pipfile:
--------------------------------------------------------------------------------
1 | [[source]]
2 | url = "https://pypi.org/simple"
3 | verify_ssl = true
4 | name = "pypi"
5 |
6 | [packages]
7 |
8 | [dev-packages]
9 |
10 | [requires]
11 | python_version = "3.10"
12 |
--------------------------------------------------------------------------------
/pages/api/hello.js:
--------------------------------------------------------------------------------
1 | // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
2 |
3 | export default function handler(req, res) {
4 | res.status(200).json({ name: 'John Doe' })
5 | }
6 |
--------------------------------------------------------------------------------
/codegen.yml:
--------------------------------------------------------------------------------
1 | overwrite: true
2 | schema: "http://127.0.0.1:8000/graphql"
3 | documents: './graphql/**/*.graphql'
4 | generates:
5 | graphql/graphql.ts:
6 | plugins:
7 | - "typescript"
8 | - "typescript-operations"
9 | - "typescript-urql"
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | module.exports = {
3 | reactStrictMode: true,
4 | async rewrites() {
5 | return {
6 | beforeFiles: [
7 | {
8 | source: "/graphql",
9 | destination: "http://localhost:8000/graphql",
10 | },
11 | ],
12 | };
13 | },
14 | };
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | a {
10 | color: inherit;
11 | text-decoration: none;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | aiosqlite==0.17.0
2 | anyio==3.5.0
3 | asgiref==3.5.0
4 | backports.cached-property==1.0.1
5 | click==8.1.3
6 | fastapi==0.75.2
7 | graphql-core==3.2.1
8 | greenlet==1.1.2
9 | h11==0.13.0
10 | httptools==0.4.0
11 | idna==3.3
12 | pydantic==1.9.0
13 | Pygments==2.12.0
14 | python-dateutil==2.8.2
15 | python-dotenv==0.20.0
16 | python-multipart==0.0.5
17 | PyYAML==6.0
18 | six==1.16.0
19 | sniffio==1.2.0
20 | SQLAlchemy==1.4.36
21 | starlette==0.17.1
22 | strawberry-graphql==0.109.1
23 | typing-extensions==4.2.0
24 | uvicorn==0.17.6
25 | uvloop==0.16.0
26 | watchgod==0.8.2
27 | websockets==10.3
28 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 |
3 | from fastapi import FastAPI
4 | from strawberry.fastapi import GraphQLRouter
5 |
6 | authors: list[str] = []
7 |
8 | @strawberry.type
9 | class Query:
10 | @strawberry.field
11 | def all_authors(self, info) -> list[str]:
12 | return authors
13 |
14 | @strawberry.type
15 | class Mutation:
16 | @strawberry.field
17 | def add_author(name: str) -> str:
18 | authors.append(name)
19 | return name
20 |
21 | schema = strawberry.Schema(query=Query, mutation=Mutation)
22 |
23 | graphql_app = GraphQLRouter(schema)
24 |
25 | app = FastAPI()
26 | app.include_router(graphql_app, prefix="/graphql")
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "next_strawberry",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint",
10 | "codegen": "graphql-codegen --config codegen.yml"
11 | },
12 | "dependencies": {
13 | "@graphql-codegen/cli": "^2.6.2",
14 | "graphql": "^16.4.0",
15 | "next": "12.1.6",
16 | "react": "18.1.0",
17 | "react-dom": "18.1.0"
18 | },
19 | "devDependencies": {
20 | "@graphql-codegen/cli": "2.6.2",
21 | "@graphql-codegen/typescript": "2.4.10",
22 | "@graphql-codegen/typescript-operations": "2.3.7",
23 | "@graphql-codegen/typescript-urql": "^3.5.9",
24 | "eslint": "8.14.0",
25 | "eslint-config-next": "12.1.6",
26 | "urql": "^2.2.0"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/graphql/operations.graphql:
--------------------------------------------------------------------------------
1 | query Books {
2 | books {
3 | ...BookFields
4 | }
5 | }
6 |
7 | query Authors {
8 | authors {
9 | ...AuthorFields
10 | }
11 | }
12 |
13 | fragment BookFields on Book {
14 | id
15 | name
16 | author {
17 | name
18 | }
19 | }
20 |
21 | fragment AuthorFields on Author {
22 | id
23 | name
24 | }
25 |
26 | mutation AddBook($name: String!, $authorName: String!) {
27 | addBook(name: $name, authorName: $authorName) {
28 | __typename
29 | ... on Book {
30 | __typename
31 | ...BookFields
32 | }
33 | }
34 | }
35 |
36 | mutation AddAuthor($name: String!) {
37 | addAuthor(name: $name) {
38 | __typename
39 | ... on AuthorExists {
40 | __typename
41 | message
42 | }
43 | ... on Author {
44 | __typename
45 | ...AuthorFields
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). It demonstrates how to use Strawberry, FastAPI, SQLAlchemy, and NextJS together. It makes use of `graphql-codegen` to automatically generate `urql` hooks based on GraphQL API that are ready to use in your React/NextJS code.
2 |
3 |
4 | ## Getting Started
5 |
6 | First, install the Python dependencies:
7 |
8 | ```
9 | $ pip install -r requirements.txt
10 | ```
11 |
12 | Next, install the npm based dependencies:
13 |
14 | ```
15 | $ npm install
16 | ```
17 |
18 | Create the db:
19 |
20 | ```
21 | $ python models.py
22 | ```
23 |
24 | Now, run the `uvicorn` server:
25 |
26 | ```
27 | $ uvicorn app:app --reload --host '::'
28 | ```
29 |
30 | Finally, run the NextJS development server:
31 |
32 | ```bash
33 | npm run dev
34 | # or
35 | yarn dev
36 | ```
37 |
38 | Now you can go to `http://127.0.0.1:3000/graphql` to explore the interactive GraphiQL app and start developing your NextJS app.
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | MIT License
3 |
4 | Copyright (c) 2022 M.Yasoob Ullah Khalid ☺
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
--------------------------------------------------------------------------------
/models.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from contextlib import asynccontextmanager
3 | from typing import AsyncGenerator, Optional
4 |
5 | from sqlalchemy import Column, ForeignKey, Integer, String
6 | from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
7 | from sqlalchemy.ext.declarative import declarative_base
8 | from sqlalchemy.orm import relationship, sessionmaker
9 |
10 | Base = declarative_base()
11 |
12 |
13 | class Author(Base):
14 | __tablename__ = "authors"
15 | id: int = Column(Integer, primary_key=True, index=True)
16 | name: str = Column(String, nullable=False, unique=True)
17 |
18 | books: list["Book"] = relationship("Book", lazy="joined", back_populates="author")
19 |
20 |
21 | class Book(Base):
22 | __tablename__ = "books"
23 | id: int = Column(Integer, primary_key=True, index=True)
24 | name: str = Column(String, nullable=False)
25 | author_id: Optional[int] = Column(Integer, ForeignKey(Author.id), nullable=True)
26 |
27 | author: Optional[Author] = relationship(Author, lazy="joined", back_populates="books")
28 |
29 |
30 | engine = create_async_engine(
31 | "sqlite+aiosqlite:///./database.db", connect_args={"check_same_thread": False}
32 | )
33 |
34 | async_session = sessionmaker(
35 | bind=engine,
36 | class_=AsyncSession,
37 | expire_on_commit=False,
38 | autocommit=False,
39 | autoflush=False,
40 | )
41 |
42 |
43 | @asynccontextmanager
44 | async def get_session() -> AsyncGenerator[AsyncSession, None]:
45 | async with async_session() as session:
46 | async with session.begin():
47 | try:
48 | yield session
49 | finally:
50 | await session.close()
51 |
52 |
53 | async def _async_main():
54 | async with engine.begin() as conn:
55 | await conn.run_sync(Base.metadata.drop_all)
56 | await conn.run_sync(Base.metadata.create_all)
57 | await engine.dispose()
58 |
59 |
60 | if __name__ == "__main__":
61 | print("Dropping and creating tables")
62 | asyncio.run(_async_main())
63 | print("Done.")
--------------------------------------------------------------------------------
/styles/Home.module.css:
--------------------------------------------------------------------------------
1 | .container {
2 | padding: 0 2rem;
3 | }
4 |
5 | .main {
6 | min-height: 100vh;
7 | padding: 4rem 0;
8 | flex: 1;
9 | display: flex;
10 | flex-direction: column;
11 | justify-content: center;
12 | align-items: center;
13 | }
14 |
15 | .footer {
16 | display: flex;
17 | flex: 1;
18 | padding: 2rem 0;
19 | border-top: 1px solid #eaeaea;
20 | justify-content: center;
21 | align-items: center;
22 | }
23 |
24 | .footer a {
25 | display: flex;
26 | justify-content: center;
27 | align-items: center;
28 | flex-grow: 1;
29 | }
30 |
31 | .title a {
32 | color: #0070f3;
33 | text-decoration: none;
34 | }
35 |
36 | .title a:hover,
37 | .title a:focus,
38 | .title a:active {
39 | text-decoration: underline;
40 | }
41 |
42 | .title {
43 | margin: 0;
44 | line-height: 1.15;
45 | font-size: 4rem;
46 | }
47 |
48 | .title,
49 | .description {
50 | text-align: center;
51 | }
52 |
53 | .description {
54 | margin: 4rem 0;
55 | line-height: 1.5;
56 | font-size: 1.5rem;
57 | }
58 |
59 | .code {
60 | background: #fafafa;
61 | border-radius: 5px;
62 | padding: 0.75rem;
63 | font-size: 1.1rem;
64 | font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
65 | Bitstream Vera Sans Mono, Courier New, monospace;
66 | }
67 |
68 | .grid {
69 | display: flex;
70 | align-items: center;
71 | justify-content: center;
72 | flex-wrap: wrap;
73 | max-width: 800px;
74 | }
75 |
76 | .card {
77 | margin: 1rem;
78 | padding: 1.5rem;
79 | text-align: left;
80 | color: inherit;
81 | text-decoration: none;
82 | border: 1px solid #eaeaea;
83 | border-radius: 10px;
84 | transition: color 0.15s ease, border-color 0.15s ease;
85 | max-width: 300px;
86 | }
87 |
88 | .card:hover,
89 | .card:focus,
90 | .card:active {
91 | color: #0070f3;
92 | border-color: #0070f3;
93 | }
94 |
95 | .card h2 {
96 | margin: 0 0 1rem 0;
97 | font-size: 1.5rem;
98 | }
99 |
100 | .card p {
101 | margin: 0;
102 | font-size: 1.25rem;
103 | line-height: 1.5;
104 | }
105 |
106 | .logo {
107 | height: 1em;
108 | margin-left: 0.5rem;
109 | }
110 |
111 | @media (max-width: 600px) {
112 | .grid {
113 | width: 100%;
114 | flex-direction: column;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/pages/index.js:
--------------------------------------------------------------------------------
1 | import Head from 'next/head'
2 | import Image from 'next/image'
3 | import styles from '../styles/Home.module.css'
4 |
5 | export default function Home() {
6 | return (
7 |
8 |
9 |
Create Next App
10 |
11 |
12 |
13 |
14 |
15 |
16 | Welcome to Next.js!
17 |
18 |
19 |
20 | Get started by editing{' '}
21 | pages/index.js
22 |
23 |
24 |
53 |
54 |
55 |
67 |
68 | )
69 | }
70 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import strawberry
2 | from typing import Optional
3 |
4 | from fastapi import FastAPI
5 | from sqlalchemy import select
6 | from strawberry.fastapi import GraphQLRouter
7 | from strawberry.types import Info
8 | from strawberry.dataloader import DataLoader
9 |
10 | import models
11 |
12 | @strawberry.type
13 | class Author:
14 | id: strawberry.ID
15 | name: str
16 |
17 | @strawberry.field
18 | async def books(self, info: Info) -> list["Book"]:
19 | books = await info.context["books_by_author"].load(self.id)
20 | return [Book.marshal(book) for book in books]
21 |
22 | @classmethod
23 | def marshal(cls, model: models.Author) -> "Author":
24 | return cls(id=strawberry.ID(str(model.id)), name=model.name)
25 |
26 |
27 | @strawberry.type
28 | class Book:
29 | id: strawberry.ID
30 | name: str
31 | author: Optional[Author] = None
32 |
33 | @classmethod
34 | def marshal(cls, model: models.Book) -> "Book":
35 | return cls(
36 | id=strawberry.ID(str(model.id)),
37 | name=model.name,
38 | author=Author.marshal(model.author) if model.author else None,
39 | )
40 |
41 | @strawberry.type
42 | class AuthorExists:
43 | message: str = "Author with this name already exist"
44 |
45 | @strawberry.type
46 | class AuthorNotFound:
47 | message: str = "Couldn't find an author with the supplied name"
48 |
49 | @strawberry.type
50 | class AuthorNameMissing:
51 | message: str = "Please supply an author name"
52 |
53 | AddBookResponse = strawberry.union("AddBookResponse", (Book, AuthorNotFound, AuthorNameMissing))
54 | AddAuthorResponse = strawberry.union("AddAuthorResponse", (Author, AuthorExists))
55 |
56 |
57 | all_tasks: list = []
58 |
59 | @strawberry.type
60 | class Query:
61 | @strawberry.field
62 | async def books(self) -> list[Book]:
63 | async with models.get_session() as s:
64 | sql = select(models.Book).order_by(models.Book.name)
65 | db_book = (await s.execute(sql)).scalars().unique().all()
66 | return [Book.marshal(book) for book in db_book]
67 |
68 | @strawberry.field
69 | async def authors(self) -> list[Author]:
70 | async with models.get_session() as s:
71 | sql = select(models.Author).order_by(models.Author.name)
72 | db_authors = (await s.execute(sql)).scalars().unique().all()
73 | return [Author.marshal(loc) for loc in db_authors]
74 |
75 |
76 | @strawberry.type
77 | class Mutation:
78 | @strawberry.mutation
79 | async def add_book(self, name: str, author_name: Optional[str]) -> AddBookResponse:
80 | async with models.get_session() as s:
81 | db_author = None
82 | if author_name:
83 | sql = select(models.Author).where(models.Author.name == author_name)
84 | db_author = (await s.execute(sql)).scalars().first()
85 | if not db_author:
86 | return AuthorNotFound()
87 | else:
88 | return AuthorNameMissing()
89 | db_book = models.Book(name=name, author=db_author)
90 | s.add(db_book)
91 | await s.commit()
92 | return Book.marshal(db_book)
93 |
94 | @strawberry.mutation
95 | async def add_author(self, name: str) -> AddAuthorResponse:
96 | async with models.get_session() as s:
97 | sql = select(models.Author).where(models.Author.name == name)
98 | existing_db_author = (await s.execute(sql)).first()
99 | if existing_db_author is not None:
100 | return AuthorExists()
101 | db_author = models.Author(name=name)
102 | s.add(db_author)
103 | await s.commit()
104 | return Author.marshal(db_author)
105 |
106 |
107 | async def load_books_by_author(keys: list) -> list[Book]:
108 | async with models.get_session() as s:
109 | all_queries = [select(models.Book).where(models.Book.author_id == key) for key in keys]
110 | data = [(await s.execute(sql)).scalars().unique().all() for sql in all_queries]
111 | print(keys, data)
112 | return data
113 |
114 |
115 | async def load_author_by_book(keys: list) -> list[Book]:
116 | async with models.get_session() as s:
117 | sql = select(models.Author).where(models.Author.id in keys)
118 | data = (await s.execute(sql)).scalars().unique().all()
119 | if not data:
120 | data.append([])
121 | return data
122 |
123 |
124 | async def get_context() -> dict:
125 | return {
126 | "author_by_book": DataLoader(load_fn=load_author_by_book),
127 | "books_by_author": DataLoader(load_fn=load_books_by_author),
128 | }
129 |
130 | schema = strawberry.Schema(query=Query, mutation=Mutation)
131 | graphql_app = GraphQLRouter(schema, context_getter=get_context)
132 |
133 | app = FastAPI()
134 | app.include_router(graphql_app, prefix="/graphql")
--------------------------------------------------------------------------------
/graphql/graphql.ts:
--------------------------------------------------------------------------------
1 | import gql from 'graphql-tag';
2 | import * as Urql from 'urql';
3 | export type Maybe = T | null;
4 | export type InputMaybe = Maybe;
5 | export type Exact = { [K in keyof T]: T[K] };
6 | export type MakeOptional = Omit & { [SubKey in K]?: Maybe };
7 | export type MakeMaybe = Omit & { [SubKey in K]: Maybe };
8 | export type Omit = Pick>;
9 | /** All built-in and custom scalars, mapped to their actual values */
10 | export type Scalars = {
11 | ID: string;
12 | String: string;
13 | Boolean: boolean;
14 | Int: number;
15 | Float: number;
16 | };
17 |
18 | export type AddAuthorResponse = Author | AuthorExists;
19 |
20 | export type AddBookResponse = AuthorNameMissing | AuthorNotFound | Book;
21 |
22 | export type Author = {
23 | __typename?: 'Author';
24 | books: Array;
25 | id: Scalars['ID'];
26 | name: Scalars['String'];
27 | };
28 |
29 | export type AuthorExists = {
30 | __typename?: 'AuthorExists';
31 | message: Scalars['String'];
32 | };
33 |
34 | export type AuthorNameMissing = {
35 | __typename?: 'AuthorNameMissing';
36 | message: Scalars['String'];
37 | };
38 |
39 | export type AuthorNotFound = {
40 | __typename?: 'AuthorNotFound';
41 | message: Scalars['String'];
42 | };
43 |
44 | export type Book = {
45 | __typename?: 'Book';
46 | author?: Maybe;
47 | id: Scalars['ID'];
48 | name: Scalars['String'];
49 | };
50 |
51 | export type Mutation = {
52 | __typename?: 'Mutation';
53 | addAuthor: AddAuthorResponse;
54 | addBook: AddBookResponse;
55 | };
56 |
57 |
58 | export type MutationAddAuthorArgs = {
59 | name: Scalars['String'];
60 | };
61 |
62 |
63 | export type MutationAddBookArgs = {
64 | authorName?: InputMaybe;
65 | name: Scalars['String'];
66 | };
67 |
68 | export type Query = {
69 | __typename?: 'Query';
70 | authors: Array;
71 | books: Array;
72 | };
73 |
74 | export type BooksQueryVariables = Exact<{ [key: string]: never; }>;
75 |
76 |
77 | export type BooksQuery = { __typename?: 'Query', books: Array<{ __typename?: 'Book', id: string, name: string, author?: { __typename?: 'Author', name: string } | null }> };
78 |
79 | export type AuthorsQueryVariables = Exact<{ [key: string]: never; }>;
80 |
81 |
82 | export type AuthorsQuery = { __typename?: 'Query', authors: Array<{ __typename?: 'Author', id: string, name: string }> };
83 |
84 | export type BookFieldsFragment = { __typename?: 'Book', id: string, name: string, author?: { __typename?: 'Author', name: string } | null };
85 |
86 | export type AuthorFieldsFragment = { __typename?: 'Author', id: string, name: string };
87 |
88 | export type AddBookMutationVariables = Exact<{
89 | name: Scalars['String'];
90 | authorName: Scalars['String'];
91 | }>;
92 |
93 |
94 | export type AddBookMutation = { __typename?: 'Mutation', addBook: { __typename: 'AuthorNameMissing' } | { __typename: 'AuthorNotFound' } | { __typename: 'Book', id: string, name: string, author?: { __typename?: 'Author', name: string } | null } };
95 |
96 | export type AddAuthorMutationVariables = Exact<{
97 | name: Scalars['String'];
98 | }>;
99 |
100 |
101 | export type AddAuthorMutation = { __typename?: 'Mutation', addAuthor: { __typename: 'Author', id: string, name: string } | { __typename: 'AuthorExists', message: string } };
102 |
103 | export const BookFieldsFragmentDoc = gql`
104 | fragment BookFields on Book {
105 | id
106 | name
107 | author {
108 | name
109 | }
110 | }
111 | `;
112 | export const AuthorFieldsFragmentDoc = gql`
113 | fragment AuthorFields on Author {
114 | id
115 | name
116 | }
117 | `;
118 | export const BooksDocument = gql`
119 | query Books {
120 | books {
121 | ...BookFields
122 | }
123 | }
124 | ${BookFieldsFragmentDoc}`;
125 |
126 | export function useBooksQuery(options?: Omit, 'query'>) {
127 | return Urql.useQuery({ query: BooksDocument, ...options });
128 | };
129 | export const AuthorsDocument = gql`
130 | query Authors {
131 | authors {
132 | ...AuthorFields
133 | }
134 | }
135 | ${AuthorFieldsFragmentDoc}`;
136 |
137 | export function useAuthorsQuery(options?: Omit, 'query'>) {
138 | return Urql.useQuery({ query: AuthorsDocument, ...options });
139 | };
140 | export const AddBookDocument = gql`
141 | mutation AddBook($name: String!, $authorName: String!) {
142 | addBook(name: $name, authorName: $authorName) {
143 | __typename
144 | ... on Book {
145 | __typename
146 | ...BookFields
147 | }
148 | }
149 | }
150 | ${BookFieldsFragmentDoc}`;
151 |
152 | export function useAddBookMutation() {
153 | return Urql.useMutation(AddBookDocument);
154 | };
155 | export const AddAuthorDocument = gql`
156 | mutation AddAuthor($name: String!) {
157 | addAuthor(name: $name) {
158 | __typename
159 | ... on AuthorExists {
160 | __typename
161 | message
162 | }
163 | ... on Author {
164 | __typename
165 | ...AuthorFields
166 | }
167 | }
168 | }
169 | ${AuthorFieldsFragmentDoc}`;
170 |
171 | export function useAddAuthorMutation() {
172 | return Urql.useMutation(AddAuthorDocument);
173 | };
--------------------------------------------------------------------------------